First build (#3)
* Client app template and ui * ui fixes * ui fixes * Submodule update * Tap drivers added * Openvpn added * Openvpn for macos added * ui fixes * Router class added * Refactoring * Add installer * openvpnconfigurator * updated gitignore * easyrsa * QtSsh * OpenVPN files for Windows * WIP: main * General improvements and bug fixes * server scripts and connection classes * openvpn script fixes * General improvements and bug fixes * Server scripts fixes * Filter ssh output * Fix typo * ui fixes * Frameless window * ui fixes for macos * Refactoring
This commit is contained in:
commit
f0e5fbeda0
288 changed files with 94603 additions and 13 deletions
16
.gitignore
vendored
16
.gitignore
vendored
|
|
@ -1,17 +1,8 @@
|
||||||
# User settings
|
# User settings
|
||||||
*.user
|
*.user
|
||||||
macOSPackage/
|
macOSPackage/
|
||||||
|
AmneziaVPN.dmg
|
||||||
# C++ objects and libs
|
AmneziaVPN.exe
|
||||||
*.slo
|
|
||||||
*.lo
|
|
||||||
*.o
|
|
||||||
*.a
|
|
||||||
*.la
|
|
||||||
*.lai
|
|
||||||
*.so
|
|
||||||
*.dll
|
|
||||||
*.dylib
|
|
||||||
|
|
||||||
# Qt-es
|
# Qt-es
|
||||||
/.qmake.cache
|
/.qmake.cache
|
||||||
|
|
@ -47,4 +38,5 @@ CMakeLists.txt.user*
|
||||||
*.*~
|
*.*~
|
||||||
|
|
||||||
# Certificates
|
# Certificates
|
||||||
*.p12
|
*.p12
|
||||||
|
|
||||||
|
|
|
||||||
27
.gitlab-ci.yml
Normal file
27
.gitlab-ci.yml
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
variables:
|
||||||
|
GIT_STRATEGY: clone
|
||||||
|
|
||||||
|
stages:
|
||||||
|
- build
|
||||||
|
|
||||||
|
build-windows:
|
||||||
|
stage: build
|
||||||
|
tags:
|
||||||
|
- windows
|
||||||
|
script:
|
||||||
|
- cmd.exe /k "deploy\windows-env.bat && cd deploy && windows.bat"
|
||||||
|
artifacts:
|
||||||
|
name: artifacts-windows
|
||||||
|
paths:
|
||||||
|
- AmneziaVPN.exe
|
||||||
|
|
||||||
|
build-macos:
|
||||||
|
stage: build
|
||||||
|
tags:
|
||||||
|
- macos
|
||||||
|
script:
|
||||||
|
- cd deploy && ./macos.sh
|
||||||
|
artifacts:
|
||||||
|
name: artifacts-macos
|
||||||
|
paths:
|
||||||
|
- AmneziaVPN.dmg
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
Subproject commit 542ee3adbc1b663d8764ea8b219f802beaf8523c
|
|
||||||
2
AmneziaVPN.pro
Normal file
2
AmneziaVPN.pro
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
TEMPLATE = subdirs
|
||||||
|
SUBDIRS = client service platform
|
||||||
2
README.md
Normal file
2
README.md
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
# Amnezia
|
||||||
|
|
||||||
50
client/3rd/QtSsh/.gitignore
vendored
Normal file
50
client/3rd/QtSsh/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
# User settings
|
||||||
|
*.user
|
||||||
|
macOSPackage/
|
||||||
|
|
||||||
|
# C++ objects and libs
|
||||||
|
*.slo
|
||||||
|
*.lo
|
||||||
|
*.o
|
||||||
|
*.a
|
||||||
|
*.la
|
||||||
|
*.lai
|
||||||
|
*.so
|
||||||
|
*.dll
|
||||||
|
*.dylib
|
||||||
|
|
||||||
|
# Qt-es
|
||||||
|
/.qmake.cache
|
||||||
|
/.qmake.stash
|
||||||
|
*.pro.user
|
||||||
|
*.pro.user.*
|
||||||
|
*.qbs.user
|
||||||
|
*.qbs.user.*
|
||||||
|
*.moc
|
||||||
|
moc_*.cpp
|
||||||
|
qrc_*.cpp
|
||||||
|
ui_*.h
|
||||||
|
Makefile*
|
||||||
|
*build-*
|
||||||
|
|
||||||
|
# QtCreator
|
||||||
|
|
||||||
|
*.autosave
|
||||||
|
|
||||||
|
# QtCtreator Qml
|
||||||
|
*.qmlproject.user
|
||||||
|
*.qmlproject.user.*
|
||||||
|
|
||||||
|
# QtCtreator CMake
|
||||||
|
CMakeLists.txt.user*
|
||||||
|
|
||||||
|
# MACOS files
|
||||||
|
.DS_Store
|
||||||
|
._.DS_Store
|
||||||
|
._*
|
||||||
|
|
||||||
|
# tmp files
|
||||||
|
*.*~
|
||||||
|
|
||||||
|
# Certificates
|
||||||
|
*.p12
|
||||||
3
client/3rd/QtSsh/.qmake.conf
Normal file
3
client/3rd/QtSsh/.qmake.conf
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
load(qt_build_config)
|
||||||
|
|
||||||
|
MODULE_VERSION = 4.3.1
|
||||||
1
client/3rd/QtSsh/QtSsh.pro
Normal file
1
client/3rd/QtSsh/QtSsh.pro
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
load(qt_parts)
|
||||||
46
client/3rd/QtSsh/README.md
Normal file
46
client/3rd/QtSsh/README.md
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
# QSsh
|
||||||
|
|
||||||
|
this project is base on Qt-creator-open-source-4.3.1
|
||||||
|
project is at
|
||||||
|
|
||||||
|
`http://code.qt.io/cgit/qt-creator/qt-creator.git/`
|
||||||
|
|
||||||
|
you can download code zip at
|
||||||
|
|
||||||
|
`http://download.qt.io/official_releases/qtcreator/4.3/4.3.1/`
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
|
||||||
|
> * For linux user, if your Qt is installed through package manager tools such "apt-get", make sure that you have installed the Qt5 develop package *qtbase5-private-dev*
|
||||||
|
|
||||||
|
### Usage(1): Use QtSsh as Qt5's addon module
|
||||||
|
|
||||||
|
#### Building the module
|
||||||
|
|
||||||
|
> **Note**: Perl is needed in this step.
|
||||||
|
|
||||||
|
* Download the source code.
|
||||||
|
|
||||||
|
* Put the source code in any directory you like
|
||||||
|
|
||||||
|
* Go to top directory of the project in a terminal and run
|
||||||
|
|
||||||
|
```
|
||||||
|
qmake
|
||||||
|
make
|
||||||
|
make install
|
||||||
|
```
|
||||||
|
|
||||||
|
The library, the header files, and others will be installed to your system.
|
||||||
|
|
||||||
|
#### Import the module
|
||||||
|
|
||||||
|
` QT += ssh`
|
||||||
|
|
||||||
|
#### Include Headerfile
|
||||||
|
|
||||||
|
` #include<QtSsh/sshconnection.h>`
|
||||||
|
|
||||||
|
#### Close qtc.ssh log
|
||||||
|
|
||||||
|
` QLoggingCategory::setFilterRules(QStringLiteral("qtc.ssh=false")`
|
||||||
2
client/3rd/QtSsh/examples/examples.pro
Normal file
2
client/3rd/QtSsh/examples/examples.pro
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
TEMPLATE = subdirs
|
||||||
|
SUBDIRS = gitlab
|
||||||
5
client/3rd/QtSsh/examples/gitlab/Qml.qrc
Normal file
5
client/3rd/QtSsh/examples/gitlab/Qml.qrc
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
<RCC>
|
||||||
|
<qresource prefix="/">
|
||||||
|
<file>Qml/Main.qml</file>
|
||||||
|
</qresource>
|
||||||
|
</RCC>
|
||||||
51
client/3rd/QtSsh/examples/gitlab/Qml/Main.qml
Normal file
51
client/3rd/QtSsh/examples/gitlab/Qml/Main.qml
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
import QtQuick 2.0
|
||||||
|
import QtQuick.Controls 2.0
|
||||||
|
import Ssh 1.0
|
||||||
|
Item {
|
||||||
|
width: 1024
|
||||||
|
height: 768
|
||||||
|
|
||||||
|
TextField {
|
||||||
|
id: input
|
||||||
|
x: 104
|
||||||
|
y: 44
|
||||||
|
width: 482
|
||||||
|
height: 40
|
||||||
|
implicitWidth: 200
|
||||||
|
selectByMouse: true
|
||||||
|
text: "https://www.zhihu.com"
|
||||||
|
}
|
||||||
|
function get(url) {
|
||||||
|
var xhr = new XMLHttpRequest();
|
||||||
|
xhr.onreadystatechange = function() {
|
||||||
|
if (xhr.readyState === XMLHttpRequest.DONE) {
|
||||||
|
console.log(xhr.responseXML, xhr.responseText.toString())
|
||||||
|
} else if (xhr.readyState === XMLHttpRequest) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
xhr.open('GET', url)
|
||||||
|
xhr.send()
|
||||||
|
}
|
||||||
|
Button {
|
||||||
|
x: 627
|
||||||
|
y: 44
|
||||||
|
text: "get"
|
||||||
|
onClicked: {
|
||||||
|
get(input.text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Button {
|
||||||
|
id: button
|
||||||
|
x: 104
|
||||||
|
y: 170
|
||||||
|
text: qsTr("Ssh")
|
||||||
|
onClicked: ssh.connectToHost()
|
||||||
|
}
|
||||||
|
Ssh {
|
||||||
|
id: ssh
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
17
client/3rd/QtSsh/examples/gitlab/Src/Main.cpp
Normal file
17
client/3rd/QtSsh/examples/gitlab/Src/Main.cpp
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
#include <QGuiApplication>
|
||||||
|
#include <QQmlApplicationEngine>
|
||||||
|
#include <QQuickView>
|
||||||
|
#include "Ssh.hpp"
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
|
||||||
|
QGuiApplication app(argc, argv);
|
||||||
|
|
||||||
|
qmlRegisterType<Ssh> ("Ssh", 1, 0, "Ssh");
|
||||||
|
|
||||||
|
QQuickView view;
|
||||||
|
view.setSource(QUrl("qrc:/Qml/Main.qml"));
|
||||||
|
view.show();
|
||||||
|
return app.exec();
|
||||||
|
}
|
||||||
61
client/3rd/QtSsh/examples/gitlab/Src/Ssh.cpp
Normal file
61
client/3rd/QtSsh/examples/gitlab/Src/Ssh.cpp
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
#include "Ssh.hpp"
|
||||||
|
#include <QDir>
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QLoggingCategory>
|
||||||
|
|
||||||
|
Ssh::Ssh(QObject *parent) : QObject(parent) {
|
||||||
|
//关掉qtc.ssh中的各种打印信息
|
||||||
|
QLoggingCategory::setFilterRules(QStringLiteral("qtc.ssh=false"));
|
||||||
|
|
||||||
|
mParams.host="ftb.autoio.org";
|
||||||
|
mParams.userName = "ftb";
|
||||||
|
mParams.port = 11122;
|
||||||
|
|
||||||
|
mParams.privateKeyFile = QDir::homePath() + QStringLiteral("/.ssh/id_rsa");
|
||||||
|
mParams.timeout = 5;
|
||||||
|
mParams.authenticationType = SshConnectionParameters::AuthenticationTypePublicKey;
|
||||||
|
mParams.options = SshIgnoreDefaultProxy;
|
||||||
|
mParams.hostKeyCheckingMode = SshHostKeyCheckingNone;
|
||||||
|
|
||||||
|
mConnections = std::make_shared<SshConnection>(mParams);
|
||||||
|
connect(mConnections.get(), &SshConnection::error, [&](QSsh::SshError){
|
||||||
|
qWarning() << "Error: " << mConnections->errorString();
|
||||||
|
});
|
||||||
|
connect(mConnections.get(), &SshConnection::connected, [&](){
|
||||||
|
qWarning() << "Connected";
|
||||||
|
create();
|
||||||
|
});
|
||||||
|
connect(mConnections.get(), &SshConnection::disconnected, [](){
|
||||||
|
qWarning() << "Disconnected";
|
||||||
|
});
|
||||||
|
connect(mConnections.get(), &SshConnection::dataAvailable, [](const QString &message){
|
||||||
|
qWarning() << "Message: " << message;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void Ssh::connectToHost() {
|
||||||
|
mConnections->connectToHost();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Ssh::create() {
|
||||||
|
mRemoteProcess = mConnections->createRemoteProcess(QString::fromLatin1("/bin/ls -a").toUtf8());
|
||||||
|
if (!mRemoteProcess) {
|
||||||
|
qWarning() << QLatin1String("Error: UnmRemoteProcess SSH connection creates remote process.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
connect(mRemoteProcess.data(), &SshRemoteProcess::started, [&](){
|
||||||
|
qWarning() << "started";
|
||||||
|
});
|
||||||
|
connect(mRemoteProcess.data(), &SshRemoteProcess::readyReadStandardOutput, [&](){
|
||||||
|
qWarning() << "StandardOutput";
|
||||||
|
qWarning() << QString::fromLatin1(mRemoteProcess->readAllStandardOutput()).split('\n');
|
||||||
|
});
|
||||||
|
connect(mRemoteProcess.data(), &SshRemoteProcess::readyReadStandardError, [&](){
|
||||||
|
qWarning() << "StandardError" << mRemoteProcess->readAllStandardError();
|
||||||
|
});
|
||||||
|
connect(mRemoteProcess.data(), &SshRemoteProcess::closed, [&](int exitStatus){
|
||||||
|
qWarning() << "Exit" << exitStatus;
|
||||||
|
});
|
||||||
|
mRemoteProcess->start();
|
||||||
|
}
|
||||||
|
|
||||||
26
client/3rd/QtSsh/examples/gitlab/Src/Ssh.hpp
Normal file
26
client/3rd/QtSsh/examples/gitlab/Src/Ssh.hpp
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QtSsh/sshconnection.h>
|
||||||
|
#include <QtSsh/sshremoteprocess.h>
|
||||||
|
|
||||||
|
using namespace QSsh;
|
||||||
|
|
||||||
|
class Ssh : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit Ssh(QObject *parent = nullptr);
|
||||||
|
|
||||||
|
Q_INVOKABLE void connectToHost();
|
||||||
|
void create();
|
||||||
|
|
||||||
|
private:
|
||||||
|
SshConnectionParameters mParams;
|
||||||
|
std::shared_ptr<SshConnection> mConnections;
|
||||||
|
QSharedPointer<SshRemoteProcess> mRemoteProcess;
|
||||||
|
};
|
||||||
|
|
||||||
29
client/3rd/QtSsh/examples/gitlab/gitlab.pro
Normal file
29
client/3rd/QtSsh/examples/gitlab/gitlab.pro
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
QT += quick network ssh
|
||||||
|
CONFIG += c++11
|
||||||
|
TEMPLATE = app
|
||||||
|
# The following define makes your compiler emit warnings if you use
|
||||||
|
# any feature of Qt which as been marked deprecated (the exact warnings
|
||||||
|
# depend on your compiler). Please consult the documentation of the
|
||||||
|
# deprecated API in order to know how to port your code away from it.
|
||||||
|
#DEFINES += QT_DEPRECATED_WARNINGS
|
||||||
|
|
||||||
|
# You can also make your code fail to compile if you use deprecated APIs.
|
||||||
|
# In order to do so, uncomment the following line.
|
||||||
|
# You can also select to disable deprecated APIs only up to a certain version of Qt.
|
||||||
|
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
|
||||||
|
|
||||||
|
SOURCES += Src/Main.cpp \
|
||||||
|
Src/Ssh.cpp
|
||||||
|
|
||||||
|
RESOURCES += Qml.qrc
|
||||||
|
|
||||||
|
# Additional import path used to resolve QML modules in Qt Creator's code model
|
||||||
|
QML_IMPORT_PATH =
|
||||||
|
|
||||||
|
# Additional import path used to resolve QML modules just for Qt Quick Designer
|
||||||
|
QML_DESIGNER_IMPORT_PATH =
|
||||||
|
|
||||||
|
HEADERS += \
|
||||||
|
Src/Ssh.hpp
|
||||||
|
|
||||||
|
|
||||||
47176
client/3rd/QtSsh/src/botan/botan.cpp
Normal file
47176
client/3rd/QtSsh/src/botan/botan.cpp
Normal file
File diff suppressed because it is too large
Load diff
16210
client/3rd/QtSsh/src/botan/botan.h
Normal file
16210
client/3rd/QtSsh/src/botan/botan.h
Normal file
File diff suppressed because it is too large
Load diff
50
client/3rd/QtSsh/src/botan/botan.pri
Normal file
50
client/3rd/QtSsh/src/botan/botan.pri
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
INCLUDEPATH *= $$PWD/..
|
||||||
|
HEADERS += $$PWD/botan.h
|
||||||
|
|
||||||
|
SOURCES += $$PWD/botan.cpp
|
||||||
|
|
||||||
|
CONFIG += exceptions
|
||||||
|
|
||||||
|
DEPENDPATH += .
|
||||||
|
|
||||||
|
DEFINES += BOTAN_DLL=
|
||||||
|
unix:DEFINES += BOTAN_TARGET_OS_HAS_GETTIMEOFDAY BOTAN_HAS_ALLOC_MMAP \
|
||||||
|
BOTAN_HAS_ENTROPY_SRC_DEV_RANDOM BOTAN_HAS_ENTROPY_SRC_EGD BOTAN_HAS_ENTROPY_SRC_FTW \
|
||||||
|
BOTAN_HAS_ENTROPY_SRC_UNIX BOTAN_HAS_MUTEX_PTHREAD BOTAN_HAS_PIPE_UNIXFD_IO
|
||||||
|
*linux*:DEFINES += BOTAN_TARGET_OS_IS_LINUX BOTAN_TARGET_OS_HAS_CLOCK_GETTIME \
|
||||||
|
BOTAN_TARGET_OS_HAS_DLOPEN BOTAN_TARGET_OS_HAS_GMTIME_R BOTAN_TARGET_OS_HAS_POSIX_MLOCK \
|
||||||
|
BOTAN_HAS_DYNAMICALLY_LOADED_ENGINE BOTAN_HAS_DYNAMIC_LOADER
|
||||||
|
macx:DEFINES += BOTAN_TARGET_OS_IS_DARWIN
|
||||||
|
*g++*:DEFINES += BOTAN_BUILD_COMPILER_IS_GCC
|
||||||
|
*clang*:DEFINES += BOTAN_BUILD_COMPILER_IS_CLANG
|
||||||
|
*icc*:DEFINES += BOTAN_BUILD_COMPILER_IS_INTEL
|
||||||
|
|
||||||
|
CONFIG(x86_64):DEFINES += BOTAN_TARGET_ARCH_IS_X86_64
|
||||||
|
|
||||||
|
win32 {
|
||||||
|
DEFINES += BOTAN_TARGET_OS_IS_WINDOWS \
|
||||||
|
BOTAN_TARGET_OS_HAS_LOADLIBRARY BOTAN_TARGET_OS_HAS_WIN32_GET_SYSTEMTIME \
|
||||||
|
BOTAN_TARGET_OS_HAS_WIN32_VIRTUAL_LOCK \
|
||||||
|
BOTAN_HAS_ENTROPY_SRC_CAPI BOTAN_HAS_ENTROPY_SRC_WIN32 \
|
||||||
|
BOTAN_HAS_MUTEX_WIN32
|
||||||
|
|
||||||
|
msvc {
|
||||||
|
QMAKE_CXXFLAGS_EXCEPTIONS_ON = -EHs
|
||||||
|
QMAKE_CXXFLAGS += -wd4251 -wd4290 -wd4250 -wd4297 -wd4267 -wd4334
|
||||||
|
DEFINES += BOTAN_BUILD_COMPILER_IS_MSVC BOTAN_TARGET_OS_HAS_GMTIME_S _SCL_SECURE_NO_WARNINGS
|
||||||
|
} else {
|
||||||
|
QMAKE_CFLAGS += -fpermissive -finline-functions -Wno-long-long
|
||||||
|
QMAKE_CXXFLAGS += -fpermissive -finline-functions -Wno-long-long
|
||||||
|
}
|
||||||
|
LIBS += -ladvapi32 -luser32
|
||||||
|
}
|
||||||
|
|
||||||
|
unix:*-g++* {
|
||||||
|
QMAKE_CFLAGS += -fPIC -fpermissive -finline-functions -Wno-long-long
|
||||||
|
QMAKE_CXXFLAGS += -fPIC -fpermissive -finline-functions -Wno-long-long
|
||||||
|
}
|
||||||
|
|
||||||
|
linux*|freebsd* {
|
||||||
|
LIBS += -lrt $$QMAKE_LIBS_DYNLOAD
|
||||||
|
}
|
||||||
|
|
||||||
1881
client/3rd/QtSsh/src/botan/configure.py
Normal file
1881
client/3rd/QtSsh/src/botan/configure.py
Normal file
File diff suppressed because it is too large
Load diff
49
client/3rd/QtSsh/src/botan/doc/license.txt
Normal file
49
client/3rd/QtSsh/src/botan/doc/license.txt
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
|
||||||
|
.. _license:
|
||||||
|
.. highlight:: none
|
||||||
|
|
||||||
|
License
|
||||||
|
========================================
|
||||||
|
|
||||||
|
Botan (http://botan.randombit.net/) is distributed under these terms::
|
||||||
|
|
||||||
|
Copyright (C) 1999-2011 Jack Lloyd
|
||||||
|
2001 Peter J Jones
|
||||||
|
2004-2007 Justin Karneges
|
||||||
|
2004 Vaclav Ovsik
|
||||||
|
2005 Matthew Gregan
|
||||||
|
2005-2006 Matt Johnston
|
||||||
|
2006 Luca Piccarreta
|
||||||
|
2007 Yves Jerschow
|
||||||
|
2007-2008 FlexSecure GmbH
|
||||||
|
2007-2008 Technische Universitat Darmstadt
|
||||||
|
2007-2008 Falko Strenzke
|
||||||
|
2007-2008 Martin Doering
|
||||||
|
2007 Manuel Hartl
|
||||||
|
2007 Christoph Ludwig
|
||||||
|
2007 Patrick Sona
|
||||||
|
2010 Olivier de Gaalon
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are
|
||||||
|
met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions, and the following disclaimer.
|
||||||
|
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
notice, this list of conditions, and the following disclaimer in the
|
||||||
|
documentation and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) "AS IS" AND ANY EXPRESS OR
|
||||||
|
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE,
|
||||||
|
ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) OR CONTRIBUTOR(S) BE
|
||||||
|
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
||||||
|
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||||
|
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
||||||
|
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
|
||||||
|
IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
15
client/3rd/QtSsh/src/botan/readme.txt
Normal file
15
client/3rd/QtSsh/src/botan/readme.txt
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
Botan 1.10.2, 2012-06-17
|
||||||
|
http://botan.randombit.net/
|
||||||
|
|
||||||
|
Botan is a C++ class library for performing a wide variety of
|
||||||
|
cryptographic operations. It is released under the 2 clause BSD
|
||||||
|
license; see doc/license.txt for the specifics. You can file bugs in
|
||||||
|
Bugzilla (http://bugs.randombit.net/) or by sending a report to the
|
||||||
|
botan-devel mailing list. More information about the mailing list is
|
||||||
|
at http://lists.randombit.net/mailman/listinfo/botan-devel/
|
||||||
|
|
||||||
|
You can find documentation online at http://botan.randombit.net/ as
|
||||||
|
well as in the doc directory in the distribution. Several examples can
|
||||||
|
be found in doc/examples as well.
|
||||||
|
|
||||||
|
Jack Lloyd (lloyd@randombit.net)
|
||||||
3
client/3rd/QtSsh/src/src.pro
Normal file
3
client/3rd/QtSsh/src/src.pro
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
TEMPLATE = subdirs
|
||||||
|
|
||||||
|
SUBDIRS = ssh
|
||||||
972
client/3rd/QtSsh/src/ssh/sftpchannel.cpp
Normal file
972
client/3rd/QtSsh/src/ssh/sftpchannel.cpp
Normal file
|
|
@ -0,0 +1,972 @@
|
||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2016 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#include "sftpchannel.h"
|
||||||
|
#include "sftpchannel_p.h"
|
||||||
|
|
||||||
|
#include "sshexception_p.h"
|
||||||
|
#include "sshincomingpacket_p.h"
|
||||||
|
#include "sshlogging_p.h"
|
||||||
|
#include "sshsendfacility_p.h"
|
||||||
|
|
||||||
|
#include <QDir>
|
||||||
|
#include <QFile>
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\class QSsh::SftpChannel
|
||||||
|
|
||||||
|
\brief The SftpChannel class provides SFTP operations.
|
||||||
|
|
||||||
|
Objects are created via SshConnection::createSftpChannel().
|
||||||
|
The channel needs to be initialized with
|
||||||
|
a call to initialize() and is closed via closeChannel(). After closing
|
||||||
|
a channel, no more operations are possible. It cannot be re-opened
|
||||||
|
using initialize(); use SshConnection::createSftpChannel() if you need
|
||||||
|
a new one.
|
||||||
|
|
||||||
|
After the initialized() signal has been emitted, operations can be started.
|
||||||
|
All SFTP operations are asynchronous (non-blocking) and can be in-flight
|
||||||
|
simultaneously (though callers must ensure that concurrently running jobs
|
||||||
|
are independent of each other, e.g. they must not write to the same file).
|
||||||
|
Operations are identified by their job id, which is returned by
|
||||||
|
the respective member function. If the function can right away detect that
|
||||||
|
the operation cannot succeed, it returns SftpInvalidJob. If an error occurs
|
||||||
|
later, the finished() signal is emitted for the respective job with a
|
||||||
|
non-empty error string.
|
||||||
|
|
||||||
|
Note that directory names must not have a trailing slash.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace QSsh {
|
||||||
|
namespace Internal {
|
||||||
|
namespace {
|
||||||
|
const quint32 ProtocolVersion = 3;
|
||||||
|
|
||||||
|
QString errorMessage(const QString &serverMessage,
|
||||||
|
const QString &alternativeMessage)
|
||||||
|
{
|
||||||
|
return serverMessage.isEmpty() ? alternativeMessage : serverMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString errorMessage(const SftpStatusResponse &response,
|
||||||
|
const QString &alternativeMessage)
|
||||||
|
{
|
||||||
|
return response.status == SSH_FX_OK ? QString()
|
||||||
|
: errorMessage(response.errorString, alternativeMessage);
|
||||||
|
}
|
||||||
|
} // anonymous namespace
|
||||||
|
} // namespace Internal
|
||||||
|
|
||||||
|
SftpChannel::SftpChannel(quint32 channelId,
|
||||||
|
Internal::SshSendFacility &sendFacility)
|
||||||
|
: d(new Internal::SftpChannelPrivate(channelId, sendFacility, this))
|
||||||
|
{
|
||||||
|
connect(d, &Internal::SftpChannelPrivate::initialized,
|
||||||
|
this, &SftpChannel::initialized, Qt::QueuedConnection);
|
||||||
|
connect(d, &Internal::SftpChannelPrivate::channelError,
|
||||||
|
this, &SftpChannel::channelError, Qt::QueuedConnection);
|
||||||
|
connect(d, &Internal::SftpChannelPrivate::dataAvailable,
|
||||||
|
this, &SftpChannel::dataAvailable, Qt::QueuedConnection);
|
||||||
|
connect(d, &Internal::SftpChannelPrivate::fileInfoAvailable,
|
||||||
|
this, &SftpChannel::fileInfoAvailable, Qt::QueuedConnection);
|
||||||
|
connect(d, &Internal::SftpChannelPrivate::finished,
|
||||||
|
this, &SftpChannel::finished, Qt::QueuedConnection);
|
||||||
|
connect(d, &Internal::SftpChannelPrivate::closed,
|
||||||
|
this, &SftpChannel::closed, Qt::QueuedConnection);
|
||||||
|
}
|
||||||
|
|
||||||
|
SftpChannel::State SftpChannel::state() const
|
||||||
|
{
|
||||||
|
switch (d->channelState()) {
|
||||||
|
case Internal::AbstractSshChannel::Inactive:
|
||||||
|
return Uninitialized;
|
||||||
|
case Internal::AbstractSshChannel::SessionRequested:
|
||||||
|
return Initializing;
|
||||||
|
case Internal::AbstractSshChannel::CloseRequested:
|
||||||
|
return Closing;
|
||||||
|
case Internal::AbstractSshChannel::Closed:
|
||||||
|
return Closed;
|
||||||
|
case Internal::AbstractSshChannel::SessionEstablished:
|
||||||
|
return d->m_sftpState == Internal::SftpChannelPrivate::Initialized
|
||||||
|
? Initialized : Initializing;
|
||||||
|
default:
|
||||||
|
Q_ASSERT(!"Oh no, we forgot to handle a channel state!");
|
||||||
|
return Closed; // For the compiler.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SftpChannel::initialize()
|
||||||
|
{
|
||||||
|
d->requestSessionStart();
|
||||||
|
d->m_sftpState = Internal::SftpChannelPrivate::SubsystemRequested;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SftpChannel::closeChannel()
|
||||||
|
{
|
||||||
|
d->closeChannel();
|
||||||
|
}
|
||||||
|
|
||||||
|
SftpJobId SftpChannel::statFile(const QString &path)
|
||||||
|
{
|
||||||
|
return d->createJob(Internal::SftpStatFile::Ptr(
|
||||||
|
new Internal::SftpStatFile(++d->m_nextJobId, path)));
|
||||||
|
}
|
||||||
|
|
||||||
|
SftpJobId SftpChannel::listDirectory(const QString &path)
|
||||||
|
{
|
||||||
|
return d->createJob(Internal::SftpListDir::Ptr(
|
||||||
|
new Internal::SftpListDir(++d->m_nextJobId, path)));
|
||||||
|
}
|
||||||
|
|
||||||
|
SftpJobId SftpChannel::createDirectory(const QString &path)
|
||||||
|
{
|
||||||
|
return d->createJob(Internal::SftpMakeDir::Ptr(
|
||||||
|
new Internal::SftpMakeDir(++d->m_nextJobId, path)));
|
||||||
|
}
|
||||||
|
|
||||||
|
SftpJobId SftpChannel::removeDirectory(const QString &path)
|
||||||
|
{
|
||||||
|
return d->createJob(Internal::SftpRmDir::Ptr(
|
||||||
|
new Internal::SftpRmDir(++d->m_nextJobId, path)));
|
||||||
|
}
|
||||||
|
|
||||||
|
SftpJobId SftpChannel::removeFile(const QString &path)
|
||||||
|
{
|
||||||
|
return d->createJob(Internal::SftpRm::Ptr(
|
||||||
|
new Internal::SftpRm(++d->m_nextJobId, path)));
|
||||||
|
}
|
||||||
|
|
||||||
|
SftpJobId SftpChannel::renameFileOrDirectory(const QString &oldPath,
|
||||||
|
const QString &newPath)
|
||||||
|
{
|
||||||
|
return d->createJob(Internal::SftpRename::Ptr(
|
||||||
|
new Internal::SftpRename(++d->m_nextJobId, oldPath, newPath)));
|
||||||
|
}
|
||||||
|
|
||||||
|
SftpJobId SftpChannel::createLink(const QString &filePath, const QString &target)
|
||||||
|
{
|
||||||
|
return d->createJob(Internal::SftpCreateLink::Ptr(
|
||||||
|
new Internal::SftpCreateLink(++d->m_nextJobId, filePath, target)));
|
||||||
|
}
|
||||||
|
|
||||||
|
SftpJobId SftpChannel::createFile(const QString &path, SftpOverwriteMode mode)
|
||||||
|
{
|
||||||
|
return d->createJob(Internal::SftpCreateFile::Ptr(
|
||||||
|
new Internal::SftpCreateFile(++d->m_nextJobId, path, mode)));
|
||||||
|
}
|
||||||
|
|
||||||
|
SftpJobId SftpChannel::uploadFile(const QString &localFilePath,
|
||||||
|
const QString &remoteFilePath, SftpOverwriteMode mode)
|
||||||
|
{
|
||||||
|
QSharedPointer<QFile> localFile(new QFile(localFilePath));
|
||||||
|
if (!localFile->open(QIODevice::ReadOnly))
|
||||||
|
return SftpInvalidJob;
|
||||||
|
return d->createJob(Internal::SftpUploadFile::Ptr(
|
||||||
|
new Internal::SftpUploadFile(++d->m_nextJobId, remoteFilePath, localFile, mode)));
|
||||||
|
}
|
||||||
|
|
||||||
|
SftpJobId SftpChannel::downloadFile(const QString &remoteFilePath,
|
||||||
|
const QString &localFilePath, SftpOverwriteMode mode)
|
||||||
|
{
|
||||||
|
QSharedPointer<QFile> localFile(new QFile(localFilePath));
|
||||||
|
if (mode == SftpSkipExisting && localFile->exists())
|
||||||
|
return SftpInvalidJob;
|
||||||
|
QIODevice::OpenMode openMode = QIODevice::WriteOnly;
|
||||||
|
if (mode == SftpOverwriteExisting)
|
||||||
|
openMode |= QIODevice::Truncate;
|
||||||
|
else if (mode == SftpAppendToExisting)
|
||||||
|
openMode |= QIODevice::Append;
|
||||||
|
if (!localFile->open(openMode))
|
||||||
|
return SftpInvalidJob;
|
||||||
|
return d->createJob(Internal::SftpDownload::Ptr(
|
||||||
|
new Internal::SftpDownload(++d->m_nextJobId, remoteFilePath, localFile)));
|
||||||
|
}
|
||||||
|
|
||||||
|
SftpJobId SftpChannel::uploadDir(const QString &localDirPath,
|
||||||
|
const QString &remoteParentDirPath)
|
||||||
|
{
|
||||||
|
if (state() != Initialized)
|
||||||
|
return SftpInvalidJob;
|
||||||
|
const QDir localDir(localDirPath);
|
||||||
|
if (!localDir.exists() || !localDir.isReadable())
|
||||||
|
return SftpInvalidJob;
|
||||||
|
const Internal::SftpUploadDir::Ptr uploadDirOp(
|
||||||
|
new Internal::SftpUploadDir(++d->m_nextJobId));
|
||||||
|
const QString remoteDirPath
|
||||||
|
= remoteParentDirPath + QLatin1Char('/') + localDir.dirName();
|
||||||
|
const Internal::SftpMakeDir::Ptr mkdirOp(
|
||||||
|
new Internal::SftpMakeDir(++d->m_nextJobId, remoteDirPath, uploadDirOp));
|
||||||
|
uploadDirOp->mkdirsInProgress.insert(mkdirOp,
|
||||||
|
Internal::SftpUploadDir::Dir(localDirPath, remoteDirPath));
|
||||||
|
d->createJob(mkdirOp);
|
||||||
|
return uploadDirOp->jobId;
|
||||||
|
}
|
||||||
|
|
||||||
|
SftpChannel::~SftpChannel()
|
||||||
|
{
|
||||||
|
delete d;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
namespace Internal {
|
||||||
|
|
||||||
|
SftpChannelPrivate::SftpChannelPrivate(quint32 channelId,
|
||||||
|
SshSendFacility &sendFacility, SftpChannel *sftp)
|
||||||
|
: AbstractSshChannel(channelId, sendFacility),
|
||||||
|
m_nextJobId(0), m_sftpState(Inactive), m_sftp(sftp)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
SftpJobId SftpChannelPrivate::createJob(const AbstractSftpOperation::Ptr &job)
|
||||||
|
{
|
||||||
|
if (m_sftp->state() != SftpChannel::Initialized)
|
||||||
|
return SftpInvalidJob;
|
||||||
|
m_jobs.insert(job->jobId, job);
|
||||||
|
sendData(job->initialPacket(m_outgoingPacket).rawData());
|
||||||
|
return job->jobId;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SftpChannelPrivate::handleChannelSuccess()
|
||||||
|
{
|
||||||
|
if (channelState() == CloseRequested)
|
||||||
|
return;
|
||||||
|
qCDebug(sshLog, "sftp subsystem initialized");
|
||||||
|
sendData(m_outgoingPacket.generateInit(ProtocolVersion).rawData());
|
||||||
|
m_sftpState = InitSent;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SftpChannelPrivate::handleChannelFailure()
|
||||||
|
{
|
||||||
|
if (channelState() == CloseRequested)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (m_sftpState != SubsystemRequested) {
|
||||||
|
throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
|
||||||
|
"Unexpected SSH_MSG_CHANNEL_FAILURE packet.");
|
||||||
|
}
|
||||||
|
emit channelError(tr("Server could not start SFTP subsystem."));
|
||||||
|
closeChannel();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SftpChannelPrivate::handleChannelDataInternal(const QByteArray &data)
|
||||||
|
{
|
||||||
|
if (channelState() == CloseRequested)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_incomingData += data;
|
||||||
|
m_incomingPacket.consumeData(m_incomingData);
|
||||||
|
while (m_incomingPacket.isComplete()) {
|
||||||
|
handleCurrentPacket();
|
||||||
|
m_incomingPacket.clear();
|
||||||
|
m_incomingPacket.consumeData(m_incomingData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SftpChannelPrivate::handleChannelExtendedDataInternal(quint32 type,
|
||||||
|
const QByteArray &data)
|
||||||
|
{
|
||||||
|
qCWarning(sshLog, "Unexpected extended data '%s' of type %d on SFTP channel.",
|
||||||
|
data.data(), type);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SftpChannelPrivate::handleExitStatus(const SshChannelExitStatus &exitStatus)
|
||||||
|
{
|
||||||
|
qCDebug(sshLog, "Remote SFTP service exited with exit code %d", exitStatus.exitStatus);
|
||||||
|
|
||||||
|
if (channelState() == CloseRequested || channelState() == Closed)
|
||||||
|
return;
|
||||||
|
|
||||||
|
emit channelError(tr("The SFTP server finished unexpectedly with exit code %1.")
|
||||||
|
.arg(exitStatus.exitStatus));
|
||||||
|
|
||||||
|
// Note: According to the specs, the server must close the channel after this happens,
|
||||||
|
// but OpenSSH doesn't do that, so we need to initiate the closing procedure ourselves.
|
||||||
|
closeChannel();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SftpChannelPrivate::handleExitSignal(const SshChannelExitSignal &signal)
|
||||||
|
{
|
||||||
|
emit channelError(tr("The SFTP server crashed: %1.").arg(signal.error));
|
||||||
|
closeChannel(); // See above.
|
||||||
|
}
|
||||||
|
|
||||||
|
void SftpChannelPrivate::handleCurrentPacket()
|
||||||
|
{
|
||||||
|
qCDebug(sshLog, "Handling SFTP packet of type %d", m_incomingPacket.type());
|
||||||
|
switch (m_incomingPacket.type()) {
|
||||||
|
case SSH_FXP_VERSION:
|
||||||
|
handleServerVersion();
|
||||||
|
break;
|
||||||
|
case SSH_FXP_HANDLE:
|
||||||
|
handleHandle();
|
||||||
|
break;
|
||||||
|
case SSH_FXP_NAME:
|
||||||
|
handleName();
|
||||||
|
break;
|
||||||
|
case SSH_FXP_STATUS:
|
||||||
|
handleStatus();
|
||||||
|
break;
|
||||||
|
case SSH_FXP_DATA:
|
||||||
|
handleReadData();
|
||||||
|
break;
|
||||||
|
case SSH_FXP_ATTRS:
|
||||||
|
handleAttrs();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw SshServerException(SSH_DISCONNECT_PROTOCOL_ERROR,
|
||||||
|
"Unexpected packet.",
|
||||||
|
tr("Unexpected packet of type %1.").arg(m_incomingPacket.type()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SftpChannelPrivate::handleServerVersion()
|
||||||
|
{
|
||||||
|
checkChannelActive();
|
||||||
|
if (m_sftpState != InitSent) {
|
||||||
|
throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
|
||||||
|
"Unexpected SSH_FXP_VERSION packet.");
|
||||||
|
}
|
||||||
|
|
||||||
|
qCDebug(sshLog, "sftp init received");
|
||||||
|
const quint32 serverVersion = m_incomingPacket.extractServerVersion();
|
||||||
|
if (serverVersion != ProtocolVersion) {
|
||||||
|
emit channelError(tr("Protocol version mismatch: Expected %1, got %2")
|
||||||
|
.arg(serverVersion).arg(ProtocolVersion));
|
||||||
|
closeChannel();
|
||||||
|
} else {
|
||||||
|
m_sftpState = Initialized;
|
||||||
|
emit initialized();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SftpChannelPrivate::handleHandle()
|
||||||
|
{
|
||||||
|
const SftpHandleResponse &response = m_incomingPacket.asHandleResponse();
|
||||||
|
JobMap::Iterator it = lookupJob(response.requestId);
|
||||||
|
const QSharedPointer<AbstractSftpOperationWithHandle> job
|
||||||
|
= it.value().dynamicCast<AbstractSftpOperationWithHandle>();
|
||||||
|
if (job.isNull()) {
|
||||||
|
throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
|
||||||
|
"Unexpected SSH_FXP_HANDLE packet.");
|
||||||
|
}
|
||||||
|
if (job->state != AbstractSftpOperationWithHandle::OpenRequested) {
|
||||||
|
throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
|
||||||
|
"Unexpected SSH_FXP_HANDLE packet.");
|
||||||
|
}
|
||||||
|
job->remoteHandle = response.handle;
|
||||||
|
job->state = AbstractSftpOperationWithHandle::Open;
|
||||||
|
|
||||||
|
switch (it.value()->type()) {
|
||||||
|
case AbstractSftpOperation::ListDir:
|
||||||
|
handleLsHandle(it);
|
||||||
|
break;
|
||||||
|
case AbstractSftpOperation::CreateFile:
|
||||||
|
handleCreateFileHandle(it);
|
||||||
|
break;
|
||||||
|
case AbstractSftpOperation::Download:
|
||||||
|
handleGetHandle(it);
|
||||||
|
break;
|
||||||
|
case AbstractSftpOperation::UploadFile:
|
||||||
|
handlePutHandle(it);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Q_ASSERT(!"Oh no, I forgot to handle an SFTP operation type!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SftpChannelPrivate::handleLsHandle(const JobMap::Iterator &it)
|
||||||
|
{
|
||||||
|
SftpListDir::Ptr op = it.value().staticCast<SftpListDir>();
|
||||||
|
sendData(m_outgoingPacket.generateReadDir(op->remoteHandle,
|
||||||
|
op->jobId).rawData());
|
||||||
|
}
|
||||||
|
|
||||||
|
void SftpChannelPrivate::handleCreateFileHandle(const JobMap::Iterator &it)
|
||||||
|
{
|
||||||
|
SftpCreateFile::Ptr op = it.value().staticCast<SftpCreateFile>();
|
||||||
|
sendData(m_outgoingPacket.generateCloseHandle(op->remoteHandle,
|
||||||
|
op->jobId).rawData());
|
||||||
|
}
|
||||||
|
|
||||||
|
void SftpChannelPrivate::handleGetHandle(const JobMap::Iterator &it)
|
||||||
|
{
|
||||||
|
SftpDownload::Ptr op = it.value().staticCast<SftpDownload>();
|
||||||
|
sendData(m_outgoingPacket.generateFstat(op->remoteHandle,
|
||||||
|
op->jobId).rawData());
|
||||||
|
op->statRequested = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SftpChannelPrivate::handlePutHandle(const JobMap::Iterator &it)
|
||||||
|
{
|
||||||
|
SftpUploadFile::Ptr op = it.value().staticCast<SftpUploadFile>();
|
||||||
|
if (op->parentJob && op->parentJob->hasError)
|
||||||
|
sendTransferCloseHandle(op, it.key());
|
||||||
|
|
||||||
|
// OpenSSH does not implement the RFC's append functionality, so we
|
||||||
|
// have to emulate it.
|
||||||
|
if (op->mode == SftpAppendToExisting) {
|
||||||
|
sendData(m_outgoingPacket.generateFstat(op->remoteHandle,
|
||||||
|
op->jobId).rawData());
|
||||||
|
op->statRequested = true;
|
||||||
|
} else {
|
||||||
|
spawnWriteRequests(it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SftpChannelPrivate::handleStatus()
|
||||||
|
{
|
||||||
|
const SftpStatusResponse &response = m_incomingPacket.asStatusResponse();
|
||||||
|
qCDebug(sshLog, "%s: status = %d", Q_FUNC_INFO, response.status);
|
||||||
|
JobMap::Iterator it = lookupJob(response.requestId);
|
||||||
|
switch (it.value()->type()) {
|
||||||
|
case AbstractSftpOperation::ListDir:
|
||||||
|
handleLsStatus(it, response);
|
||||||
|
break;
|
||||||
|
case AbstractSftpOperation::Download:
|
||||||
|
handleGetStatus(it, response);
|
||||||
|
break;
|
||||||
|
case AbstractSftpOperation::UploadFile:
|
||||||
|
handlePutStatus(it, response);
|
||||||
|
break;
|
||||||
|
case AbstractSftpOperation::MakeDir:
|
||||||
|
handleMkdirStatus(it, response);
|
||||||
|
break;
|
||||||
|
case AbstractSftpOperation::StatFile:
|
||||||
|
case AbstractSftpOperation::RmDir:
|
||||||
|
case AbstractSftpOperation::Rm:
|
||||||
|
case AbstractSftpOperation::Rename:
|
||||||
|
case AbstractSftpOperation::CreateFile:
|
||||||
|
case AbstractSftpOperation::CreateLink:
|
||||||
|
handleStatusGeneric(it, response);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SftpChannelPrivate::handleStatusGeneric(const JobMap::Iterator &it,
|
||||||
|
const SftpStatusResponse &response)
|
||||||
|
{
|
||||||
|
AbstractSftpOperation::Ptr op = it.value();
|
||||||
|
const QString error = errorMessage(response, tr("Unknown error."));
|
||||||
|
emit finished(op->jobId, error);
|
||||||
|
m_jobs.erase(it);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SftpChannelPrivate::handleMkdirStatus(const JobMap::Iterator &it,
|
||||||
|
const SftpStatusResponse &response)
|
||||||
|
{
|
||||||
|
SftpMakeDir::Ptr op = it.value().staticCast<SftpMakeDir>();
|
||||||
|
QSharedPointer<SftpUploadDir> parentJob = op->parentJob;
|
||||||
|
if (parentJob == SftpUploadDir::Ptr()) {
|
||||||
|
handleStatusGeneric(it, response);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (parentJob->hasError) {
|
||||||
|
m_jobs.erase(it);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef QMap<SftpMakeDir::Ptr, SftpUploadDir::Dir>::Iterator DirIt;
|
||||||
|
DirIt dirIt = parentJob->mkdirsInProgress.find(op);
|
||||||
|
Q_ASSERT(dirIt != parentJob->mkdirsInProgress.end());
|
||||||
|
const QString &remoteDir = dirIt.value().remoteDir;
|
||||||
|
if (response.status == SSH_FX_OK) {
|
||||||
|
emit dataAvailable(parentJob->jobId,
|
||||||
|
tr("Created remote directory \"%1\".").arg(remoteDir));
|
||||||
|
} else if (response.status == SSH_FX_FAILURE) {
|
||||||
|
emit dataAvailable(parentJob->jobId,
|
||||||
|
tr("Remote directory \"%1\" already exists.").arg(remoteDir));
|
||||||
|
} else {
|
||||||
|
parentJob->setError();
|
||||||
|
emit finished(parentJob->jobId,
|
||||||
|
tr("Error creating directory \"%1\": %2")
|
||||||
|
.arg(remoteDir, response.errorString));
|
||||||
|
m_jobs.erase(it);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QDir localDir(dirIt.value().localDir);
|
||||||
|
const QFileInfoList &dirInfos
|
||||||
|
= localDir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot);
|
||||||
|
foreach (const QFileInfo &dirInfo, dirInfos) {
|
||||||
|
const QString remoteSubDir = remoteDir + QLatin1Char('/') + dirInfo.fileName();
|
||||||
|
const SftpMakeDir::Ptr mkdirOp(
|
||||||
|
new SftpMakeDir(++m_nextJobId, remoteSubDir, parentJob));
|
||||||
|
parentJob->mkdirsInProgress.insert(mkdirOp,
|
||||||
|
SftpUploadDir::Dir(dirInfo.absoluteFilePath(), remoteSubDir));
|
||||||
|
createJob(mkdirOp);
|
||||||
|
}
|
||||||
|
|
||||||
|
const QFileInfoList &fileInfos = localDir.entryInfoList(QDir::Files);
|
||||||
|
foreach (const QFileInfo &fileInfo, fileInfos) {
|
||||||
|
QSharedPointer<QFile> localFile(new QFile(fileInfo.absoluteFilePath()));
|
||||||
|
if (!localFile->open(QIODevice::ReadOnly)) {
|
||||||
|
parentJob->setError();
|
||||||
|
emit finished(parentJob->jobId,
|
||||||
|
tr("Could not open local file \"%1\": %2")
|
||||||
|
.arg(fileInfo.absoluteFilePath(), localFile->errorString()));
|
||||||
|
m_jobs.erase(it);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString remoteFilePath = remoteDir + QLatin1Char('/') + fileInfo.fileName();
|
||||||
|
SftpUploadFile::Ptr uploadFileOp(new SftpUploadFile(++m_nextJobId,
|
||||||
|
remoteFilePath, localFile, SftpOverwriteExisting, parentJob));
|
||||||
|
createJob(uploadFileOp);
|
||||||
|
parentJob->uploadsInProgress.append(uploadFileOp);
|
||||||
|
}
|
||||||
|
|
||||||
|
parentJob->mkdirsInProgress.erase(dirIt);
|
||||||
|
if (parentJob->mkdirsInProgress.isEmpty()
|
||||||
|
&& parentJob->uploadsInProgress.isEmpty())
|
||||||
|
emit finished(parentJob->jobId);
|
||||||
|
m_jobs.erase(it);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SftpChannelPrivate::handleLsStatus(const JobMap::Iterator &it,
|
||||||
|
const SftpStatusResponse &response)
|
||||||
|
{
|
||||||
|
SftpListDir::Ptr op = it.value().staticCast<SftpListDir>();
|
||||||
|
switch (op->state) {
|
||||||
|
case SftpListDir::OpenRequested:
|
||||||
|
emit finished(op->jobId, errorMessage(response.errorString,
|
||||||
|
tr("Remote directory could not be opened for reading.")));
|
||||||
|
m_jobs.erase(it);
|
||||||
|
break;
|
||||||
|
case SftpListDir::Open:
|
||||||
|
if (response.status != SSH_FX_EOF)
|
||||||
|
reportRequestError(op, errorMessage(response.errorString,
|
||||||
|
tr("Failed to list remote directory contents.")));
|
||||||
|
op->state = SftpListDir::CloseRequested;
|
||||||
|
sendData(m_outgoingPacket.generateCloseHandle(op->remoteHandle,
|
||||||
|
op->jobId).rawData());
|
||||||
|
break;
|
||||||
|
case SftpListDir::CloseRequested:
|
||||||
|
if (!op->hasError) {
|
||||||
|
const QString error = errorMessage(response,
|
||||||
|
tr("Failed to close remote directory."));
|
||||||
|
emit finished(op->jobId, error);
|
||||||
|
}
|
||||||
|
m_jobs.erase(it);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
|
||||||
|
"Unexpected SSH_FXP_STATUS packet.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SftpChannelPrivate::handleGetStatus(const JobMap::Iterator &it,
|
||||||
|
const SftpStatusResponse &response)
|
||||||
|
{
|
||||||
|
SftpDownload::Ptr op = it.value().staticCast<SftpDownload>();
|
||||||
|
switch (op->state) {
|
||||||
|
case SftpDownload::OpenRequested:
|
||||||
|
emit finished(op->jobId,
|
||||||
|
errorMessage(response.errorString,
|
||||||
|
tr("Failed to open remote file for reading.")));
|
||||||
|
m_jobs.erase(it);
|
||||||
|
break;
|
||||||
|
case SftpDownload::Open:
|
||||||
|
if (op->statRequested) {
|
||||||
|
reportRequestError(op, errorMessage(response.errorString,
|
||||||
|
tr("Failed to retrieve information on the remote file ('stat' failed).")));
|
||||||
|
sendTransferCloseHandle(op, response.requestId);
|
||||||
|
} else {
|
||||||
|
if ((response.status != SSH_FX_EOF || response.requestId != op->eofId)
|
||||||
|
&& !op->hasError)
|
||||||
|
reportRequestError(op, errorMessage(response.errorString,
|
||||||
|
tr("Failed to read remote file.")));
|
||||||
|
finishTransferRequest(it);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SftpDownload::CloseRequested:
|
||||||
|
Q_ASSERT(op->inFlightCount == 1);
|
||||||
|
if (!op->hasError) {
|
||||||
|
if (response.status == SSH_FX_OK)
|
||||||
|
emit finished(op->jobId);
|
||||||
|
else
|
||||||
|
reportRequestError(op, errorMessage(response.errorString,
|
||||||
|
tr("Failed to close remote file.")));
|
||||||
|
}
|
||||||
|
removeTransferRequest(it);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
|
||||||
|
"Unexpected SSH_FXP_STATUS packet.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SftpChannelPrivate::handlePutStatus(const JobMap::Iterator &it,
|
||||||
|
const SftpStatusResponse &response)
|
||||||
|
{
|
||||||
|
SftpUploadFile::Ptr job = it.value().staticCast<SftpUploadFile>();
|
||||||
|
switch (job->state) {
|
||||||
|
case SftpUploadFile::OpenRequested: {
|
||||||
|
bool emitError = false;
|
||||||
|
if (job->parentJob) {
|
||||||
|
if (!job->parentJob->hasError) {
|
||||||
|
job->parentJob->setError();
|
||||||
|
emitError = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
emitError = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (emitError) {
|
||||||
|
emit finished(job->jobId,
|
||||||
|
errorMessage(response.errorString,
|
||||||
|
tr("Failed to open remote file for writing.")));
|
||||||
|
}
|
||||||
|
m_jobs.erase(it);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case SftpUploadFile::Open:
|
||||||
|
if (job->hasError || (job->parentJob && job->parentJob->hasError)) {
|
||||||
|
job->hasError = true;
|
||||||
|
finishTransferRequest(it);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response.status == SSH_FX_OK) {
|
||||||
|
sendWriteRequest(it);
|
||||||
|
} else {
|
||||||
|
if (job->parentJob)
|
||||||
|
job->parentJob->setError();
|
||||||
|
reportRequestError(job, errorMessage(response.errorString,
|
||||||
|
tr("Failed to write remote file.")));
|
||||||
|
finishTransferRequest(it);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SftpUploadFile::CloseRequested:
|
||||||
|
Q_ASSERT(job->inFlightCount == 1);
|
||||||
|
if (job->hasError || (job->parentJob && job->parentJob->hasError)) {
|
||||||
|
m_jobs.erase(it);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response.status == SSH_FX_OK) {
|
||||||
|
if (job->parentJob) {
|
||||||
|
job->parentJob->uploadsInProgress.removeOne(job);
|
||||||
|
if (job->parentJob->mkdirsInProgress.isEmpty()
|
||||||
|
&& job->parentJob->uploadsInProgress.isEmpty())
|
||||||
|
emit finished(job->parentJob->jobId);
|
||||||
|
} else {
|
||||||
|
emit finished(job->jobId);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const QString error = errorMessage(response.errorString,
|
||||||
|
tr("Failed to close remote file."));
|
||||||
|
if (job->parentJob) {
|
||||||
|
job->parentJob->setError();
|
||||||
|
emit finished(job->parentJob->jobId, error);
|
||||||
|
} else {
|
||||||
|
emit finished(job->jobId, error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m_jobs.erase(it);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
|
||||||
|
"Unexpected SSH_FXP_STATUS packet.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SftpChannelPrivate::handleName()
|
||||||
|
{
|
||||||
|
const SftpNameResponse &response = m_incomingPacket.asNameResponse();
|
||||||
|
JobMap::Iterator it = lookupJob(response.requestId);
|
||||||
|
switch (it.value()->type()) {
|
||||||
|
case AbstractSftpOperation::ListDir: {
|
||||||
|
SftpListDir::Ptr op = it.value().staticCast<SftpListDir>();
|
||||||
|
if (op->state != SftpListDir::Open) {
|
||||||
|
throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
|
||||||
|
"Unexpected SSH_FXP_NAME packet.");
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<SftpFileInfo> fileInfoList;
|
||||||
|
for (int i = 0; i < response.files.count(); ++i) {
|
||||||
|
const SftpFile &file = response.files.at(i);
|
||||||
|
|
||||||
|
SftpFileInfo fileInfo;
|
||||||
|
fileInfo.name = file.fileName;
|
||||||
|
attributesToFileInfo(file.attributes, fileInfo);
|
||||||
|
fileInfoList << fileInfo;
|
||||||
|
}
|
||||||
|
emit fileInfoAvailable(op->jobId, fileInfoList);
|
||||||
|
sendData(m_outgoingPacket.generateReadDir(op->remoteHandle,
|
||||||
|
op->jobId).rawData());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
|
||||||
|
"Unexpected SSH_FXP_NAME packet.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SftpChannelPrivate::handleReadData()
|
||||||
|
{
|
||||||
|
const SftpDataResponse &response = m_incomingPacket.asDataResponse();
|
||||||
|
JobMap::Iterator it = lookupJob(response.requestId);
|
||||||
|
if (it.value()->type() != AbstractSftpOperation::Download) {
|
||||||
|
throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
|
||||||
|
"Unexpected SSH_FXP_DATA packet.");
|
||||||
|
}
|
||||||
|
|
||||||
|
SftpDownload::Ptr op = it.value().staticCast<SftpDownload>();
|
||||||
|
if (op->hasError) {
|
||||||
|
finishTransferRequest(it);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!op->localFile->seek(op->offsets[response.requestId])) {
|
||||||
|
reportRequestError(op, op->localFile->errorString());
|
||||||
|
finishTransferRequest(it);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (op->localFile->write(response.data) != response.data.size()) {
|
||||||
|
reportRequestError(op, op->localFile->errorString());
|
||||||
|
finishTransferRequest(it);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (op->offset >= op->fileSize && op->fileSize != 0)
|
||||||
|
finishTransferRequest(it);
|
||||||
|
else
|
||||||
|
sendReadRequest(op, response.requestId);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SftpChannelPrivate::handleAttrs()
|
||||||
|
{
|
||||||
|
const SftpAttrsResponse &response = m_incomingPacket.asAttrsResponse();
|
||||||
|
JobMap::Iterator it = lookupJob(response.requestId);
|
||||||
|
|
||||||
|
SftpStatFile::Ptr statOp = it.value().dynamicCast<SftpStatFile>();
|
||||||
|
if (statOp) {
|
||||||
|
SftpFileInfo fileInfo;
|
||||||
|
fileInfo.name = QFileInfo(statOp->path).fileName();
|
||||||
|
attributesToFileInfo(response.attrs, fileInfo);
|
||||||
|
emit fileInfoAvailable(it.key(), QList<SftpFileInfo>() << fileInfo);
|
||||||
|
emit finished(it.key());
|
||||||
|
m_jobs.erase(it);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
AbstractSftpTransfer::Ptr transfer
|
||||||
|
= it.value().dynamicCast<AbstractSftpTransfer>();
|
||||||
|
if (!transfer || transfer->state != AbstractSftpTransfer::Open
|
||||||
|
|| !transfer->statRequested) {
|
||||||
|
throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
|
||||||
|
"Unexpected SSH_FXP_ATTRS packet.");
|
||||||
|
}
|
||||||
|
Q_ASSERT(transfer->type() == AbstractSftpOperation::UploadFile
|
||||||
|
|| transfer->type() == AbstractSftpOperation::Download);
|
||||||
|
|
||||||
|
if (transfer->type() == AbstractSftpOperation::Download) {
|
||||||
|
SftpDownload::Ptr op = transfer.staticCast<SftpDownload>();
|
||||||
|
if (response.attrs.sizePresent) {
|
||||||
|
op->fileSize = response.attrs.size;
|
||||||
|
} else {
|
||||||
|
op->fileSize = 0;
|
||||||
|
op->eofId = op->jobId;
|
||||||
|
}
|
||||||
|
op->statRequested = false;
|
||||||
|
spawnReadRequests(op);
|
||||||
|
} else {
|
||||||
|
SftpUploadFile::Ptr op = transfer.staticCast<SftpUploadFile>();
|
||||||
|
if (op->parentJob && op->parentJob->hasError) {
|
||||||
|
op->hasError = true;
|
||||||
|
sendTransferCloseHandle(op, op->jobId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response.attrs.sizePresent) {
|
||||||
|
op->offset = response.attrs.size;
|
||||||
|
spawnWriteRequests(it);
|
||||||
|
} else {
|
||||||
|
if (op->parentJob)
|
||||||
|
op->parentJob->setError();
|
||||||
|
reportRequestError(op, tr("Cannot append to remote file: "
|
||||||
|
"Server does not support the file size attribute."));
|
||||||
|
sendTransferCloseHandle(op, op->jobId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SftpChannelPrivate::JobMap::Iterator SftpChannelPrivate::lookupJob(SftpJobId id)
|
||||||
|
{
|
||||||
|
JobMap::Iterator it = m_jobs.find(id);
|
||||||
|
if (it == m_jobs.end()) {
|
||||||
|
throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
|
||||||
|
"Invalid request id in SFTP packet.");
|
||||||
|
}
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SftpChannelPrivate::closeHook()
|
||||||
|
{
|
||||||
|
for (JobMap::ConstIterator it = m_jobs.constBegin(); it != m_jobs.constEnd(); ++it)
|
||||||
|
emit finished(it.key(), tr("SFTP channel closed unexpectedly."));
|
||||||
|
m_jobs.clear();
|
||||||
|
m_incomingData.clear();
|
||||||
|
m_incomingPacket.clear();
|
||||||
|
emit closed();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SftpChannelPrivate::handleOpenSuccessInternal()
|
||||||
|
{
|
||||||
|
qCDebug(sshLog, "SFTP session started");
|
||||||
|
m_sendFacility.sendSftpPacket(remoteChannel());
|
||||||
|
m_sftpState = SubsystemRequested;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SftpChannelPrivate::handleOpenFailureInternal(const QString &reason)
|
||||||
|
{
|
||||||
|
if (channelState() != SessionRequested) {
|
||||||
|
throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
|
||||||
|
"Unexpected SSH_MSG_CHANNEL_OPEN_FAILURE packet.");
|
||||||
|
}
|
||||||
|
emit channelError(tr("Server could not start session: %1").arg(reason));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SftpChannelPrivate::sendReadRequest(const SftpDownload::Ptr &job,
|
||||||
|
quint32 requestId)
|
||||||
|
{
|
||||||
|
Q_ASSERT(job->eofId == SftpInvalidJob);
|
||||||
|
sendData(m_outgoingPacket.generateReadFile(job->remoteHandle, job->offset,
|
||||||
|
AbstractSftpPacket::MaxDataSize, requestId).rawData());
|
||||||
|
job->offsets[requestId] = job->offset;
|
||||||
|
job->offset += AbstractSftpPacket::MaxDataSize;
|
||||||
|
if (job->offset >= job->fileSize)
|
||||||
|
job->eofId = requestId;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SftpChannelPrivate::reportRequestError(const AbstractSftpOperationWithHandle::Ptr &job,
|
||||||
|
const QString &error)
|
||||||
|
{
|
||||||
|
emit finished(job->jobId, error);
|
||||||
|
job->hasError = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SftpChannelPrivate::finishTransferRequest(const JobMap::Iterator &it)
|
||||||
|
{
|
||||||
|
AbstractSftpTransfer::Ptr job = it.value().staticCast<AbstractSftpTransfer>();
|
||||||
|
if (job->inFlightCount == 1)
|
||||||
|
sendTransferCloseHandle(job, it.key());
|
||||||
|
else
|
||||||
|
removeTransferRequest(it);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SftpChannelPrivate::sendTransferCloseHandle(const AbstractSftpTransfer::Ptr &job,
|
||||||
|
quint32 requestId)
|
||||||
|
{
|
||||||
|
sendData(m_outgoingPacket.generateCloseHandle(job->remoteHandle,
|
||||||
|
requestId).rawData());
|
||||||
|
job->state = SftpDownload::CloseRequested;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SftpChannelPrivate::attributesToFileInfo(const SftpFileAttributes &attributes,
|
||||||
|
SftpFileInfo &fileInfo) const
|
||||||
|
{
|
||||||
|
if (attributes.sizePresent) {
|
||||||
|
fileInfo.sizeValid = true;
|
||||||
|
fileInfo.size = attributes.size;
|
||||||
|
}
|
||||||
|
if (attributes.permissionsPresent) {
|
||||||
|
if (attributes.permissions & 0x8000) // S_IFREG
|
||||||
|
fileInfo.type = FileTypeRegular;
|
||||||
|
else if (attributes.permissions & 0x4000) // S_IFDIR
|
||||||
|
fileInfo.type = FileTypeDirectory;
|
||||||
|
else
|
||||||
|
fileInfo.type = FileTypeOther;
|
||||||
|
fileInfo.permissionsValid = true;
|
||||||
|
fileInfo.permissions = 0;
|
||||||
|
if (attributes.permissions & 00001) // S_IXOTH
|
||||||
|
fileInfo.permissions |= QFile::ExeOther;
|
||||||
|
if (attributes.permissions & 00002) // S_IWOTH
|
||||||
|
fileInfo.permissions |= QFile::WriteOther;
|
||||||
|
if (attributes.permissions & 00004) // S_IROTH
|
||||||
|
fileInfo.permissions |= QFile::ReadOther;
|
||||||
|
if (attributes.permissions & 00010) // S_IXGRP
|
||||||
|
fileInfo.permissions |= QFile::ExeGroup;
|
||||||
|
if (attributes.permissions & 00020) // S_IWGRP
|
||||||
|
fileInfo.permissions |= QFile::WriteGroup;
|
||||||
|
if (attributes.permissions & 00040) // S_IRGRP
|
||||||
|
fileInfo.permissions |= QFile::ReadGroup;
|
||||||
|
if (attributes.permissions & 00100) // S_IXUSR
|
||||||
|
fileInfo.permissions |= QFile::ExeUser | QFile::ExeOwner;
|
||||||
|
if (attributes.permissions & 00200) // S_IWUSR
|
||||||
|
fileInfo.permissions |= QFile::WriteUser | QFile::WriteOwner;
|
||||||
|
if (attributes.permissions & 00400) // S_IRUSR
|
||||||
|
fileInfo.permissions |= QFile::ReadUser | QFile::ReadOwner;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SftpChannelPrivate::removeTransferRequest(const JobMap::Iterator &it)
|
||||||
|
{
|
||||||
|
--it.value().staticCast<AbstractSftpTransfer>()->inFlightCount;
|
||||||
|
m_jobs.erase(it);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SftpChannelPrivate::sendWriteRequest(const JobMap::Iterator &it)
|
||||||
|
{
|
||||||
|
SftpUploadFile::Ptr job = it.value().staticCast<SftpUploadFile>();
|
||||||
|
QByteArray data = job->localFile->read(AbstractSftpPacket::MaxDataSize);
|
||||||
|
if (job->localFile->error() != QFile::NoError) {
|
||||||
|
if (job->parentJob)
|
||||||
|
job->parentJob->setError();
|
||||||
|
reportRequestError(job, tr("Error reading local file: %1")
|
||||||
|
.arg(job->localFile->errorString()));
|
||||||
|
finishTransferRequest(it);
|
||||||
|
} else if (data.isEmpty()) {
|
||||||
|
finishTransferRequest(it);
|
||||||
|
} else {
|
||||||
|
sendData(m_outgoingPacket.generateWriteFile(job->remoteHandle,
|
||||||
|
job->offset, data, it.key()).rawData());
|
||||||
|
job->offset += AbstractSftpPacket::MaxDataSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SftpChannelPrivate::spawnWriteRequests(const JobMap::Iterator &it)
|
||||||
|
{
|
||||||
|
SftpUploadFile::Ptr op = it.value().staticCast<SftpUploadFile>();
|
||||||
|
op->calculateInFlightCount(AbstractSftpPacket::MaxDataSize);
|
||||||
|
sendWriteRequest(it);
|
||||||
|
for (int i = 1; !op->hasError && i < op->inFlightCount; ++i)
|
||||||
|
sendWriteRequest(m_jobs.insert(++m_nextJobId, op));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SftpChannelPrivate::spawnReadRequests(const SftpDownload::Ptr &job)
|
||||||
|
{
|
||||||
|
job->calculateInFlightCount(AbstractSftpPacket::MaxDataSize);
|
||||||
|
sendReadRequest(job, job->jobId);
|
||||||
|
for (int i = 1; i < job->inFlightCount; ++i) {
|
||||||
|
const quint32 requestId = ++m_nextJobId;
|
||||||
|
m_jobs.insert(requestId, job);
|
||||||
|
sendReadRequest(job, requestId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Internal
|
||||||
|
} // namespace QSsh
|
||||||
103
client/3rd/QtSsh/src/ssh/sftpchannel.h
Normal file
103
client/3rd/QtSsh/src/ssh/sftpchannel.h
Normal file
|
|
@ -0,0 +1,103 @@
|
||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2016 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "sftpdefs.h"
|
||||||
|
#include "sftpincomingpacket_p.h"
|
||||||
|
|
||||||
|
#include "ssh_global.h"
|
||||||
|
|
||||||
|
#include <QByteArray>
|
||||||
|
#include <QObject>
|
||||||
|
#include <QSharedPointer>
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
namespace QSsh {
|
||||||
|
|
||||||
|
namespace Internal {
|
||||||
|
class SftpChannelPrivate;
|
||||||
|
class SshChannelManager;
|
||||||
|
class SshSendFacility;
|
||||||
|
} // namespace Internal
|
||||||
|
|
||||||
|
class QSSH_EXPORT SftpChannel : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
friend class Internal::SftpChannelPrivate;
|
||||||
|
friend class Internal::SshChannelManager;
|
||||||
|
public:
|
||||||
|
typedef QSharedPointer<SftpChannel> Ptr;
|
||||||
|
|
||||||
|
enum State { Uninitialized, Initializing, Initialized, Closing, Closed };
|
||||||
|
State state() const;
|
||||||
|
|
||||||
|
void initialize();
|
||||||
|
void closeChannel();
|
||||||
|
|
||||||
|
SftpJobId statFile(const QString &path);
|
||||||
|
SftpJobId listDirectory(const QString &dirPath);
|
||||||
|
SftpJobId createDirectory(const QString &dirPath);
|
||||||
|
SftpJobId removeDirectory(const QString &dirPath);
|
||||||
|
SftpJobId removeFile(const QString &filePath);
|
||||||
|
SftpJobId renameFileOrDirectory(const QString &oldPath,
|
||||||
|
const QString &newPath);
|
||||||
|
SftpJobId createFile(const QString &filePath, SftpOverwriteMode mode);
|
||||||
|
SftpJobId createLink(const QString &filePath, const QString &target);
|
||||||
|
SftpJobId uploadFile(const QString &localFilePath,
|
||||||
|
const QString &remoteFilePath, SftpOverwriteMode mode);
|
||||||
|
SftpJobId downloadFile(const QString &remoteFilePath,
|
||||||
|
const QString &localFilePath, SftpOverwriteMode mode);
|
||||||
|
SftpJobId uploadDir(const QString &localDirPath,
|
||||||
|
const QString &remoteParentDirPath);
|
||||||
|
|
||||||
|
~SftpChannel();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void initialized();
|
||||||
|
void channelError(const QString &reason);
|
||||||
|
void closed();
|
||||||
|
|
||||||
|
// error.isEmpty <=> finished successfully
|
||||||
|
void finished(QSsh::SftpJobId job, const QString &error = QString());
|
||||||
|
|
||||||
|
// TODO: Also emit for each file copied by uploadDir().
|
||||||
|
void dataAvailable(QSsh::SftpJobId job, const QString &data);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This signal is emitted as a result of:
|
||||||
|
* - statFile() (with the list having exactly one element)
|
||||||
|
* - listDirectory() (potentially more than once)
|
||||||
|
*/
|
||||||
|
void fileInfoAvailable(QSsh::SftpJobId job, const QList<QSsh::SftpFileInfo> &fileInfoList);
|
||||||
|
|
||||||
|
private:
|
||||||
|
SftpChannel(quint32 channelId, Internal::SshSendFacility &sendFacility);
|
||||||
|
|
||||||
|
Internal::SftpChannelPrivate *d;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace QSsh
|
||||||
124
client/3rd/QtSsh/src/ssh/sftpchannel_p.h
Normal file
124
client/3rd/QtSsh/src/ssh/sftpchannel_p.h
Normal file
|
|
@ -0,0 +1,124 @@
|
||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2016 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "sftpdefs.h"
|
||||||
|
#include "sftpincomingpacket_p.h"
|
||||||
|
#include "sftpoperation_p.h"
|
||||||
|
#include "sftpoutgoingpacket_p.h"
|
||||||
|
#include "sshchannel_p.h"
|
||||||
|
|
||||||
|
#include <QByteArray>
|
||||||
|
#include <QMap>
|
||||||
|
|
||||||
|
namespace QSsh {
|
||||||
|
class SftpChannel;
|
||||||
|
namespace Internal {
|
||||||
|
|
||||||
|
class SftpChannelPrivate : public AbstractSshChannel
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
friend class QSsh::SftpChannel;
|
||||||
|
public:
|
||||||
|
enum SftpState { Inactive, SubsystemRequested, InitSent, Initialized };
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void initialized();
|
||||||
|
void channelError(const QString &reason);
|
||||||
|
void closed();
|
||||||
|
void finished(QSsh::SftpJobId job, const QString &error = QString());
|
||||||
|
void dataAvailable(QSsh::SftpJobId job, const QString &data);
|
||||||
|
void fileInfoAvailable(QSsh::SftpJobId job, const QList<QSsh::SftpFileInfo> &fileInfoList);
|
||||||
|
|
||||||
|
private:
|
||||||
|
typedef QMap<SftpJobId, AbstractSftpOperation::Ptr> JobMap;
|
||||||
|
|
||||||
|
SftpChannelPrivate(quint32 channelId, SshSendFacility &sendFacility,
|
||||||
|
SftpChannel *sftp);
|
||||||
|
SftpJobId createJob(const AbstractSftpOperation::Ptr &job);
|
||||||
|
|
||||||
|
virtual void handleChannelSuccess();
|
||||||
|
virtual void handleChannelFailure();
|
||||||
|
|
||||||
|
virtual void handleOpenSuccessInternal();
|
||||||
|
virtual void handleOpenFailureInternal(const QString &reason);
|
||||||
|
virtual void handleChannelDataInternal(const QByteArray &data);
|
||||||
|
virtual void handleChannelExtendedDataInternal(quint32 type,
|
||||||
|
const QByteArray &data);
|
||||||
|
virtual void handleExitStatus(const SshChannelExitStatus &exitStatus);
|
||||||
|
virtual void handleExitSignal(const SshChannelExitSignal &signal);
|
||||||
|
|
||||||
|
virtual void closeHook();
|
||||||
|
|
||||||
|
void handleCurrentPacket();
|
||||||
|
void handleServerVersion();
|
||||||
|
void handleHandle();
|
||||||
|
void handleStatus();
|
||||||
|
void handleName();
|
||||||
|
void handleReadData();
|
||||||
|
void handleAttrs();
|
||||||
|
|
||||||
|
void handleStatusGeneric(const JobMap::Iterator &it,
|
||||||
|
const SftpStatusResponse &response);
|
||||||
|
void handleMkdirStatus(const JobMap::Iterator &it,
|
||||||
|
const SftpStatusResponse &response);
|
||||||
|
void handleLsStatus(const JobMap::Iterator &it,
|
||||||
|
const SftpStatusResponse &response);
|
||||||
|
void handleGetStatus(const JobMap::Iterator &it,
|
||||||
|
const SftpStatusResponse &response);
|
||||||
|
void handlePutStatus(const JobMap::Iterator &it,
|
||||||
|
const SftpStatusResponse &response);
|
||||||
|
|
||||||
|
void handleLsHandle(const JobMap::Iterator &it);
|
||||||
|
void handleCreateFileHandle(const JobMap::Iterator &it);
|
||||||
|
void handleGetHandle(const JobMap::Iterator &it);
|
||||||
|
void handlePutHandle(const JobMap::Iterator &it);
|
||||||
|
|
||||||
|
void spawnReadRequests(const SftpDownload::Ptr &job);
|
||||||
|
void spawnWriteRequests(const JobMap::Iterator &it);
|
||||||
|
void sendReadRequest(const SftpDownload::Ptr &job, quint32 requestId);
|
||||||
|
void sendWriteRequest(const JobMap::Iterator &it);
|
||||||
|
void finishTransferRequest(const JobMap::Iterator &it);
|
||||||
|
void removeTransferRequest(const JobMap::Iterator &it);
|
||||||
|
void reportRequestError(const AbstractSftpOperationWithHandle::Ptr &job,
|
||||||
|
const QString &error);
|
||||||
|
void sendTransferCloseHandle(const AbstractSftpTransfer::Ptr &job,
|
||||||
|
quint32 requestId);
|
||||||
|
|
||||||
|
void attributesToFileInfo(const SftpFileAttributes &attributes, SftpFileInfo &fileInfo) const;
|
||||||
|
|
||||||
|
JobMap::Iterator lookupJob(SftpJobId id);
|
||||||
|
JobMap m_jobs;
|
||||||
|
SftpOutgoingPacket m_outgoingPacket;
|
||||||
|
SftpIncomingPacket m_incomingPacket;
|
||||||
|
QByteArray m_incomingData;
|
||||||
|
SftpJobId m_nextJobId;
|
||||||
|
SftpState m_sftpState;
|
||||||
|
SftpChannel *m_sftp;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Internal
|
||||||
|
} // namespace QSsh
|
||||||
28
client/3rd/QtSsh/src/ssh/sftpdefs.cpp
Normal file
28
client/3rd/QtSsh/src/ssh/sftpdefs.cpp
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2016 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#include "sftpdefs.h"
|
||||||
|
|
||||||
|
namespace QSsh { const SftpJobId SftpInvalidJob = 0; }
|
||||||
59
client/3rd/QtSsh/src/ssh/sftpdefs.h
Normal file
59
client/3rd/QtSsh/src/ssh/sftpdefs.h
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2016 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "ssh_global.h"
|
||||||
|
|
||||||
|
#include <QFile>
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
namespace QSsh {
|
||||||
|
|
||||||
|
typedef quint32 SftpJobId;
|
||||||
|
QSSH_EXPORT extern const SftpJobId SftpInvalidJob;
|
||||||
|
|
||||||
|
enum SftpOverwriteMode {
|
||||||
|
SftpOverwriteExisting, SftpAppendToExisting, SftpSkipExisting
|
||||||
|
};
|
||||||
|
|
||||||
|
enum SftpFileType { FileTypeRegular, FileTypeDirectory, FileTypeOther, FileTypeUnknown };
|
||||||
|
|
||||||
|
class QSSH_EXPORT SftpFileInfo
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SftpFileInfo() : type(FileTypeUnknown), sizeValid(false), permissionsValid(false) { }
|
||||||
|
|
||||||
|
QString name;
|
||||||
|
SftpFileType type;
|
||||||
|
quint64 size;
|
||||||
|
QFile::Permissions permissions;
|
||||||
|
|
||||||
|
// The RFC allows an SFTP server not to support any file attributes beyond the name.
|
||||||
|
bool sizeValid;
|
||||||
|
bool permissionsValid;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace QSsh
|
||||||
383
client/3rd/QtSsh/src/ssh/sftpfilesystemmodel.cpp
Normal file
383
client/3rd/QtSsh/src/ssh/sftpfilesystemmodel.cpp
Normal file
|
|
@ -0,0 +1,383 @@
|
||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2016 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#include "sftpfilesystemmodel.h"
|
||||||
|
|
||||||
|
#include "sftpchannel.h"
|
||||||
|
#include "sshconnection.h"
|
||||||
|
#include "sshconnectionmanager.h"
|
||||||
|
|
||||||
|
#include <QFileInfo>
|
||||||
|
#include <QHash>
|
||||||
|
#include <QIcon>
|
||||||
|
#include <QList>
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
namespace QSsh {
|
||||||
|
namespace Internal {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
class SftpDirNode;
|
||||||
|
class SftpFileNode
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SftpFileNode() : parent(0) { }
|
||||||
|
virtual ~SftpFileNode() { }
|
||||||
|
|
||||||
|
QString path;
|
||||||
|
SftpFileInfo fileInfo;
|
||||||
|
SftpDirNode *parent;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SftpDirNode : public SftpFileNode
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SftpDirNode() : lsState(LsNotYetCalled) { }
|
||||||
|
~SftpDirNode() { qDeleteAll(children); }
|
||||||
|
|
||||||
|
enum { LsNotYetCalled, LsRunning, LsFinished } lsState;
|
||||||
|
QList<SftpFileNode *> children;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef QHash<SftpJobId, SftpDirNode *> DirNodeHash;
|
||||||
|
|
||||||
|
SftpFileNode *indexToFileNode(const QModelIndex &index)
|
||||||
|
{
|
||||||
|
return static_cast<SftpFileNode *>(index.internalPointer());
|
||||||
|
}
|
||||||
|
|
||||||
|
SftpDirNode *indexToDirNode(const QModelIndex &index)
|
||||||
|
{
|
||||||
|
SftpFileNode * const fileNode = indexToFileNode(index);
|
||||||
|
QSSH_ASSERT(fileNode);
|
||||||
|
return dynamic_cast<SftpDirNode *>(fileNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // anonymous namespace
|
||||||
|
|
||||||
|
class SftpFileSystemModelPrivate
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SshConnection *sshConnection;
|
||||||
|
SftpChannel::Ptr sftpChannel;
|
||||||
|
QString rootDirectory;
|
||||||
|
SftpFileNode *rootNode;
|
||||||
|
SftpJobId statJobId;
|
||||||
|
DirNodeHash lsOps;
|
||||||
|
QList<SftpJobId> externalJobs;
|
||||||
|
};
|
||||||
|
} // namespace Internal
|
||||||
|
|
||||||
|
using namespace Internal;
|
||||||
|
|
||||||
|
SftpFileSystemModel::SftpFileSystemModel(QObject *parent)
|
||||||
|
: QAbstractItemModel(parent), d(new SftpFileSystemModelPrivate)
|
||||||
|
{
|
||||||
|
d->sshConnection = 0;
|
||||||
|
d->rootDirectory = QLatin1Char('/');
|
||||||
|
d->rootNode = 0;
|
||||||
|
d->statJobId = SftpInvalidJob;
|
||||||
|
}
|
||||||
|
|
||||||
|
SftpFileSystemModel::~SftpFileSystemModel()
|
||||||
|
{
|
||||||
|
shutDown();
|
||||||
|
delete d;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SftpFileSystemModel::setSshConnection(const SshConnectionParameters &sshParams)
|
||||||
|
{
|
||||||
|
QSSH_ASSERT_AND_RETURN(!d->sshConnection);
|
||||||
|
d->sshConnection = QSsh::acquireConnection(sshParams);
|
||||||
|
connect(d->sshConnection, &SshConnection::error,
|
||||||
|
this, &SftpFileSystemModel::handleSshConnectionFailure);
|
||||||
|
if (d->sshConnection->state() == SshConnection::Connected) {
|
||||||
|
handleSshConnectionEstablished();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
connect(d->sshConnection, &SshConnection::connected,
|
||||||
|
this, &SftpFileSystemModel::handleSshConnectionEstablished);
|
||||||
|
if (d->sshConnection->state() == SshConnection::Unconnected)
|
||||||
|
d->sshConnection->connectToHost();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SftpFileSystemModel::setRootDirectory(const QString &path)
|
||||||
|
{
|
||||||
|
beginResetModel();
|
||||||
|
d->rootDirectory = path;
|
||||||
|
delete d->rootNode;
|
||||||
|
d->rootNode = 0;
|
||||||
|
d->lsOps.clear();
|
||||||
|
d->statJobId = SftpInvalidJob;
|
||||||
|
endResetModel();
|
||||||
|
statRootDirectory();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString SftpFileSystemModel::rootDirectory() const
|
||||||
|
{
|
||||||
|
return d->rootDirectory;
|
||||||
|
}
|
||||||
|
|
||||||
|
SftpJobId SftpFileSystemModel::downloadFile(const QModelIndex &index, const QString &targetFilePath)
|
||||||
|
{
|
||||||
|
QSSH_ASSERT_AND_RETURN_VALUE(d->rootNode, SftpInvalidJob);
|
||||||
|
const SftpFileNode * const fileNode = indexToFileNode(index);
|
||||||
|
QSSH_ASSERT_AND_RETURN_VALUE(fileNode, SftpInvalidJob);
|
||||||
|
QSSH_ASSERT_AND_RETURN_VALUE(fileNode->fileInfo.type == FileTypeRegular, SftpInvalidJob);
|
||||||
|
const SftpJobId jobId = d->sftpChannel->downloadFile(fileNode->path, targetFilePath,
|
||||||
|
SftpOverwriteExisting);
|
||||||
|
if (jobId != SftpInvalidJob)
|
||||||
|
d->externalJobs << jobId;
|
||||||
|
return jobId;
|
||||||
|
}
|
||||||
|
|
||||||
|
int SftpFileSystemModel::columnCount(const QModelIndex &parent) const
|
||||||
|
{
|
||||||
|
Q_UNUSED(parent);
|
||||||
|
return 2; // type + name
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant SftpFileSystemModel::data(const QModelIndex &index, int role) const
|
||||||
|
{
|
||||||
|
const SftpFileNode * const node = indexToFileNode(index);
|
||||||
|
if (index.column() == 0 && role == Qt::DecorationRole) {
|
||||||
|
switch (node->fileInfo.type) {
|
||||||
|
case FileTypeRegular:
|
||||||
|
case FileTypeOther:
|
||||||
|
return QIcon(":/utils/images/unknownfile.png");
|
||||||
|
case FileTypeDirectory:
|
||||||
|
return QIcon(":/utils/images/dir.png");
|
||||||
|
case FileTypeUnknown:
|
||||||
|
return QIcon(":/utils/images/help.png"); // Shows a question mark.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (index.column() == 1) {
|
||||||
|
if (role == Qt::DisplayRole)
|
||||||
|
return node->fileInfo.name;
|
||||||
|
if (role == PathRole)
|
||||||
|
return node->path;
|
||||||
|
}
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
|
||||||
|
Qt::ItemFlags SftpFileSystemModel::flags(const QModelIndex &index) const
|
||||||
|
{
|
||||||
|
if (!index.isValid())
|
||||||
|
return Qt::NoItemFlags;
|
||||||
|
return Qt::ItemIsSelectable | Qt::ItemIsEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant SftpFileSystemModel::headerData(int section, Qt::Orientation orientation, int role) const
|
||||||
|
{
|
||||||
|
if (orientation != Qt::Horizontal)
|
||||||
|
return QVariant();
|
||||||
|
if (role != Qt::DisplayRole)
|
||||||
|
return QVariant();
|
||||||
|
if (section == 0)
|
||||||
|
return tr("File Type");
|
||||||
|
if (section == 1)
|
||||||
|
return tr("File Name");
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
|
||||||
|
QModelIndex SftpFileSystemModel::index(int row, int column, const QModelIndex &parent) const
|
||||||
|
{
|
||||||
|
if (row < 0 || row >= rowCount(parent) || column < 0 || column >= columnCount(parent))
|
||||||
|
return QModelIndex();
|
||||||
|
if (!d->rootNode)
|
||||||
|
return QModelIndex();
|
||||||
|
if (!parent.isValid())
|
||||||
|
return createIndex(row, column, d->rootNode);
|
||||||
|
const SftpDirNode * const parentNode = indexToDirNode(parent);
|
||||||
|
QSSH_ASSERT_AND_RETURN_VALUE(parentNode, QModelIndex());
|
||||||
|
QSSH_ASSERT_AND_RETURN_VALUE(row < parentNode->children.count(), QModelIndex());
|
||||||
|
SftpFileNode * const childNode = parentNode->children.at(row);
|
||||||
|
return createIndex(row, column, childNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
QModelIndex SftpFileSystemModel::parent(const QModelIndex &child) const
|
||||||
|
{
|
||||||
|
if (!child.isValid()) // Don't assert on this, since the model tester tries it.
|
||||||
|
return QModelIndex();
|
||||||
|
|
||||||
|
const SftpFileNode * const childNode = indexToFileNode(child);
|
||||||
|
QSSH_ASSERT_AND_RETURN_VALUE(childNode, QModelIndex());
|
||||||
|
if (childNode == d->rootNode)
|
||||||
|
return QModelIndex();
|
||||||
|
SftpDirNode * const parentNode = childNode->parent;
|
||||||
|
if (parentNode == d->rootNode)
|
||||||
|
return createIndex(0, 0, d->rootNode);
|
||||||
|
const SftpDirNode * const grandParentNode = parentNode->parent;
|
||||||
|
QSSH_ASSERT_AND_RETURN_VALUE(grandParentNode, QModelIndex());
|
||||||
|
return createIndex(grandParentNode->children.indexOf(parentNode), 0, parentNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
int SftpFileSystemModel::rowCount(const QModelIndex &parent) const
|
||||||
|
{
|
||||||
|
if (!d->rootNode)
|
||||||
|
return 0;
|
||||||
|
if (!parent.isValid())
|
||||||
|
return 1;
|
||||||
|
if (parent.column() != 0)
|
||||||
|
return 0;
|
||||||
|
SftpDirNode * const dirNode = indexToDirNode(parent);
|
||||||
|
if (!dirNode)
|
||||||
|
return 0;
|
||||||
|
if (dirNode->lsState != SftpDirNode::LsNotYetCalled)
|
||||||
|
return dirNode->children.count();
|
||||||
|
d->lsOps.insert(d->sftpChannel->listDirectory(dirNode->path), dirNode);
|
||||||
|
dirNode->lsState = SftpDirNode::LsRunning;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SftpFileSystemModel::statRootDirectory()
|
||||||
|
{
|
||||||
|
d->statJobId = d->sftpChannel->statFile(d->rootDirectory);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SftpFileSystemModel::shutDown()
|
||||||
|
{
|
||||||
|
if (d->sftpChannel) {
|
||||||
|
disconnect(d->sftpChannel.data(), 0, this, 0);
|
||||||
|
d->sftpChannel->closeChannel();
|
||||||
|
d->sftpChannel.clear();
|
||||||
|
}
|
||||||
|
if (d->sshConnection) {
|
||||||
|
disconnect(d->sshConnection, 0, this, 0);
|
||||||
|
QSsh::releaseConnection(d->sshConnection);
|
||||||
|
d->sshConnection = 0;
|
||||||
|
}
|
||||||
|
delete d->rootNode;
|
||||||
|
d->rootNode = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SftpFileSystemModel::handleSshConnectionFailure()
|
||||||
|
{
|
||||||
|
emit connectionError(d->sshConnection->errorString());
|
||||||
|
beginResetModel();
|
||||||
|
shutDown();
|
||||||
|
endResetModel();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SftpFileSystemModel::handleSftpChannelInitialized()
|
||||||
|
{
|
||||||
|
connect(d->sftpChannel.data(),
|
||||||
|
&SftpChannel::fileInfoAvailable,
|
||||||
|
this, &SftpFileSystemModel::handleFileInfo);
|
||||||
|
connect(d->sftpChannel.data(), &SftpChannel::finished,
|
||||||
|
this, &SftpFileSystemModel::handleSftpJobFinished);
|
||||||
|
statRootDirectory();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SftpFileSystemModel::handleSshConnectionEstablished()
|
||||||
|
{
|
||||||
|
d->sftpChannel = d->sshConnection->createSftpChannel();
|
||||||
|
connect(d->sftpChannel.data(), &SftpChannel::initialized,
|
||||||
|
this, &SftpFileSystemModel::handleSftpChannelInitialized);
|
||||||
|
connect(d->sftpChannel.data(), &SftpChannel::channelError,
|
||||||
|
this, &SftpFileSystemModel::handleSftpChannelError);
|
||||||
|
d->sftpChannel->initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SftpFileSystemModel::handleSftpChannelError(const QString &reason)
|
||||||
|
{
|
||||||
|
emit connectionError(reason);
|
||||||
|
beginResetModel();
|
||||||
|
shutDown();
|
||||||
|
endResetModel();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SftpFileSystemModel::handleFileInfo(SftpJobId jobId, const QList<SftpFileInfo> &fileInfoList)
|
||||||
|
{
|
||||||
|
if (jobId == d->statJobId) {
|
||||||
|
QSSH_ASSERT_AND_RETURN(!d->rootNode);
|
||||||
|
beginInsertRows(QModelIndex(), 0, 0);
|
||||||
|
d->rootNode = new SftpDirNode;
|
||||||
|
d->rootNode->path = d->rootDirectory;
|
||||||
|
d->rootNode->fileInfo = fileInfoList.first();
|
||||||
|
d->rootNode->fileInfo.name = d->rootDirectory == QLatin1String("/")
|
||||||
|
? d->rootDirectory : QFileInfo(d->rootDirectory).fileName();
|
||||||
|
endInsertRows();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
SftpDirNode * const parentNode = d->lsOps.value(jobId);
|
||||||
|
QSSH_ASSERT_AND_RETURN(parentNode);
|
||||||
|
QList<SftpFileInfo> filteredList;
|
||||||
|
foreach (const SftpFileInfo &fi, fileInfoList) {
|
||||||
|
if (fi.name != QLatin1String(".") && fi.name != QLatin1String(".."))
|
||||||
|
filteredList << fi;
|
||||||
|
}
|
||||||
|
if (filteredList.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
// In theory beginInsertRows() should suffice, but that fails to have an effect
|
||||||
|
// if rowCount() returned 0 earlier.
|
||||||
|
emit layoutAboutToBeChanged();
|
||||||
|
|
||||||
|
foreach (const SftpFileInfo &fileInfo, filteredList) {
|
||||||
|
SftpFileNode *childNode;
|
||||||
|
if (fileInfo.type == FileTypeDirectory)
|
||||||
|
childNode = new SftpDirNode;
|
||||||
|
else
|
||||||
|
childNode = new SftpFileNode;
|
||||||
|
childNode->path = parentNode->path;
|
||||||
|
if (!childNode->path.endsWith(QLatin1Char('/')))
|
||||||
|
childNode->path += QLatin1Char('/');
|
||||||
|
childNode->path += fileInfo.name;
|
||||||
|
childNode->fileInfo = fileInfo;
|
||||||
|
childNode->parent = parentNode;
|
||||||
|
parentNode->children << childNode;
|
||||||
|
}
|
||||||
|
emit layoutChanged(); // Should be endInsertRows(), see above.
|
||||||
|
}
|
||||||
|
|
||||||
|
void SftpFileSystemModel::handleSftpJobFinished(SftpJobId jobId, const QString &errorMessage)
|
||||||
|
{
|
||||||
|
if (jobId == d->statJobId) {
|
||||||
|
d->statJobId = SftpInvalidJob;
|
||||||
|
if (!errorMessage.isEmpty())
|
||||||
|
emit sftpOperationFailed(tr("Error getting \"stat\" info about \"%1\": %2")
|
||||||
|
.arg(rootDirectory(), errorMessage));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DirNodeHash::Iterator it = d->lsOps.find(jobId);
|
||||||
|
if (it != d->lsOps.end()) {
|
||||||
|
QSSH_ASSERT(it.value()->lsState == SftpDirNode::LsRunning);
|
||||||
|
it.value()->lsState = SftpDirNode::LsFinished;
|
||||||
|
if (!errorMessage.isEmpty())
|
||||||
|
emit sftpOperationFailed(tr("Error listing contents of directory \"%1\": %2")
|
||||||
|
.arg(it.value()->path, errorMessage));
|
||||||
|
d->lsOps.erase(it);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int jobIndex = d->externalJobs.indexOf(jobId);
|
||||||
|
QSSH_ASSERT_AND_RETURN(jobIndex != -1);
|
||||||
|
d->externalJobs.removeAt(jobIndex);
|
||||||
|
emit sftpOperationFinished(jobId, errorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace QSsh
|
||||||
100
client/3rd/QtSsh/src/ssh/sftpfilesystemmodel.h
Normal file
100
client/3rd/QtSsh/src/ssh/sftpfilesystemmodel.h
Normal file
|
|
@ -0,0 +1,100 @@
|
||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2016 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "sftpdefs.h"
|
||||||
|
|
||||||
|
#include "ssh_global.h"
|
||||||
|
|
||||||
|
#include <QAbstractItemModel>
|
||||||
|
|
||||||
|
namespace QSsh {
|
||||||
|
class SshConnectionParameters;
|
||||||
|
|
||||||
|
namespace Internal { class SftpFileSystemModelPrivate; }
|
||||||
|
|
||||||
|
// Very simple read-only model. Symbolic links are not followed.
|
||||||
|
class QSSH_EXPORT SftpFileSystemModel : public QAbstractItemModel
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit SftpFileSystemModel(QObject *parent = 0);
|
||||||
|
~SftpFileSystemModel();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Once this is called, an SFTP connection is established and the model is populated.
|
||||||
|
* The effect of additional calls is undefined.
|
||||||
|
*/
|
||||||
|
void setSshConnection(const SshConnectionParameters &sshParams);
|
||||||
|
|
||||||
|
void setRootDirectory(const QString &path); // Default is "/".
|
||||||
|
QString rootDirectory() const;
|
||||||
|
|
||||||
|
SftpJobId downloadFile(const QModelIndex &index, const QString &targetFilePath);
|
||||||
|
|
||||||
|
// Use this to get the full path of a file or directory.
|
||||||
|
static const int PathRole = Qt::UserRole;
|
||||||
|
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
/*
|
||||||
|
* E.g. "Permission denied". Note that this can happen without direct user intervention,
|
||||||
|
* due to e.g. the view calling rowCount() on a non-readable directory. This signal should
|
||||||
|
* therefore not result in a message box or similar, since it might occur very often.
|
||||||
|
*/
|
||||||
|
void sftpOperationFailed(const QString &errorMessage);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This error is not recoverable. The model will not have any content after
|
||||||
|
* the signal has been emitted.
|
||||||
|
*/
|
||||||
|
void connectionError(const QString &errorMessage);
|
||||||
|
|
||||||
|
// Success <=> error.isEmpty().
|
||||||
|
void sftpOperationFinished(QSsh::SftpJobId, const QString &error);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void handleSshConnectionEstablished();
|
||||||
|
void handleSshConnectionFailure();
|
||||||
|
void handleSftpChannelInitialized();
|
||||||
|
void handleSftpChannelError(const QString &reason);
|
||||||
|
void handleFileInfo(QSsh::SftpJobId jobId, const QList<QSsh::SftpFileInfo> &fileInfoList);
|
||||||
|
void handleSftpJobFinished(QSsh::SftpJobId jobId, const QString &errorMessage);
|
||||||
|
|
||||||
|
int columnCount(const QModelIndex &parent = QModelIndex()) const;
|
||||||
|
Qt::ItemFlags flags(const QModelIndex &index) const;
|
||||||
|
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
|
||||||
|
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const;
|
||||||
|
QModelIndex parent(const QModelIndex &child) const;
|
||||||
|
int rowCount(const QModelIndex &parent = QModelIndex()) const;
|
||||||
|
|
||||||
|
void statRootDirectory();
|
||||||
|
void shutDown();
|
||||||
|
|
||||||
|
Internal::SftpFileSystemModelPrivate * const d;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace QSsh;
|
||||||
217
client/3rd/QtSsh/src/ssh/sftpincomingpacket.cpp
Normal file
217
client/3rd/QtSsh/src/ssh/sftpincomingpacket.cpp
Normal file
|
|
@ -0,0 +1,217 @@
|
||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2016 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#include "sftpincomingpacket_p.h"
|
||||||
|
|
||||||
|
#include "sshexception_p.h"
|
||||||
|
#include "sshlogging_p.h"
|
||||||
|
#include "sshpacketparser_p.h"
|
||||||
|
|
||||||
|
namespace QSsh {
|
||||||
|
namespace Internal {
|
||||||
|
|
||||||
|
SftpIncomingPacket::SftpIncomingPacket() : m_length(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void SftpIncomingPacket::consumeData(QByteArray &newData)
|
||||||
|
{
|
||||||
|
qCDebug(sshLog, "%s: current data size = %d, new data size = %d", Q_FUNC_INFO,
|
||||||
|
m_data.size(), newData.size());
|
||||||
|
|
||||||
|
if (isComplete() || dataSize() + newData.size() < sizeof m_length)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (dataSize() < sizeof m_length) {
|
||||||
|
moveFirstBytes(m_data, newData, sizeof m_length - m_data.size());
|
||||||
|
m_length = SshPacketParser::asUint32(m_data, static_cast<quint32>(0));
|
||||||
|
if (m_length < static_cast<quint32>(TypeOffset + 1)
|
||||||
|
|| m_length > MaxPacketSize) {
|
||||||
|
throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
|
||||||
|
"Invalid length field in SFTP packet.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
moveFirstBytes(m_data, newData,
|
||||||
|
qMin<quint32>(m_length - dataSize() + 4, newData.size()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SftpIncomingPacket::moveFirstBytes(QByteArray &target, QByteArray &source,
|
||||||
|
int n)
|
||||||
|
{
|
||||||
|
target.append(source.left(n));
|
||||||
|
source.remove(0, n);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SftpIncomingPacket::isComplete() const
|
||||||
|
{
|
||||||
|
return m_length == dataSize() - 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SftpIncomingPacket::clear()
|
||||||
|
{
|
||||||
|
m_data.clear();
|
||||||
|
m_length = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
quint32 SftpIncomingPacket::extractServerVersion() const
|
||||||
|
{
|
||||||
|
Q_ASSERT(isComplete());
|
||||||
|
Q_ASSERT(type() == SSH_FXP_VERSION);
|
||||||
|
try {
|
||||||
|
return SshPacketParser::asUint32(m_data, TypeOffset + 1);
|
||||||
|
} catch (const SshPacketParseException &) {
|
||||||
|
throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
|
||||||
|
"Invalid SSH_FXP_VERSION packet.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SftpHandleResponse SftpIncomingPacket::asHandleResponse() const
|
||||||
|
{
|
||||||
|
Q_ASSERT(isComplete());
|
||||||
|
Q_ASSERT(type() == SSH_FXP_HANDLE);
|
||||||
|
try {
|
||||||
|
SftpHandleResponse response;
|
||||||
|
quint32 offset = RequestIdOffset;
|
||||||
|
response.requestId = SshPacketParser::asUint32(m_data, &offset);
|
||||||
|
response.handle = SshPacketParser::asString(m_data, &offset);
|
||||||
|
return response;
|
||||||
|
} catch (const SshPacketParseException &) {
|
||||||
|
throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
|
||||||
|
"Invalid SSH_FXP_HANDLE packet");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SftpStatusResponse SftpIncomingPacket::asStatusResponse() const
|
||||||
|
{
|
||||||
|
Q_ASSERT(isComplete());
|
||||||
|
Q_ASSERT(type() == SSH_FXP_STATUS);
|
||||||
|
try {
|
||||||
|
SftpStatusResponse response;
|
||||||
|
quint32 offset = RequestIdOffset;
|
||||||
|
response.requestId = SshPacketParser::asUint32(m_data, &offset);
|
||||||
|
response.status = static_cast<SftpStatusCode>(SshPacketParser::asUint32(m_data, &offset));
|
||||||
|
response.errorString = SshPacketParser::asUserString(m_data, &offset);
|
||||||
|
response.language = SshPacketParser::asString(m_data, &offset);
|
||||||
|
return response;
|
||||||
|
} catch (const SshPacketParseException &) {
|
||||||
|
throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
|
||||||
|
"Invalid SSH_FXP_STATUS packet.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SftpNameResponse SftpIncomingPacket::asNameResponse() const
|
||||||
|
{
|
||||||
|
Q_ASSERT(isComplete());
|
||||||
|
Q_ASSERT(type() == SSH_FXP_NAME);
|
||||||
|
try {
|
||||||
|
SftpNameResponse response;
|
||||||
|
quint32 offset = RequestIdOffset;
|
||||||
|
response.requestId = SshPacketParser::asUint32(m_data, &offset);
|
||||||
|
const quint32 count = SshPacketParser::asUint32(m_data, &offset);
|
||||||
|
for (quint32 i = 0; i < count; ++i)
|
||||||
|
response.files << asFile(offset);
|
||||||
|
return response;
|
||||||
|
} catch (const SshPacketParseException &) {
|
||||||
|
throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
|
||||||
|
"Invalid SSH_FXP_NAME packet.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SftpDataResponse SftpIncomingPacket::asDataResponse() const
|
||||||
|
{
|
||||||
|
Q_ASSERT(isComplete());
|
||||||
|
Q_ASSERT(type() == SSH_FXP_DATA);
|
||||||
|
try {
|
||||||
|
SftpDataResponse response;
|
||||||
|
quint32 offset = RequestIdOffset;
|
||||||
|
response.requestId = SshPacketParser::asUint32(m_data, &offset);
|
||||||
|
response.data = SshPacketParser::asString(m_data, &offset);
|
||||||
|
return response;
|
||||||
|
} catch (const SshPacketParseException &) {
|
||||||
|
throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
|
||||||
|
"Invalid SSH_FXP_DATA packet.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SftpAttrsResponse SftpIncomingPacket::asAttrsResponse() const
|
||||||
|
{
|
||||||
|
Q_ASSERT(isComplete());
|
||||||
|
Q_ASSERT(type() == SSH_FXP_ATTRS);
|
||||||
|
try {
|
||||||
|
SftpAttrsResponse response;
|
||||||
|
quint32 offset = RequestIdOffset;
|
||||||
|
response.requestId = SshPacketParser::asUint32(m_data, &offset);
|
||||||
|
response.attrs = asFileAttributes(offset);
|
||||||
|
return response;
|
||||||
|
} catch (const SshPacketParseException &) {
|
||||||
|
throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
|
||||||
|
"Invalid SSH_FXP_ATTRS packet.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SftpFile SftpIncomingPacket::asFile(quint32 &offset) const
|
||||||
|
{
|
||||||
|
SftpFile file;
|
||||||
|
file.fileName
|
||||||
|
= QString::fromUtf8(SshPacketParser::asString(m_data, &offset));
|
||||||
|
file.longName
|
||||||
|
= QString::fromUtf8(SshPacketParser::asString(m_data, &offset));
|
||||||
|
file.attributes = asFileAttributes(offset);
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
|
||||||
|
SftpFileAttributes SftpIncomingPacket::asFileAttributes(quint32 &offset) const
|
||||||
|
{
|
||||||
|
SftpFileAttributes attributes;
|
||||||
|
const quint32 flags = SshPacketParser::asUint32(m_data, &offset);
|
||||||
|
attributes.sizePresent = flags & SSH_FILEXFER_ATTR_SIZE;
|
||||||
|
attributes.timesPresent = flags & SSH_FILEXFER_ATTR_ACMODTIME;
|
||||||
|
attributes.uidAndGidPresent = flags & SSH_FILEXFER_ATTR_UIDGID;
|
||||||
|
attributes.permissionsPresent = flags & SSH_FILEXFER_ATTR_PERMISSIONS;
|
||||||
|
if (attributes.sizePresent)
|
||||||
|
attributes.size = SshPacketParser::asUint64(m_data, &offset);
|
||||||
|
if (attributes.uidAndGidPresent) {
|
||||||
|
attributes.uid = SshPacketParser::asUint32(m_data, &offset);
|
||||||
|
attributes.gid = SshPacketParser::asUint32(m_data, &offset);
|
||||||
|
}
|
||||||
|
if (attributes.permissionsPresent)
|
||||||
|
attributes.permissions = SshPacketParser::asUint32(m_data, &offset);
|
||||||
|
if (attributes.timesPresent) {
|
||||||
|
attributes.atime = SshPacketParser::asUint32(m_data, &offset);
|
||||||
|
attributes.mtime = SshPacketParser::asUint32(m_data, &offset);
|
||||||
|
}
|
||||||
|
if (flags & SSH_FILEXFER_ATTR_EXTENDED) {
|
||||||
|
const quint32 count = SshPacketParser::asUint32(m_data, &offset);
|
||||||
|
for (quint32 i = 0; i < count; ++i) {
|
||||||
|
SshPacketParser::asString(m_data, &offset);
|
||||||
|
SshPacketParser::asString(m_data, &offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return attributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Internal
|
||||||
|
} // namespace QSsh
|
||||||
104
client/3rd/QtSsh/src/ssh/sftpincomingpacket_p.h
Normal file
104
client/3rd/QtSsh/src/ssh/sftpincomingpacket_p.h
Normal file
|
|
@ -0,0 +1,104 @@
|
||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2016 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "sftppacket_p.h"
|
||||||
|
|
||||||
|
namespace QSsh {
|
||||||
|
namespace Internal {
|
||||||
|
|
||||||
|
struct SftpHandleResponse {
|
||||||
|
quint32 requestId;
|
||||||
|
QByteArray handle;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SftpStatusResponse {
|
||||||
|
quint32 requestId;
|
||||||
|
SftpStatusCode status;
|
||||||
|
QString errorString;
|
||||||
|
QByteArray language;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SftpFileAttributes {
|
||||||
|
bool sizePresent;
|
||||||
|
bool timesPresent;
|
||||||
|
bool uidAndGidPresent;
|
||||||
|
bool permissionsPresent;
|
||||||
|
quint64 size;
|
||||||
|
quint32 uid;
|
||||||
|
quint32 gid;
|
||||||
|
quint32 permissions;
|
||||||
|
quint32 atime;
|
||||||
|
quint32 mtime;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SftpFile {
|
||||||
|
QString fileName;
|
||||||
|
QString longName; // Not present in later RFCs, so we don't expose this to the user.
|
||||||
|
SftpFileAttributes attributes;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SftpNameResponse {
|
||||||
|
quint32 requestId;
|
||||||
|
QList<SftpFile> files;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SftpDataResponse {
|
||||||
|
quint32 requestId;
|
||||||
|
QByteArray data;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SftpAttrsResponse {
|
||||||
|
quint32 requestId;
|
||||||
|
SftpFileAttributes attrs;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SftpIncomingPacket : public AbstractSftpPacket
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SftpIncomingPacket();
|
||||||
|
|
||||||
|
void consumeData(QByteArray &data);
|
||||||
|
void clear();
|
||||||
|
bool isComplete() const;
|
||||||
|
quint32 extractServerVersion() const;
|
||||||
|
SftpHandleResponse asHandleResponse() const;
|
||||||
|
SftpStatusResponse asStatusResponse() const;
|
||||||
|
SftpNameResponse asNameResponse() const;
|
||||||
|
SftpDataResponse asDataResponse() const;
|
||||||
|
SftpAttrsResponse asAttrsResponse() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void moveFirstBytes(QByteArray &target, QByteArray &source, int n);
|
||||||
|
|
||||||
|
SftpFileAttributes asFileAttributes(quint32 &offset) const;
|
||||||
|
SftpFile asFile(quint32 &offset) const;
|
||||||
|
|
||||||
|
quint32 m_length;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Internal
|
||||||
|
} // namespace QSsh
|
||||||
220
client/3rd/QtSsh/src/ssh/sftpoperation.cpp
Normal file
220
client/3rd/QtSsh/src/ssh/sftpoperation.cpp
Normal file
|
|
@ -0,0 +1,220 @@
|
||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2016 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#include "sftpoperation_p.h"
|
||||||
|
|
||||||
|
#include "sftpoutgoingpacket_p.h"
|
||||||
|
|
||||||
|
#include <QFile>
|
||||||
|
|
||||||
|
namespace QSsh {
|
||||||
|
namespace Internal {
|
||||||
|
|
||||||
|
AbstractSftpOperation::AbstractSftpOperation(SftpJobId jobId) : jobId(jobId)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
AbstractSftpOperation::~AbstractSftpOperation() { }
|
||||||
|
|
||||||
|
|
||||||
|
SftpStatFile::SftpStatFile(SftpJobId jobId, const QString &path)
|
||||||
|
: AbstractSftpOperation(jobId), path(path)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
SftpOutgoingPacket &SftpStatFile::initialPacket(SftpOutgoingPacket &packet)
|
||||||
|
{
|
||||||
|
return packet.generateStat(path, jobId);
|
||||||
|
}
|
||||||
|
|
||||||
|
SftpMakeDir::SftpMakeDir(SftpJobId jobId, const QString &path,
|
||||||
|
const SftpUploadDir::Ptr &parentJob)
|
||||||
|
: AbstractSftpOperation(jobId), parentJob(parentJob), remoteDir(path)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
SftpOutgoingPacket &SftpMakeDir::initialPacket(SftpOutgoingPacket &packet)
|
||||||
|
{
|
||||||
|
return packet.generateMkDir(remoteDir, jobId);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
SftpRmDir::SftpRmDir(SftpJobId id, const QString &path)
|
||||||
|
: AbstractSftpOperation(id), remoteDir(path)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
SftpOutgoingPacket &SftpRmDir::initialPacket(SftpOutgoingPacket &packet)
|
||||||
|
{
|
||||||
|
return packet.generateRmDir(remoteDir, jobId);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
SftpRm::SftpRm(SftpJobId jobId, const QString &path)
|
||||||
|
: AbstractSftpOperation(jobId), remoteFile(path) {}
|
||||||
|
|
||||||
|
SftpOutgoingPacket &SftpRm::initialPacket(SftpOutgoingPacket &packet)
|
||||||
|
{
|
||||||
|
return packet.generateRm(remoteFile, jobId);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
SftpRename::SftpRename(SftpJobId jobId, const QString &oldPath,
|
||||||
|
const QString &newPath)
|
||||||
|
: AbstractSftpOperation(jobId), oldPath(oldPath), newPath(newPath)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
SftpOutgoingPacket &SftpRename::initialPacket(SftpOutgoingPacket &packet)
|
||||||
|
{
|
||||||
|
return packet.generateRename(oldPath, newPath, jobId);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
SftpCreateLink::SftpCreateLink(SftpJobId jobId, const QString &filePath, const QString &target)
|
||||||
|
: AbstractSftpOperation(jobId), filePath(filePath), target(target)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
SftpOutgoingPacket &SftpCreateLink::initialPacket(SftpOutgoingPacket &packet)
|
||||||
|
{
|
||||||
|
return packet.generateCreateLink(filePath, target, jobId);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
AbstractSftpOperationWithHandle::AbstractSftpOperationWithHandle(SftpJobId jobId,
|
||||||
|
const QString &remotePath)
|
||||||
|
: AbstractSftpOperation(jobId),
|
||||||
|
remotePath(remotePath), state(Inactive), hasError(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
AbstractSftpOperationWithHandle::~AbstractSftpOperationWithHandle() { }
|
||||||
|
|
||||||
|
|
||||||
|
SftpListDir::SftpListDir(SftpJobId jobId, const QString &path)
|
||||||
|
: AbstractSftpOperationWithHandle(jobId, path)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
SftpOutgoingPacket &SftpListDir::initialPacket(SftpOutgoingPacket &packet)
|
||||||
|
{
|
||||||
|
state = OpenRequested;
|
||||||
|
return packet.generateOpenDir(remotePath, jobId);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
SftpCreateFile::SftpCreateFile(SftpJobId jobId, const QString &path,
|
||||||
|
SftpOverwriteMode mode)
|
||||||
|
: AbstractSftpOperationWithHandle(jobId, path), mode(mode)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
SftpOutgoingPacket & SftpCreateFile::initialPacket(SftpOutgoingPacket &packet)
|
||||||
|
{
|
||||||
|
state = OpenRequested;
|
||||||
|
return packet.generateOpenFileForWriting(remotePath, mode,
|
||||||
|
SftpOutgoingPacket::DefaultPermissions, jobId);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const int AbstractSftpTransfer::MaxInFlightCount = 10; // Experimentally found to be enough.
|
||||||
|
|
||||||
|
AbstractSftpTransfer::AbstractSftpTransfer(SftpJobId jobId, const QString &remotePath,
|
||||||
|
const QSharedPointer<QFile> &localFile)
|
||||||
|
: AbstractSftpOperationWithHandle(jobId, remotePath),
|
||||||
|
localFile(localFile), fileSize(0), offset(0), inFlightCount(0),
|
||||||
|
statRequested(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
AbstractSftpTransfer::~AbstractSftpTransfer() {}
|
||||||
|
|
||||||
|
void AbstractSftpTransfer::calculateInFlightCount(quint32 chunkSize)
|
||||||
|
{
|
||||||
|
if (fileSize == 0) {
|
||||||
|
inFlightCount = 1;
|
||||||
|
} else {
|
||||||
|
inFlightCount = fileSize / chunkSize;
|
||||||
|
if (fileSize % chunkSize)
|
||||||
|
++inFlightCount;
|
||||||
|
if (inFlightCount > MaxInFlightCount)
|
||||||
|
inFlightCount = MaxInFlightCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
SftpDownload::SftpDownload(SftpJobId jobId, const QString &remotePath,
|
||||||
|
const QSharedPointer<QFile> &localFile)
|
||||||
|
: AbstractSftpTransfer(jobId, remotePath, localFile), eofId(SftpInvalidJob)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
SftpOutgoingPacket &SftpDownload::initialPacket(SftpOutgoingPacket &packet)
|
||||||
|
{
|
||||||
|
state = OpenRequested;
|
||||||
|
return packet.generateOpenFileForReading(remotePath, jobId);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
SftpUploadFile::SftpUploadFile(SftpJobId jobId, const QString &remotePath,
|
||||||
|
const QSharedPointer<QFile> &localFile, SftpOverwriteMode mode,
|
||||||
|
const SftpUploadDir::Ptr &parentJob)
|
||||||
|
: AbstractSftpTransfer(jobId, remotePath, localFile),
|
||||||
|
parentJob(parentJob), mode(mode)
|
||||||
|
{
|
||||||
|
fileSize = localFile->size();
|
||||||
|
}
|
||||||
|
|
||||||
|
SftpOutgoingPacket &SftpUploadFile::initialPacket(SftpOutgoingPacket &packet)
|
||||||
|
{
|
||||||
|
state = OpenRequested;
|
||||||
|
quint32 permissions = 0;
|
||||||
|
const QFile::Permissions &qtPermissions = localFile->permissions();
|
||||||
|
if (qtPermissions & QFile::ExeOther)
|
||||||
|
permissions |= 1 << 0;
|
||||||
|
if (qtPermissions & QFile::WriteOther)
|
||||||
|
permissions |= 1 << 1;
|
||||||
|
if (qtPermissions & QFile::ReadOther)
|
||||||
|
permissions |= 1 << 2;
|
||||||
|
if (qtPermissions & QFile::ExeGroup)
|
||||||
|
permissions |= 1<< 3;
|
||||||
|
if (qtPermissions & QFile::WriteGroup)
|
||||||
|
permissions |= 1<< 4;
|
||||||
|
if (qtPermissions & QFile::ReadGroup)
|
||||||
|
permissions |= 1<< 5;
|
||||||
|
if (qtPermissions & QFile::ExeOwner)
|
||||||
|
permissions |= 1<< 6;
|
||||||
|
if (qtPermissions & QFile::WriteOwner)
|
||||||
|
permissions |= 1<< 7;
|
||||||
|
if (qtPermissions & QFile::ReadOwner)
|
||||||
|
permissions |= 1<< 8;
|
||||||
|
return packet.generateOpenFileForWriting(remotePath, mode, permissions, jobId);
|
||||||
|
}
|
||||||
|
|
||||||
|
SftpUploadDir::~SftpUploadDir() {}
|
||||||
|
|
||||||
|
} // namespace Internal
|
||||||
|
} // namespace QSsh
|
||||||
244
client/3rd/QtSsh/src/ssh/sftpoperation_p.h
Normal file
244
client/3rd/QtSsh/src/ssh/sftpoperation_p.h
Normal file
|
|
@ -0,0 +1,244 @@
|
||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2016 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "sftpdefs.h"
|
||||||
|
|
||||||
|
#include <QByteArray>
|
||||||
|
#include <QList>
|
||||||
|
#include <QMap>
|
||||||
|
#include <QSharedPointer>
|
||||||
|
|
||||||
|
QT_BEGIN_NAMESPACE
|
||||||
|
class QFile;
|
||||||
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
|
namespace QSsh {
|
||||||
|
namespace Internal {
|
||||||
|
|
||||||
|
class SftpOutgoingPacket;
|
||||||
|
|
||||||
|
struct AbstractSftpOperation
|
||||||
|
{
|
||||||
|
typedef QSharedPointer<AbstractSftpOperation> Ptr;
|
||||||
|
enum Type {
|
||||||
|
StatFile, ListDir, MakeDir, RmDir, Rm, Rename, CreateLink, CreateFile, Download, UploadFile
|
||||||
|
};
|
||||||
|
|
||||||
|
AbstractSftpOperation(SftpJobId jobId);
|
||||||
|
virtual ~AbstractSftpOperation();
|
||||||
|
virtual Type type() const = 0;
|
||||||
|
virtual SftpOutgoingPacket &initialPacket(SftpOutgoingPacket &packet) = 0;
|
||||||
|
|
||||||
|
const SftpJobId jobId;
|
||||||
|
|
||||||
|
private:
|
||||||
|
AbstractSftpOperation(const AbstractSftpOperation &);
|
||||||
|
AbstractSftpOperation &operator=(const AbstractSftpOperation &);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SftpUploadDir;
|
||||||
|
|
||||||
|
struct SftpStatFile : public AbstractSftpOperation
|
||||||
|
{
|
||||||
|
typedef QSharedPointer<SftpStatFile> Ptr;
|
||||||
|
|
||||||
|
SftpStatFile(SftpJobId jobId, const QString &path);
|
||||||
|
virtual Type type() const { return StatFile; }
|
||||||
|
virtual SftpOutgoingPacket &initialPacket(SftpOutgoingPacket &packet);
|
||||||
|
|
||||||
|
const QString path;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SftpMakeDir : public AbstractSftpOperation
|
||||||
|
{
|
||||||
|
typedef QSharedPointer<SftpMakeDir> Ptr;
|
||||||
|
|
||||||
|
SftpMakeDir(SftpJobId jobId, const QString &path,
|
||||||
|
const QSharedPointer<SftpUploadDir> &parentJob = QSharedPointer<SftpUploadDir>());
|
||||||
|
virtual Type type() const { return MakeDir; }
|
||||||
|
virtual SftpOutgoingPacket &initialPacket(SftpOutgoingPacket &packet);
|
||||||
|
|
||||||
|
const QSharedPointer<SftpUploadDir> parentJob;
|
||||||
|
const QString remoteDir;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SftpRmDir : public AbstractSftpOperation
|
||||||
|
{
|
||||||
|
typedef QSharedPointer<SftpRmDir> Ptr;
|
||||||
|
|
||||||
|
SftpRmDir(SftpJobId id, const QString &path);
|
||||||
|
virtual Type type() const { return RmDir; }
|
||||||
|
virtual SftpOutgoingPacket &initialPacket(SftpOutgoingPacket &packet);
|
||||||
|
|
||||||
|
const QString remoteDir;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SftpRm : public AbstractSftpOperation
|
||||||
|
{
|
||||||
|
typedef QSharedPointer<SftpRm> Ptr;
|
||||||
|
|
||||||
|
SftpRm(SftpJobId jobId, const QString &path);
|
||||||
|
virtual Type type() const { return Rm; }
|
||||||
|
virtual SftpOutgoingPacket &initialPacket(SftpOutgoingPacket &packet);
|
||||||
|
|
||||||
|
const QString remoteFile;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SftpRename : public AbstractSftpOperation
|
||||||
|
{
|
||||||
|
typedef QSharedPointer<SftpRename> Ptr;
|
||||||
|
|
||||||
|
SftpRename(SftpJobId jobId, const QString &oldPath, const QString &newPath);
|
||||||
|
virtual Type type() const { return Rename; }
|
||||||
|
virtual SftpOutgoingPacket &initialPacket(SftpOutgoingPacket &packet);
|
||||||
|
|
||||||
|
const QString oldPath;
|
||||||
|
const QString newPath;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SftpCreateLink : public AbstractSftpOperation
|
||||||
|
{
|
||||||
|
typedef QSharedPointer<SftpCreateLink> Ptr;
|
||||||
|
|
||||||
|
SftpCreateLink(SftpJobId jobId, const QString &filePath, const QString &target);
|
||||||
|
virtual Type type() const { return CreateLink; }
|
||||||
|
virtual SftpOutgoingPacket &initialPacket(SftpOutgoingPacket &packet);
|
||||||
|
|
||||||
|
const QString filePath;
|
||||||
|
const QString target;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct AbstractSftpOperationWithHandle : public AbstractSftpOperation
|
||||||
|
{
|
||||||
|
typedef QSharedPointer<AbstractSftpOperationWithHandle> Ptr;
|
||||||
|
enum State { Inactive, OpenRequested, Open, CloseRequested };
|
||||||
|
|
||||||
|
AbstractSftpOperationWithHandle(SftpJobId jobId, const QString &remotePath);
|
||||||
|
~AbstractSftpOperationWithHandle();
|
||||||
|
|
||||||
|
const QString remotePath;
|
||||||
|
QByteArray remoteHandle;
|
||||||
|
State state;
|
||||||
|
bool hasError;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct SftpListDir : public AbstractSftpOperationWithHandle
|
||||||
|
{
|
||||||
|
typedef QSharedPointer<SftpListDir> Ptr;
|
||||||
|
|
||||||
|
SftpListDir(SftpJobId jobId, const QString &path);
|
||||||
|
virtual Type type() const { return ListDir; }
|
||||||
|
virtual SftpOutgoingPacket &initialPacket(SftpOutgoingPacket &packet);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct SftpCreateFile : public AbstractSftpOperationWithHandle
|
||||||
|
{
|
||||||
|
typedef QSharedPointer<SftpCreateFile> Ptr;
|
||||||
|
|
||||||
|
SftpCreateFile(SftpJobId jobId, const QString &path, SftpOverwriteMode mode);
|
||||||
|
virtual Type type() const { return CreateFile; }
|
||||||
|
virtual SftpOutgoingPacket &initialPacket(SftpOutgoingPacket &packet);
|
||||||
|
|
||||||
|
const SftpOverwriteMode mode;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AbstractSftpTransfer : public AbstractSftpOperationWithHandle
|
||||||
|
{
|
||||||
|
typedef QSharedPointer<AbstractSftpTransfer> Ptr;
|
||||||
|
|
||||||
|
AbstractSftpTransfer(SftpJobId jobId, const QString &remotePath,
|
||||||
|
const QSharedPointer<QFile> &localFile);
|
||||||
|
~AbstractSftpTransfer();
|
||||||
|
void calculateInFlightCount(quint32 chunkSize);
|
||||||
|
|
||||||
|
static const int MaxInFlightCount;
|
||||||
|
|
||||||
|
const QSharedPointer<QFile> localFile;
|
||||||
|
quint64 fileSize;
|
||||||
|
quint64 offset;
|
||||||
|
int inFlightCount;
|
||||||
|
bool statRequested;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SftpDownload : public AbstractSftpTransfer
|
||||||
|
{
|
||||||
|
typedef QSharedPointer<SftpDownload> Ptr;
|
||||||
|
SftpDownload(SftpJobId jobId, const QString &remotePath,
|
||||||
|
const QSharedPointer<QFile> &localFile);
|
||||||
|
virtual Type type() const { return Download; }
|
||||||
|
virtual SftpOutgoingPacket &initialPacket(SftpOutgoingPacket &packet);
|
||||||
|
|
||||||
|
QMap<quint32, quint64> offsets;
|
||||||
|
SftpJobId eofId;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SftpUploadFile : public AbstractSftpTransfer
|
||||||
|
{
|
||||||
|
typedef QSharedPointer<SftpUploadFile> Ptr;
|
||||||
|
|
||||||
|
SftpUploadFile(SftpJobId jobId, const QString &remotePath,
|
||||||
|
const QSharedPointer<QFile> &localFile, SftpOverwriteMode mode,
|
||||||
|
const QSharedPointer<SftpUploadDir> &parentJob = QSharedPointer<SftpUploadDir>());
|
||||||
|
virtual Type type() const { return UploadFile; }
|
||||||
|
virtual SftpOutgoingPacket &initialPacket(SftpOutgoingPacket &packet);
|
||||||
|
|
||||||
|
const QSharedPointer<SftpUploadDir> parentJob;
|
||||||
|
SftpOverwriteMode mode;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Composite operation.
|
||||||
|
struct SftpUploadDir
|
||||||
|
{
|
||||||
|
typedef QSharedPointer<SftpUploadDir> Ptr;
|
||||||
|
|
||||||
|
struct Dir {
|
||||||
|
Dir(const QString &l, const QString &r) : localDir(l), remoteDir(r) {}
|
||||||
|
QString localDir;
|
||||||
|
QString remoteDir;
|
||||||
|
};
|
||||||
|
|
||||||
|
SftpUploadDir(SftpJobId jobId) : jobId(jobId), hasError(false) {}
|
||||||
|
~SftpUploadDir();
|
||||||
|
|
||||||
|
void setError()
|
||||||
|
{
|
||||||
|
hasError = true;
|
||||||
|
uploadsInProgress.clear();
|
||||||
|
mkdirsInProgress.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
const SftpJobId jobId;
|
||||||
|
bool hasError;
|
||||||
|
QList<SftpUploadFile::Ptr> uploadsInProgress;
|
||||||
|
QMap<SftpMakeDir::Ptr, Dir> mkdirsInProgress;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Internal
|
||||||
|
} // namespace QSsh
|
||||||
220
client/3rd/QtSsh/src/ssh/sftpoutgoingpacket.cpp
Normal file
220
client/3rd/QtSsh/src/ssh/sftpoutgoingpacket.cpp
Normal file
|
|
@ -0,0 +1,220 @@
|
||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2016 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#include "sftpoutgoingpacket_p.h"
|
||||||
|
|
||||||
|
#include "sshlogging_p.h"
|
||||||
|
#include "sshpacket_p.h"
|
||||||
|
|
||||||
|
#include <QtEndian>
|
||||||
|
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
|
namespace QSsh {
|
||||||
|
namespace Internal {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
const quint32 DefaultAttributes = 0;
|
||||||
|
const quint32 SSH_FXF_READ = 0x00000001;
|
||||||
|
const quint32 SSH_FXF_WRITE = 0x00000002;
|
||||||
|
const quint32 SSH_FXF_APPEND = 0x00000004;
|
||||||
|
const quint32 SSH_FXF_CREAT = 0x00000008;
|
||||||
|
const quint32 SSH_FXF_TRUNC = 0x00000010;
|
||||||
|
const quint32 SSH_FXF_EXCL = 0x00000020;
|
||||||
|
}
|
||||||
|
|
||||||
|
SftpOutgoingPacket::SftpOutgoingPacket()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
SftpOutgoingPacket &SftpOutgoingPacket::generateInit(quint32 version)
|
||||||
|
{
|
||||||
|
return init(SSH_FXP_INIT, 0).appendInt(version).finalize();
|
||||||
|
}
|
||||||
|
|
||||||
|
SftpOutgoingPacket &SftpOutgoingPacket::generateStat(const QString &path, quint32 requestId)
|
||||||
|
{
|
||||||
|
return init(SSH_FXP_LSTAT, requestId).appendString(path).finalize();
|
||||||
|
}
|
||||||
|
|
||||||
|
SftpOutgoingPacket &SftpOutgoingPacket::generateOpenDir(const QString &path,
|
||||||
|
quint32 requestId)
|
||||||
|
{
|
||||||
|
return init(SSH_FXP_OPENDIR, requestId).appendString(path).finalize();
|
||||||
|
}
|
||||||
|
|
||||||
|
SftpOutgoingPacket &SftpOutgoingPacket::generateReadDir(const QByteArray &handle,
|
||||||
|
quint32 requestId)
|
||||||
|
{
|
||||||
|
return init(SSH_FXP_READDIR, requestId).appendString(handle).finalize();
|
||||||
|
}
|
||||||
|
|
||||||
|
SftpOutgoingPacket &SftpOutgoingPacket::generateCloseHandle(const QByteArray &handle,
|
||||||
|
quint32 requestId)
|
||||||
|
{
|
||||||
|
return init(SSH_FXP_CLOSE, requestId).appendString(handle).finalize();
|
||||||
|
}
|
||||||
|
|
||||||
|
SftpOutgoingPacket &SftpOutgoingPacket::generateMkDir(const QString &path,
|
||||||
|
quint32 requestId)
|
||||||
|
{
|
||||||
|
return init(SSH_FXP_MKDIR, requestId).appendString(path)
|
||||||
|
.appendInt(DefaultAttributes).finalize();
|
||||||
|
}
|
||||||
|
|
||||||
|
SftpOutgoingPacket &SftpOutgoingPacket::generateRmDir(const QString &path,
|
||||||
|
quint32 requestId)
|
||||||
|
{
|
||||||
|
return init(SSH_FXP_RMDIR, requestId).appendString(path).finalize();
|
||||||
|
}
|
||||||
|
|
||||||
|
SftpOutgoingPacket &SftpOutgoingPacket::generateRm(const QString &path,
|
||||||
|
quint32 requestId)
|
||||||
|
{
|
||||||
|
return init(SSH_FXP_REMOVE, requestId).appendString(path).finalize();
|
||||||
|
}
|
||||||
|
|
||||||
|
SftpOutgoingPacket &SftpOutgoingPacket::generateRename(const QString &oldPath,
|
||||||
|
const QString &newPath, quint32 requestId)
|
||||||
|
{
|
||||||
|
return init(SSH_FXP_RENAME, requestId).appendString(oldPath)
|
||||||
|
.appendString(newPath).finalize();
|
||||||
|
}
|
||||||
|
|
||||||
|
SftpOutgoingPacket &SftpOutgoingPacket::generateOpenFileForWriting(const QString &path,
|
||||||
|
SftpOverwriteMode mode, quint32 permissions, quint32 requestId)
|
||||||
|
{
|
||||||
|
QList<quint32> attributes;
|
||||||
|
if (permissions != DefaultPermissions)
|
||||||
|
attributes << SSH_FILEXFER_ATTR_PERMISSIONS << permissions;
|
||||||
|
else
|
||||||
|
attributes << DefaultAttributes;
|
||||||
|
return generateOpenFile(path, Write, mode, attributes, requestId);
|
||||||
|
}
|
||||||
|
|
||||||
|
SftpOutgoingPacket &SftpOutgoingPacket::generateOpenFileForReading(const QString &path,
|
||||||
|
quint32 requestId)
|
||||||
|
{
|
||||||
|
// Note: Overwrite mode is irrelevant and will be ignored.
|
||||||
|
return generateOpenFile(path, Read, SftpSkipExisting, QList<quint32>() << DefaultAttributes,
|
||||||
|
requestId);
|
||||||
|
}
|
||||||
|
|
||||||
|
SftpOutgoingPacket &SftpOutgoingPacket::generateReadFile(const QByteArray &handle,
|
||||||
|
quint64 offset, quint32 length, quint32 requestId)
|
||||||
|
{
|
||||||
|
return init(SSH_FXP_READ, requestId).appendString(handle).appendInt64(offset)
|
||||||
|
.appendInt(length).finalize();
|
||||||
|
}
|
||||||
|
|
||||||
|
SftpOutgoingPacket &SftpOutgoingPacket::generateFstat(const QByteArray &handle,
|
||||||
|
quint32 requestId)
|
||||||
|
{
|
||||||
|
return init(SSH_FXP_FSTAT, requestId).appendString(handle).finalize();
|
||||||
|
}
|
||||||
|
|
||||||
|
SftpOutgoingPacket &SftpOutgoingPacket::generateWriteFile(const QByteArray &handle,
|
||||||
|
quint64 offset, const QByteArray &data, quint32 requestId)
|
||||||
|
{
|
||||||
|
return init(SSH_FXP_WRITE, requestId).appendString(handle)
|
||||||
|
.appendInt64(offset).appendString(data).finalize();
|
||||||
|
}
|
||||||
|
|
||||||
|
SftpOutgoingPacket &SftpOutgoingPacket::generateCreateLink(const QString &filePath,
|
||||||
|
const QString &target, quint32 requestId)
|
||||||
|
{
|
||||||
|
return init(SSH_FXP_SYMLINK, requestId).appendString(filePath).appendString(target).finalize();
|
||||||
|
}
|
||||||
|
|
||||||
|
SftpOutgoingPacket &SftpOutgoingPacket::generateOpenFile(const QString &path,
|
||||||
|
OpenType openType, SftpOverwriteMode mode, const QList<quint32> &attributes, quint32 requestId)
|
||||||
|
{
|
||||||
|
quint32 pFlags = 0;
|
||||||
|
switch (openType) {
|
||||||
|
case Read:
|
||||||
|
pFlags = SSH_FXF_READ;
|
||||||
|
break;
|
||||||
|
case Write:
|
||||||
|
pFlags = SSH_FXF_WRITE | SSH_FXF_CREAT;
|
||||||
|
switch (mode) {
|
||||||
|
case SftpOverwriteExisting: pFlags |= SSH_FXF_TRUNC; break;
|
||||||
|
case SftpAppendToExisting: pFlags |= SSH_FXF_APPEND; break;
|
||||||
|
case SftpSkipExisting: pFlags |= SSH_FXF_EXCL; break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
init(SSH_FXP_OPEN, requestId).appendString(path).appendInt(pFlags);
|
||||||
|
foreach (const quint32 attribute, attributes)
|
||||||
|
appendInt(attribute);
|
||||||
|
return finalize();
|
||||||
|
}
|
||||||
|
|
||||||
|
SftpOutgoingPacket &SftpOutgoingPacket::init(SftpPacketType type,
|
||||||
|
quint32 requestId)
|
||||||
|
{
|
||||||
|
m_data.resize(TypeOffset + 1);
|
||||||
|
m_data[TypeOffset] = type;
|
||||||
|
if (type != SSH_FXP_INIT) {
|
||||||
|
appendInt(requestId);
|
||||||
|
qCDebug(sshLog, "Generating SFTP packet of type %d with request id %u", type, requestId);
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
SftpOutgoingPacket &SftpOutgoingPacket::appendInt(quint32 val)
|
||||||
|
{
|
||||||
|
m_data.append(AbstractSshPacket::encodeInt(val));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
SftpOutgoingPacket &SftpOutgoingPacket::appendInt64(quint64 value)
|
||||||
|
{
|
||||||
|
m_data.append(AbstractSshPacket::encodeInt(value));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
SftpOutgoingPacket &SftpOutgoingPacket::appendString(const QString &string)
|
||||||
|
{
|
||||||
|
m_data.append(AbstractSshPacket::encodeString(string.toUtf8()));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
SftpOutgoingPacket &SftpOutgoingPacket::appendString(const QByteArray &string)
|
||||||
|
{
|
||||||
|
m_data += AbstractSshPacket::encodeString(string);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
SftpOutgoingPacket &SftpOutgoingPacket::finalize()
|
||||||
|
{
|
||||||
|
AbstractSshPacket::setLengthField(m_data);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
const quint32 SftpOutgoingPacket::DefaultPermissions = std::numeric_limits<quint32>::max();
|
||||||
|
|
||||||
|
} // namespace Internal
|
||||||
|
} // namespace QSsh
|
||||||
84
client/3rd/QtSsh/src/ssh/sftpoutgoingpacket_p.h
Normal file
84
client/3rd/QtSsh/src/ssh/sftpoutgoingpacket_p.h
Normal file
|
|
@ -0,0 +1,84 @@
|
||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2016 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "sftppacket_p.h"
|
||||||
|
#include "sftpdefs.h"
|
||||||
|
|
||||||
|
namespace QSsh {
|
||||||
|
namespace Internal {
|
||||||
|
|
||||||
|
class SftpOutgoingPacket : public AbstractSftpPacket
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SftpOutgoingPacket();
|
||||||
|
SftpOutgoingPacket &generateInit(quint32 version);
|
||||||
|
SftpOutgoingPacket &generateStat(const QString &path, quint32 requestId);
|
||||||
|
SftpOutgoingPacket &generateOpenDir(const QString &path, quint32 requestId);
|
||||||
|
SftpOutgoingPacket &generateReadDir(const QByteArray &handle,
|
||||||
|
quint32 requestId);
|
||||||
|
SftpOutgoingPacket &generateCloseHandle(const QByteArray &handle,
|
||||||
|
quint32 requestId);
|
||||||
|
SftpOutgoingPacket &generateMkDir(const QString &path, quint32 requestId);
|
||||||
|
SftpOutgoingPacket &generateRmDir(const QString &path, quint32 requestId);
|
||||||
|
SftpOutgoingPacket &generateRm(const QString &path, quint32 requestId);
|
||||||
|
SftpOutgoingPacket &generateRename(const QString &oldPath,
|
||||||
|
const QString &newPath, quint32 requestId);
|
||||||
|
SftpOutgoingPacket &generateOpenFileForWriting(const QString &path,
|
||||||
|
SftpOverwriteMode mode, quint32 permissions, quint32 requestId);
|
||||||
|
SftpOutgoingPacket &generateOpenFileForReading(const QString &path,
|
||||||
|
quint32 requestId);
|
||||||
|
SftpOutgoingPacket &generateReadFile(const QByteArray &handle,
|
||||||
|
quint64 offset, quint32 length, quint32 requestId);
|
||||||
|
SftpOutgoingPacket &generateFstat(const QByteArray &handle,
|
||||||
|
quint32 requestId);
|
||||||
|
SftpOutgoingPacket &generateWriteFile(const QByteArray &handle,
|
||||||
|
quint64 offset, const QByteArray &data, quint32 requestId);
|
||||||
|
|
||||||
|
// Note: OpenSSH's SFTP server has a bug that reverses the filePath and target
|
||||||
|
// arguments, so this operation is not portable.
|
||||||
|
SftpOutgoingPacket &generateCreateLink(const QString &filePath, const QString &target,
|
||||||
|
quint32 requestId);
|
||||||
|
|
||||||
|
static const quint32 DefaultPermissions;
|
||||||
|
|
||||||
|
private:
|
||||||
|
static QByteArray encodeString(const QString &string);
|
||||||
|
|
||||||
|
enum OpenType { Read, Write };
|
||||||
|
SftpOutgoingPacket &generateOpenFile(const QString &path, OpenType openType,
|
||||||
|
SftpOverwriteMode mode, const QList<quint32> &attributes, quint32 requestId);
|
||||||
|
|
||||||
|
SftpOutgoingPacket &init(SftpPacketType type, quint32 requestId);
|
||||||
|
SftpOutgoingPacket &appendInt(quint32 value);
|
||||||
|
SftpOutgoingPacket &appendInt64(quint64 value);
|
||||||
|
SftpOutgoingPacket &appendString(const QString &string);
|
||||||
|
SftpOutgoingPacket &appendString(const QByteArray &string);
|
||||||
|
SftpOutgoingPacket &finalize();
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Internal
|
||||||
|
} // namespace QSsh
|
||||||
49
client/3rd/QtSsh/src/ssh/sftppacket.cpp
Normal file
49
client/3rd/QtSsh/src/ssh/sftppacket.cpp
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2016 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#include "sftppacket_p.h"
|
||||||
|
|
||||||
|
#include "sshpacketparser_p.h"
|
||||||
|
|
||||||
|
namespace QSsh {
|
||||||
|
namespace Internal {
|
||||||
|
|
||||||
|
const quint32 AbstractSftpPacket::MaxDataSize = 32000;
|
||||||
|
const quint32 AbstractSftpPacket::MaxPacketSize = 34000;
|
||||||
|
const int AbstractSftpPacket::TypeOffset = 4;
|
||||||
|
const int AbstractSftpPacket::RequestIdOffset = TypeOffset + 1;
|
||||||
|
const int AbstractSftpPacket::PayloadOffset = RequestIdOffset + 4;
|
||||||
|
|
||||||
|
AbstractSftpPacket::AbstractSftpPacket()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
quint32 AbstractSftpPacket::requestId() const
|
||||||
|
{
|
||||||
|
return SshPacketParser::asUint32(m_data, RequestIdOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Internal
|
||||||
|
} // namespace QSsh
|
||||||
109
client/3rd/QtSsh/src/ssh/sftppacket_p.h
Normal file
109
client/3rd/QtSsh/src/ssh/sftppacket_p.h
Normal file
|
|
@ -0,0 +1,109 @@
|
||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2016 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QByteArray>
|
||||||
|
#include <QList>
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
namespace QSsh {
|
||||||
|
namespace Internal {
|
||||||
|
|
||||||
|
enum SftpPacketType {
|
||||||
|
SSH_FXP_INIT = 1,
|
||||||
|
SSH_FXP_VERSION = 2,
|
||||||
|
SSH_FXP_OPEN = 3,
|
||||||
|
SSH_FXP_CLOSE = 4,
|
||||||
|
SSH_FXP_READ = 5,
|
||||||
|
SSH_FXP_WRITE = 6,
|
||||||
|
SSH_FXP_LSTAT = 7,
|
||||||
|
SSH_FXP_FSTAT = 8,
|
||||||
|
SSH_FXP_SETSTAT = 9,
|
||||||
|
SSH_FXP_FSETSTAT = 10,
|
||||||
|
SSH_FXP_OPENDIR = 11,
|
||||||
|
SSH_FXP_READDIR = 12,
|
||||||
|
SSH_FXP_REMOVE = 13,
|
||||||
|
SSH_FXP_MKDIR = 14,
|
||||||
|
SSH_FXP_RMDIR = 15,
|
||||||
|
SSH_FXP_REALPATH = 16,
|
||||||
|
SSH_FXP_STAT = 17,
|
||||||
|
SSH_FXP_RENAME = 18,
|
||||||
|
SSH_FXP_READLINK = 19,
|
||||||
|
SSH_FXP_SYMLINK = 20, // Removed from later protocol versions. Try not to use.
|
||||||
|
|
||||||
|
SSH_FXP_STATUS = 101,
|
||||||
|
SSH_FXP_HANDLE = 102,
|
||||||
|
SSH_FXP_DATA = 103,
|
||||||
|
SSH_FXP_NAME = 104,
|
||||||
|
SSH_FXP_ATTRS = 105,
|
||||||
|
|
||||||
|
SSH_FXP_EXTENDED = 200,
|
||||||
|
SSH_FXP_EXTENDED_REPLY = 201
|
||||||
|
};
|
||||||
|
|
||||||
|
enum SftpStatusCode {
|
||||||
|
SSH_FX_OK = 0,
|
||||||
|
SSH_FX_EOF = 1,
|
||||||
|
SSH_FX_NO_SUCH_FILE = 2,
|
||||||
|
SSH_FX_PERMISSION_DENIED = 3,
|
||||||
|
SSH_FX_FAILURE = 4,
|
||||||
|
SSH_FX_BAD_MESSAGE = 5,
|
||||||
|
SSH_FX_NO_CONNECTION = 6,
|
||||||
|
SSH_FX_CONNECTION_LOST = 7,
|
||||||
|
SSH_FX_OP_UNSUPPORTED = 8
|
||||||
|
};
|
||||||
|
|
||||||
|
enum SftpAttributeType {
|
||||||
|
SSH_FILEXFER_ATTR_SIZE = 0x00000001,
|
||||||
|
SSH_FILEXFER_ATTR_UIDGID = 0x00000002,
|
||||||
|
SSH_FILEXFER_ATTR_PERMISSIONS = 0x00000004,
|
||||||
|
SSH_FILEXFER_ATTR_ACMODTIME = 0x00000008,
|
||||||
|
SSH_FILEXFER_ATTR_EXTENDED = 0x80000000
|
||||||
|
};
|
||||||
|
|
||||||
|
class AbstractSftpPacket
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AbstractSftpPacket();
|
||||||
|
quint32 requestId() const;
|
||||||
|
const QByteArray &rawData() const { return m_data; }
|
||||||
|
SftpPacketType type() const { return static_cast<SftpPacketType>(m_data.at(TypeOffset)); }
|
||||||
|
|
||||||
|
static const quint32 MaxDataSize; // "Pure" data size per read/writepacket.
|
||||||
|
static const quint32 MaxPacketSize;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
quint32 dataSize() const { return static_cast<quint32>(m_data.size()); }
|
||||||
|
|
||||||
|
static const int TypeOffset;
|
||||||
|
static const int RequestIdOffset;
|
||||||
|
static const int PayloadOffset;
|
||||||
|
|
||||||
|
QByteArray m_data;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Internal
|
||||||
|
} // namespace QSsh
|
||||||
81
client/3rd/QtSsh/src/ssh/ssh.pri
Normal file
81
client/3rd/QtSsh/src/ssh/ssh.pri
Normal file
|
|
@ -0,0 +1,81 @@
|
||||||
|
QT += gui network widgets
|
||||||
|
|
||||||
|
INCLUDEPATH += $$PWD
|
||||||
|
DEPENDPATH += $$PWD
|
||||||
|
|
||||||
|
SOURCES += $$PWD/sshsendfacility.cpp \
|
||||||
|
$$PWD/sshremoteprocess.cpp \
|
||||||
|
$$PWD/sshpacketparser.cpp \
|
||||||
|
$$PWD/sshpacket.cpp \
|
||||||
|
$$PWD/sshoutgoingpacket.cpp \
|
||||||
|
$$PWD/sshkeygenerator.cpp \
|
||||||
|
$$PWD/sshkeyexchange.cpp \
|
||||||
|
$$PWD/sshincomingpacket.cpp \
|
||||||
|
$$PWD/sshcryptofacility.cpp \
|
||||||
|
$$PWD/sshconnection.cpp \
|
||||||
|
$$PWD/sshchannelmanager.cpp \
|
||||||
|
$$PWD/sshchannel.cpp \
|
||||||
|
$$PWD/sshcapabilities.cpp \
|
||||||
|
$$PWD/sftppacket.cpp \
|
||||||
|
$$PWD/sftpoutgoingpacket.cpp \
|
||||||
|
$$PWD/sftpoperation.cpp \
|
||||||
|
$$PWD/sftpincomingpacket.cpp \
|
||||||
|
$$PWD/sftpdefs.cpp \
|
||||||
|
$$PWD/sftpchannel.cpp \
|
||||||
|
$$PWD/sshremoteprocessrunner.cpp \
|
||||||
|
$$PWD/sshconnectionmanager.cpp \
|
||||||
|
$$PWD/sshkeypasswordretriever.cpp \
|
||||||
|
$$PWD/sftpfilesystemmodel.cpp \
|
||||||
|
$$PWD/sshkeycreationdialog.cpp \
|
||||||
|
$$PWD/sshinit.cpp \
|
||||||
|
$$PWD/sshdirecttcpiptunnel.cpp \
|
||||||
|
$$PWD/sshlogging.cpp \
|
||||||
|
$$PWD/sshhostkeydatabase.cpp \
|
||||||
|
$$PWD/sshtcpipforwardserver.cpp \
|
||||||
|
$$PWD/sshtcpiptunnel.cpp \
|
||||||
|
$$PWD/sshforwardedtcpiptunnel.cpp
|
||||||
|
|
||||||
|
HEADERS += $$PWD/sshsendfacility_p.h \
|
||||||
|
$$PWD/sshremoteprocess.h \
|
||||||
|
$$PWD/sshremoteprocess_p.h \
|
||||||
|
$$PWD/sshpacketparser_p.h \
|
||||||
|
$$PWD/sshpacket_p.h \
|
||||||
|
$$PWD/sshoutgoingpacket_p.h \
|
||||||
|
$$PWD/sshkeygenerator.h \
|
||||||
|
$$PWD/sshkeyexchange_p.h \
|
||||||
|
$$PWD/sshincomingpacket_p.h \
|
||||||
|
$$PWD/sshexception_p.h \
|
||||||
|
$$PWD/ssherrors.h \
|
||||||
|
$$PWD/sshcryptofacility_p.h \
|
||||||
|
$$PWD/sshconnection.h \
|
||||||
|
$$PWD/sshconnection_p.h \
|
||||||
|
$$PWD/sshchannelmanager_p.h \
|
||||||
|
$$PWD/sshchannel_p.h \
|
||||||
|
$$PWD/sshcapabilities_p.h \
|
||||||
|
$$PWD/sshbotanconversions_p.h \
|
||||||
|
$$PWD/sftppacket_p.h \
|
||||||
|
$$PWD/sftpoutgoingpacket_p.h \
|
||||||
|
$$PWD/sftpoperation_p.h \
|
||||||
|
$$PWD/sftpincomingpacket_p.h \
|
||||||
|
$$PWD/sftpdefs.h \
|
||||||
|
$$PWD/sftpchannel.h \
|
||||||
|
$$PWD/sftpchannel_p.h \
|
||||||
|
$$PWD/sshremoteprocessrunner.h \
|
||||||
|
$$PWD/sshconnectionmanager.h \
|
||||||
|
$$PWD/sshpseudoterminal.h \
|
||||||
|
$$PWD/sshkeypasswordretriever_p.h \
|
||||||
|
$$PWD/sftpfilesystemmodel.h \
|
||||||
|
$$PWD/sshkeycreationdialog.h \
|
||||||
|
$$PWD/ssh_global.h \
|
||||||
|
$$PWD/sshdirecttcpiptunnel_p.h \
|
||||||
|
$$PWD/sshinit_p.h \
|
||||||
|
$$PWD/sshdirecttcpiptunnel.h \
|
||||||
|
$$PWD/sshlogging_p.h \
|
||||||
|
$$PWD/sshhostkeydatabase.h \
|
||||||
|
$$PWD/sshtcpipforwardserver.h \
|
||||||
|
$$PWD/sshtcpipforwardserver_p.h \
|
||||||
|
$$PWD/sshtcpiptunnel_p.h \
|
||||||
|
$$PWD/sshforwardedtcpiptunnel.h \
|
||||||
|
$$PWD/sshforwardedtcpiptunnel_p.h
|
||||||
|
|
||||||
|
FORMS += $$PWD/sshkeycreationdialog.ui
|
||||||
8
client/3rd/QtSsh/src/ssh/ssh.pro
Normal file
8
client/3rd/QtSsh/src/ssh/ssh.pro
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
TARGET = QtSsh
|
||||||
|
|
||||||
|
load(qt_module)
|
||||||
|
|
||||||
|
DEFINES += QTCSSH_LIBRARY
|
||||||
|
|
||||||
|
include($$PWD/ssh.pri)
|
||||||
|
include($$PWD/../botan/botan.pri)
|
||||||
1
client/3rd/QtSsh/src/ssh/ssh_dependencies.pri
Normal file
1
client/3rd/QtSsh/src/ssh/ssh_dependencies.pri
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
QTC_LIB_NAME = QtcSsh
|
||||||
41
client/3rd/QtSsh/src/ssh/ssh_global.h
Normal file
41
client/3rd/QtSsh/src/ssh/ssh_global.h
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2016 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QtGlobal>
|
||||||
|
|
||||||
|
//#if defined(QTCSSH_LIBRARY)
|
||||||
|
//# define QSSH_EXPORT Q_DECL_EXPORT
|
||||||
|
//#else
|
||||||
|
//# define QSSH_EXPORT Q_DECL_IMPORT
|
||||||
|
//#endif
|
||||||
|
|
||||||
|
#define QSSH_EXPORT
|
||||||
|
|
||||||
|
#define QSSH_PRINT_WARNING qWarning("Soft assert at %s:%d", __FILE__, __LINE__)
|
||||||
|
#define QSSH_ASSERT(cond) do { if (!(cond)) { QSSH_PRINT_WARNING; } } while (false)
|
||||||
|
#define QSSH_ASSERT_AND_RETURN(cond) do { if (!(cond)) { QSSH_PRINT_WARNING; return; } } while (false)
|
||||||
|
#define QSSH_ASSERT_AND_RETURN_VALUE(cond, value) do { if (!(cond)) { QSSH_PRINT_WARNING; return value; } } while (false)
|
||||||
132
client/3rd/QtSsh/src/ssh/sshbotanconversions_p.h
Normal file
132
client/3rd/QtSsh/src/ssh/sshbotanconversions_p.h
Normal file
|
|
@ -0,0 +1,132 @@
|
||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2016 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "sshcapabilities_p.h"
|
||||||
|
#include "sshexception_p.h"
|
||||||
|
|
||||||
|
#include <botan/botan.h>
|
||||||
|
|
||||||
|
namespace QSsh {
|
||||||
|
namespace Internal {
|
||||||
|
|
||||||
|
inline const Botan::byte *convertByteArray(const QByteArray &a)
|
||||||
|
{
|
||||||
|
return reinterpret_cast<const Botan::byte *>(a.constData());
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Botan::byte *convertByteArray(QByteArray &a)
|
||||||
|
{
|
||||||
|
return reinterpret_cast<Botan::byte *>(a.data());
|
||||||
|
}
|
||||||
|
|
||||||
|
inline QByteArray convertByteArray(const Botan::SecureVector<Botan::byte> &v)
|
||||||
|
{
|
||||||
|
return QByteArray(reinterpret_cast<const char *>(v.begin()), static_cast<int>(v.size()));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline const char *botanKeyExchangeAlgoName(const QByteArray &rfcAlgoName)
|
||||||
|
{
|
||||||
|
if (rfcAlgoName == SshCapabilities::DiffieHellmanGroup1Sha1)
|
||||||
|
return "modp/ietf/1024";
|
||||||
|
if (rfcAlgoName == SshCapabilities::DiffieHellmanGroup14Sha1)
|
||||||
|
return "modp/ietf/2048";
|
||||||
|
if (rfcAlgoName == SshCapabilities::EcdhNistp256)
|
||||||
|
return "secp256r1";
|
||||||
|
if (rfcAlgoName == SshCapabilities::EcdhNistp384)
|
||||||
|
return "secp384r1";
|
||||||
|
if (rfcAlgoName == SshCapabilities::EcdhNistp521)
|
||||||
|
return "secp521r1";
|
||||||
|
throw SshClientException(SshInternalError, SSH_TR("Unexpected key exchange algorithm \"%1\"")
|
||||||
|
.arg(QString::fromLatin1(rfcAlgoName)));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline const char *botanCryptAlgoName(const QByteArray &rfcAlgoName)
|
||||||
|
{
|
||||||
|
if (rfcAlgoName == SshCapabilities::CryptAlgoAes128Cbc
|
||||||
|
|| rfcAlgoName == SshCapabilities::CryptAlgoAes128Ctr) {
|
||||||
|
return "AES-128";
|
||||||
|
}
|
||||||
|
if (rfcAlgoName == SshCapabilities::CryptAlgo3DesCbc
|
||||||
|
|| rfcAlgoName == SshCapabilities::CryptAlgo3DesCtr) {
|
||||||
|
return "TripleDES";
|
||||||
|
}
|
||||||
|
if (rfcAlgoName == SshCapabilities::CryptAlgoAes192Ctr) {
|
||||||
|
return "AES-192";
|
||||||
|
}
|
||||||
|
if (rfcAlgoName == SshCapabilities::CryptAlgoAes256Ctr) {
|
||||||
|
return "AES-256";
|
||||||
|
}
|
||||||
|
throw SshClientException(SshInternalError, SSH_TR("Unexpected cipher \"%1\"")
|
||||||
|
.arg(QString::fromLatin1(rfcAlgoName)));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline const char *botanEmsaAlgoName(const QByteArray &rfcAlgoName)
|
||||||
|
{
|
||||||
|
if (rfcAlgoName == SshCapabilities::PubKeyDss)
|
||||||
|
return "EMSA1(SHA-1)";
|
||||||
|
if (rfcAlgoName == SshCapabilities::PubKeyRsa)
|
||||||
|
return "EMSA3(SHA-1)";
|
||||||
|
if (rfcAlgoName == SshCapabilities::PubKeyEcdsa256)
|
||||||
|
return "EMSA1_BSI(SHA-256)";
|
||||||
|
if (rfcAlgoName == SshCapabilities::PubKeyEcdsa384)
|
||||||
|
return "EMSA1_BSI(SHA-384)";
|
||||||
|
if (rfcAlgoName == SshCapabilities::PubKeyEcdsa521)
|
||||||
|
return "EMSA1_BSI(SHA-512)";
|
||||||
|
throw SshClientException(SshInternalError, SSH_TR("Unexpected host key algorithm \"%1\"")
|
||||||
|
.arg(QString::fromLatin1(rfcAlgoName)));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline const char *botanHMacAlgoName(const QByteArray &rfcAlgoName)
|
||||||
|
{
|
||||||
|
if (rfcAlgoName == SshCapabilities::HMacSha1)
|
||||||
|
return "SHA-1";
|
||||||
|
if (rfcAlgoName == SshCapabilities::HMacSha256)
|
||||||
|
return "SHA-256";
|
||||||
|
if (rfcAlgoName == SshCapabilities::HMacSha384)
|
||||||
|
return "SHA-384";
|
||||||
|
if (rfcAlgoName == SshCapabilities::HMacSha512)
|
||||||
|
return "SHA-512";
|
||||||
|
throw SshClientException(SshInternalError, SSH_TR("Unexpected hashing algorithm \"%1\"")
|
||||||
|
.arg(QString::fromLatin1(rfcAlgoName)));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline quint32 botanHMacKeyLen(const QByteArray &rfcAlgoName)
|
||||||
|
{
|
||||||
|
if (rfcAlgoName == SshCapabilities::HMacSha1)
|
||||||
|
return 20;
|
||||||
|
if (rfcAlgoName == SshCapabilities::HMacSha256)
|
||||||
|
return 32;
|
||||||
|
if (rfcAlgoName == SshCapabilities::HMacSha384)
|
||||||
|
return 48;
|
||||||
|
if (rfcAlgoName == SshCapabilities::HMacSha512)
|
||||||
|
return 64;
|
||||||
|
throw SshClientException(SshInternalError, SSH_TR("Unexpected hashing algorithm \"%1\"")
|
||||||
|
.arg(QString::fromLatin1(rfcAlgoName)));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Internal
|
||||||
|
} // namespace QSsh
|
||||||
170
client/3rd/QtSsh/src/ssh/sshcapabilities.cpp
Normal file
170
client/3rd/QtSsh/src/ssh/sshcapabilities.cpp
Normal file
|
|
@ -0,0 +1,170 @@
|
||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2016 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#include "sshcapabilities_p.h"
|
||||||
|
|
||||||
|
#include "sshexception_p.h"
|
||||||
|
|
||||||
|
#include <QCoreApplication>
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
namespace QSsh {
|
||||||
|
namespace Internal {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
QByteArray listAsByteArray(const QList<QByteArray> &list)
|
||||||
|
{
|
||||||
|
QByteArray array;
|
||||||
|
foreach (const QByteArray &elem, list)
|
||||||
|
array += elem + ',';
|
||||||
|
if (!array.isEmpty())
|
||||||
|
array.remove(array.count() - 1, 1);
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
} // anonymous namspace
|
||||||
|
|
||||||
|
const QByteArray SshCapabilities::DiffieHellmanGroup1Sha1("diffie-hellman-group1-sha1");
|
||||||
|
const QByteArray SshCapabilities::DiffieHellmanGroup14Sha1("diffie-hellman-group14-sha1");
|
||||||
|
const QByteArray SshCapabilities::EcdhKexNamePrefix("ecdh-sha2-nistp");
|
||||||
|
const QByteArray SshCapabilities::EcdhNistp256 = EcdhKexNamePrefix + "256";
|
||||||
|
const QByteArray SshCapabilities::EcdhNistp384 = EcdhKexNamePrefix + "384";
|
||||||
|
const QByteArray SshCapabilities::EcdhNistp521 = EcdhKexNamePrefix + "521";
|
||||||
|
const QList<QByteArray> SshCapabilities::KeyExchangeMethods = QList<QByteArray>()
|
||||||
|
<< SshCapabilities::EcdhNistp256
|
||||||
|
<< SshCapabilities::EcdhNistp384
|
||||||
|
<< SshCapabilities::EcdhNistp521
|
||||||
|
<< SshCapabilities::DiffieHellmanGroup1Sha1
|
||||||
|
<< SshCapabilities::DiffieHellmanGroup14Sha1;
|
||||||
|
|
||||||
|
const QByteArray SshCapabilities::PubKeyDss("ssh-dss");
|
||||||
|
const QByteArray SshCapabilities::PubKeyRsa("ssh-rsa");
|
||||||
|
const QByteArray SshCapabilities::PubKeyEcdsaPrefix("ecdsa-sha2-nistp");
|
||||||
|
const QByteArray SshCapabilities::PubKeyEcdsa256 = SshCapabilities::PubKeyEcdsaPrefix + "256";
|
||||||
|
const QByteArray SshCapabilities::PubKeyEcdsa384 = SshCapabilities::PubKeyEcdsaPrefix + "384";
|
||||||
|
const QByteArray SshCapabilities::PubKeyEcdsa521 = SshCapabilities::PubKeyEcdsaPrefix + "521";
|
||||||
|
const QList<QByteArray> SshCapabilities::PublicKeyAlgorithms = QList<QByteArray>()
|
||||||
|
<< SshCapabilities::PubKeyEcdsa256
|
||||||
|
<< SshCapabilities::PubKeyEcdsa384
|
||||||
|
<< SshCapabilities::PubKeyEcdsa521
|
||||||
|
<< SshCapabilities::PubKeyRsa
|
||||||
|
<< SshCapabilities::PubKeyDss;
|
||||||
|
|
||||||
|
const QByteArray SshCapabilities::CryptAlgo3DesCbc("3des-cbc");
|
||||||
|
const QByteArray SshCapabilities::CryptAlgo3DesCtr("3des-ctr");
|
||||||
|
const QByteArray SshCapabilities::CryptAlgoAes128Cbc("aes128-cbc");
|
||||||
|
const QByteArray SshCapabilities::CryptAlgoAes128Ctr("aes128-ctr");
|
||||||
|
const QByteArray SshCapabilities::CryptAlgoAes192Ctr("aes192-ctr");
|
||||||
|
const QByteArray SshCapabilities::CryptAlgoAes256Ctr("aes256-ctr");
|
||||||
|
const QList<QByteArray> SshCapabilities::EncryptionAlgorithms
|
||||||
|
= QList<QByteArray>() << SshCapabilities::CryptAlgoAes256Ctr
|
||||||
|
<< SshCapabilities::CryptAlgoAes192Ctr
|
||||||
|
<< SshCapabilities::CryptAlgoAes128Ctr
|
||||||
|
<< SshCapabilities::CryptAlgo3DesCtr
|
||||||
|
<< SshCapabilities::CryptAlgoAes128Cbc
|
||||||
|
<< SshCapabilities::CryptAlgo3DesCbc;
|
||||||
|
|
||||||
|
const QByteArray SshCapabilities::HMacSha1("hmac-sha1");
|
||||||
|
const QByteArray SshCapabilities::HMacSha196("hmac-sha1-96");
|
||||||
|
const QByteArray SshCapabilities::HMacSha256("hmac-sha2-256");
|
||||||
|
const QByteArray SshCapabilities::HMacSha384("hmac-sha2-384");
|
||||||
|
const QByteArray SshCapabilities::HMacSha512("hmac-sha2-512");
|
||||||
|
const QList<QByteArray> SshCapabilities::MacAlgorithms
|
||||||
|
= QList<QByteArray>() /* << SshCapabilities::HMacSha196 */
|
||||||
|
<< SshCapabilities::HMacSha256
|
||||||
|
<< SshCapabilities::HMacSha384
|
||||||
|
<< SshCapabilities::HMacSha512
|
||||||
|
<< SshCapabilities::HMacSha1;
|
||||||
|
|
||||||
|
const QList<QByteArray> SshCapabilities::CompressionAlgorithms
|
||||||
|
= QList<QByteArray>() << "none";
|
||||||
|
|
||||||
|
const QByteArray SshCapabilities::SshConnectionService("ssh-connection");
|
||||||
|
|
||||||
|
QList<QByteArray> SshCapabilities::commonCapabilities(const QList<QByteArray> &myCapabilities,
|
||||||
|
const QList<QByteArray> &serverCapabilities)
|
||||||
|
{
|
||||||
|
QList<QByteArray> capabilities;
|
||||||
|
foreach (const QByteArray &myCapability, myCapabilities) {
|
||||||
|
if (serverCapabilities.contains(myCapability))
|
||||||
|
capabilities << myCapability;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!capabilities.isEmpty())
|
||||||
|
return capabilities;
|
||||||
|
|
||||||
|
throw SshServerException(SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
|
||||||
|
"Server and client capabilities do not match.",
|
||||||
|
QCoreApplication::translate("SshConnection",
|
||||||
|
"Server and client capabilities don't match. "
|
||||||
|
"Client list was: %1.\nServer list was %2.")
|
||||||
|
.arg(QString::fromLocal8Bit(listAsByteArray(myCapabilities).data()))
|
||||||
|
.arg(QString::fromLocal8Bit(listAsByteArray(serverCapabilities).data())));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray SshCapabilities::findBestMatch(const QList<QByteArray> &myCapabilities,
|
||||||
|
const QList<QByteArray> &serverCapabilities)
|
||||||
|
{
|
||||||
|
return commonCapabilities(myCapabilities, serverCapabilities).first();
|
||||||
|
}
|
||||||
|
|
||||||
|
int SshCapabilities::ecdsaIntegerWidthInBytes(const QByteArray &ecdsaAlgo)
|
||||||
|
{
|
||||||
|
if (ecdsaAlgo == PubKeyEcdsa256)
|
||||||
|
return 32;
|
||||||
|
if (ecdsaAlgo == PubKeyEcdsa384)
|
||||||
|
return 48;
|
||||||
|
if (ecdsaAlgo == PubKeyEcdsa521)
|
||||||
|
return 66;
|
||||||
|
throw SshClientException(SshInternalError, SSH_TR("Unexpected ecdsa algorithm \"%1\"")
|
||||||
|
.arg(QString::fromLatin1(ecdsaAlgo)));
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray SshCapabilities::ecdsaPubKeyAlgoForKeyWidth(int keyWidthInBytes)
|
||||||
|
{
|
||||||
|
if (keyWidthInBytes <= 32)
|
||||||
|
return PubKeyEcdsa256;
|
||||||
|
if (keyWidthInBytes <= 48)
|
||||||
|
return PubKeyEcdsa384;
|
||||||
|
if (keyWidthInBytes <= 66)
|
||||||
|
return PubKeyEcdsa521;
|
||||||
|
throw SshClientException(SshInternalError, SSH_TR("Unexpected ecdsa key size (%1 bytes)")
|
||||||
|
.arg(keyWidthInBytes));
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *SshCapabilities::oid(const QByteArray &ecdsaAlgo)
|
||||||
|
{
|
||||||
|
if (ecdsaAlgo == PubKeyEcdsa256)
|
||||||
|
return "secp256r1";
|
||||||
|
if (ecdsaAlgo == PubKeyEcdsa384)
|
||||||
|
return "secp384r1";
|
||||||
|
if (ecdsaAlgo == PubKeyEcdsa521)
|
||||||
|
return "secp521r1";
|
||||||
|
throw SshClientException(SshInternalError, SSH_TR("Unexpected ecdsa algorithm \"%1\"")
|
||||||
|
.arg(QString::fromLatin1(ecdsaAlgo)));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Internal
|
||||||
|
} // namespace QSsh
|
||||||
83
client/3rd/QtSsh/src/ssh/sshcapabilities_p.h
Normal file
83
client/3rd/QtSsh/src/ssh/sshcapabilities_p.h
Normal file
|
|
@ -0,0 +1,83 @@
|
||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2016 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QByteArray>
|
||||||
|
#include <QList>
|
||||||
|
|
||||||
|
namespace QSsh {
|
||||||
|
namespace Internal {
|
||||||
|
|
||||||
|
class SshCapabilities
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static const QByteArray DiffieHellmanGroup1Sha1;
|
||||||
|
static const QByteArray DiffieHellmanGroup14Sha1;
|
||||||
|
static const QByteArray EcdhKexNamePrefix;
|
||||||
|
static const QByteArray EcdhNistp256;
|
||||||
|
static const QByteArray EcdhNistp384;
|
||||||
|
static const QByteArray EcdhNistp521; // sic
|
||||||
|
static const QList<QByteArray> KeyExchangeMethods;
|
||||||
|
|
||||||
|
static const QByteArray PubKeyDss;
|
||||||
|
static const QByteArray PubKeyRsa;
|
||||||
|
static const QByteArray PubKeyEcdsaPrefix;
|
||||||
|
static const QByteArray PubKeyEcdsa256;
|
||||||
|
static const QByteArray PubKeyEcdsa384;
|
||||||
|
static const QByteArray PubKeyEcdsa521;
|
||||||
|
static const QList<QByteArray> PublicKeyAlgorithms;
|
||||||
|
|
||||||
|
static const QByteArray CryptAlgo3DesCbc;
|
||||||
|
static const QByteArray CryptAlgo3DesCtr;
|
||||||
|
static const QByteArray CryptAlgoAes128Cbc;
|
||||||
|
static const QByteArray CryptAlgoAes128Ctr;
|
||||||
|
static const QByteArray CryptAlgoAes192Ctr;
|
||||||
|
static const QByteArray CryptAlgoAes256Ctr;
|
||||||
|
static const QList<QByteArray> EncryptionAlgorithms;
|
||||||
|
|
||||||
|
static const QByteArray HMacSha1;
|
||||||
|
static const QByteArray HMacSha196;
|
||||||
|
static const QByteArray HMacSha256;
|
||||||
|
static const QByteArray HMacSha384;
|
||||||
|
static const QByteArray HMacSha512;
|
||||||
|
static const QList<QByteArray> MacAlgorithms;
|
||||||
|
|
||||||
|
static const QList<QByteArray> CompressionAlgorithms;
|
||||||
|
|
||||||
|
static const QByteArray SshConnectionService;
|
||||||
|
|
||||||
|
static QList<QByteArray> commonCapabilities(const QList<QByteArray> &myCapabilities,
|
||||||
|
const QList<QByteArray> &serverCapabilities);
|
||||||
|
static QByteArray findBestMatch(const QList<QByteArray> &myCapabilities,
|
||||||
|
const QList<QByteArray> &serverCapabilities);
|
||||||
|
|
||||||
|
static int ecdsaIntegerWidthInBytes(const QByteArray &ecdsaAlgo);
|
||||||
|
static QByteArray ecdsaPubKeyAlgoForKeyWidth(int keyWidthInBytes);
|
||||||
|
static const char *oid(const QByteArray &ecdsaAlgo);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Internal
|
||||||
|
} // namespace QSsh
|
||||||
280
client/3rd/QtSsh/src/ssh/sshchannel.cpp
Normal file
280
client/3rd/QtSsh/src/ssh/sshchannel.cpp
Normal file
|
|
@ -0,0 +1,280 @@
|
||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2016 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#include "sshchannel_p.h"
|
||||||
|
|
||||||
|
#include "sshincomingpacket_p.h"
|
||||||
|
#include "sshlogging_p.h"
|
||||||
|
#include "sshsendfacility_p.h"
|
||||||
|
|
||||||
|
#include <botan/botan.h>
|
||||||
|
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
|
namespace QSsh {
|
||||||
|
namespace Internal {
|
||||||
|
|
||||||
|
// "Payload length" (RFC 4253, 6.1), i.e. minus packet type, channel number
|
||||||
|
// and length field for string.
|
||||||
|
const quint32 MinMaxPacketSize = 32768 - sizeof(quint32) - sizeof(quint32) - 1;
|
||||||
|
|
||||||
|
const quint32 NoChannel = 0xffffffffu;
|
||||||
|
|
||||||
|
AbstractSshChannel::AbstractSshChannel(quint32 channelId,
|
||||||
|
SshSendFacility &sendFacility)
|
||||||
|
: m_sendFacility(sendFacility),
|
||||||
|
m_localChannel(channelId), m_remoteChannel(NoChannel),
|
||||||
|
m_localWindowSize(initialWindowSize()), m_remoteWindowSize(0),
|
||||||
|
m_state(Inactive)
|
||||||
|
{
|
||||||
|
m_timeoutTimer.setSingleShot(true);
|
||||||
|
connect(&m_timeoutTimer, &QTimer::timeout, this, &AbstractSshChannel::timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
AbstractSshChannel::~AbstractSshChannel()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void AbstractSshChannel::setChannelState(ChannelState state)
|
||||||
|
{
|
||||||
|
m_state = state;
|
||||||
|
if (state == Closed)
|
||||||
|
closeHook();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AbstractSshChannel::requestSessionStart()
|
||||||
|
{
|
||||||
|
// Note: We are just being paranoid here about the Botan exceptions,
|
||||||
|
// which are extremely unlikely to happen, because if there was a problem
|
||||||
|
// with our cryptography stuff, it would have hit us before, on
|
||||||
|
// establishing the connection.
|
||||||
|
try {
|
||||||
|
m_sendFacility.sendSessionPacket(m_localChannel, initialWindowSize(), maxPacketSize());
|
||||||
|
setChannelState(SessionRequested);
|
||||||
|
m_timeoutTimer.start(ReplyTimeout);
|
||||||
|
} catch (const std::exception &e) {
|
||||||
|
qCWarning(sshLog, "Botan error: %s", e.what());
|
||||||
|
closeChannel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AbstractSshChannel::sendData(const QByteArray &data)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
m_sendBuffer += data;
|
||||||
|
flushSendBuffer();
|
||||||
|
} catch (const std::exception &e) {
|
||||||
|
qCWarning(sshLog, "Botan error: %s", e.what());
|
||||||
|
closeChannel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
quint32 AbstractSshChannel::initialWindowSize()
|
||||||
|
{
|
||||||
|
return maxPacketSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
quint32 AbstractSshChannel::maxPacketSize()
|
||||||
|
{
|
||||||
|
return 16 * 1024 * 1024;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AbstractSshChannel::handleWindowAdjust(quint32 bytesToAdd)
|
||||||
|
{
|
||||||
|
checkChannelActive();
|
||||||
|
|
||||||
|
const quint64 newValue = m_remoteWindowSize + bytesToAdd;
|
||||||
|
if (newValue > 0xffffffffu) {
|
||||||
|
throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
|
||||||
|
"Illegal window size requested.");
|
||||||
|
}
|
||||||
|
|
||||||
|
m_remoteWindowSize = newValue;
|
||||||
|
flushSendBuffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AbstractSshChannel::flushSendBuffer()
|
||||||
|
{
|
||||||
|
while (true) {
|
||||||
|
const quint32 bytesToSend = qMin(m_remoteMaxPacketSize,
|
||||||
|
qMin<quint32>(m_remoteWindowSize, m_sendBuffer.size()));
|
||||||
|
if (bytesToSend == 0)
|
||||||
|
break;
|
||||||
|
const QByteArray &data = m_sendBuffer.left(bytesToSend);
|
||||||
|
m_sendFacility.sendChannelDataPacket(m_remoteChannel, data);
|
||||||
|
m_sendBuffer.remove(0, bytesToSend);
|
||||||
|
m_remoteWindowSize -= bytesToSend;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AbstractSshChannel::handleOpenSuccess(quint32 remoteChannelId,
|
||||||
|
quint32 remoteWindowSize, quint32 remoteMaxPacketSize)
|
||||||
|
{
|
||||||
|
const ChannelState oldState = m_state;
|
||||||
|
switch (oldState) {
|
||||||
|
case CloseRequested: // closeChannel() was called while we were in SessionRequested state
|
||||||
|
case SessionRequested:
|
||||||
|
break; // Ok, continue.
|
||||||
|
default:
|
||||||
|
throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
|
||||||
|
"Unexpected SSH_MSG_CHANNEL_OPEN_CONFIRMATION packet.");
|
||||||
|
}
|
||||||
|
|
||||||
|
m_timeoutTimer.stop();
|
||||||
|
|
||||||
|
if (remoteMaxPacketSize < MinMaxPacketSize) {
|
||||||
|
throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
|
||||||
|
"Maximum packet size too low.");
|
||||||
|
}
|
||||||
|
|
||||||
|
qCDebug(sshLog, "Channel opened. remote channel id: %u, remote window size: %u, "
|
||||||
|
"remote max packet size: %u",
|
||||||
|
remoteChannelId, remoteWindowSize, remoteMaxPacketSize);
|
||||||
|
m_remoteChannel = remoteChannelId;
|
||||||
|
m_remoteWindowSize = remoteWindowSize;
|
||||||
|
m_remoteMaxPacketSize = remoteMaxPacketSize;
|
||||||
|
setChannelState(SessionEstablished);
|
||||||
|
if (oldState == CloseRequested)
|
||||||
|
closeChannel();
|
||||||
|
else
|
||||||
|
handleOpenSuccessInternal();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AbstractSshChannel::handleOpenFailure(const QString &reason)
|
||||||
|
{
|
||||||
|
switch (m_state) {
|
||||||
|
case SessionRequested:
|
||||||
|
break; // Ok, continue.
|
||||||
|
case CloseRequested:
|
||||||
|
return; // Late server reply; we requested a channel close in the meantime.
|
||||||
|
default:
|
||||||
|
throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
|
||||||
|
"Unexpected SSH_MSG_CHANNEL_OPEN_FAILURE packet.");
|
||||||
|
}
|
||||||
|
|
||||||
|
m_timeoutTimer.stop();
|
||||||
|
|
||||||
|
qCDebug(sshLog, "Channel open request failed for channel %u", m_localChannel);
|
||||||
|
handleOpenFailureInternal(reason);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AbstractSshChannel::handleChannelEof()
|
||||||
|
{
|
||||||
|
if (m_state == Inactive || m_state == Closed) {
|
||||||
|
throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
|
||||||
|
"Unexpected SSH_MSG_CHANNEL_EOF message.");
|
||||||
|
}
|
||||||
|
m_localWindowSize = 0;
|
||||||
|
emit eof();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AbstractSshChannel::handleChannelClose()
|
||||||
|
{
|
||||||
|
qCDebug(sshLog, "Receiving CLOSE for channel %u", m_localChannel);
|
||||||
|
if (channelState() == Inactive || channelState() == Closed) {
|
||||||
|
throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
|
||||||
|
"Unexpected SSH_MSG_CHANNEL_CLOSE message.");
|
||||||
|
}
|
||||||
|
closeChannel();
|
||||||
|
setChannelState(Closed);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AbstractSshChannel::handleChannelData(const QByteArray &data)
|
||||||
|
{
|
||||||
|
const int bytesToDeliver = handleChannelOrExtendedChannelData(data);
|
||||||
|
handleChannelDataInternal(bytesToDeliver == data.size()
|
||||||
|
? data : data.left(bytesToDeliver));
|
||||||
|
}
|
||||||
|
|
||||||
|
void AbstractSshChannel::handleChannelExtendedData(quint32 type, const QByteArray &data)
|
||||||
|
{
|
||||||
|
const int bytesToDeliver = handleChannelOrExtendedChannelData(data);
|
||||||
|
handleChannelExtendedDataInternal(type, bytesToDeliver == data.size()
|
||||||
|
? data : data.left(bytesToDeliver));
|
||||||
|
}
|
||||||
|
|
||||||
|
void AbstractSshChannel::handleChannelRequest(const SshIncomingPacket &packet)
|
||||||
|
{
|
||||||
|
checkChannelActive();
|
||||||
|
const QByteArray &requestType = packet.extractChannelRequestType();
|
||||||
|
if (requestType == SshIncomingPacket::ExitStatusType)
|
||||||
|
handleExitStatus(packet.extractChannelExitStatus());
|
||||||
|
else if (requestType == SshIncomingPacket::ExitSignalType)
|
||||||
|
handleExitSignal(packet.extractChannelExitSignal());
|
||||||
|
else if (requestType != "eow@openssh.com") // Suppress warning for this one, as it's sent all the time.
|
||||||
|
qCWarning(sshLog, "Ignoring unknown request type '%s'", requestType.data());
|
||||||
|
}
|
||||||
|
|
||||||
|
int AbstractSshChannel::handleChannelOrExtendedChannelData(const QByteArray &data)
|
||||||
|
{
|
||||||
|
checkChannelActive();
|
||||||
|
|
||||||
|
const int bytesToDeliver = qMin<quint32>(data.size(), maxDataSize());
|
||||||
|
if (bytesToDeliver != data.size())
|
||||||
|
qCWarning(sshLog, "Misbehaving server does not respect local window, clipping.");
|
||||||
|
|
||||||
|
m_localWindowSize -= bytesToDeliver;
|
||||||
|
if (m_localWindowSize < maxPacketSize()) {
|
||||||
|
m_localWindowSize += maxPacketSize();
|
||||||
|
m_sendFacility.sendWindowAdjustPacket(m_remoteChannel, maxPacketSize());
|
||||||
|
}
|
||||||
|
return bytesToDeliver;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AbstractSshChannel::closeChannel()
|
||||||
|
{
|
||||||
|
if (m_state == CloseRequested) {
|
||||||
|
m_timeoutTimer.stop();
|
||||||
|
} else if (m_state != Closed) {
|
||||||
|
if (m_state == Inactive) {
|
||||||
|
setChannelState(Closed);
|
||||||
|
} else {
|
||||||
|
const ChannelState oldState = m_state;
|
||||||
|
setChannelState(CloseRequested);
|
||||||
|
if (m_remoteChannel != NoChannel) {
|
||||||
|
m_sendFacility.sendChannelEofPacket(m_remoteChannel);
|
||||||
|
m_sendFacility.sendChannelClosePacket(m_remoteChannel);
|
||||||
|
} else {
|
||||||
|
QSSH_ASSERT(oldState == SessionRequested);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AbstractSshChannel::checkChannelActive()
|
||||||
|
{
|
||||||
|
if (channelState() == Inactive || channelState() == Closed)
|
||||||
|
throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
|
||||||
|
"Channel not open.");
|
||||||
|
}
|
||||||
|
|
||||||
|
quint32 AbstractSshChannel::maxDataSize() const
|
||||||
|
{
|
||||||
|
return qMin(m_localWindowSize, maxPacketSize());
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Internal
|
||||||
|
} // namespace QSsh
|
||||||
117
client/3rd/QtSsh/src/ssh/sshchannel_p.h
Normal file
117
client/3rd/QtSsh/src/ssh/sshchannel_p.h
Normal file
|
|
@ -0,0 +1,117 @@
|
||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2016 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QByteArray>
|
||||||
|
#include <QObject>
|
||||||
|
#include <QString>
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
|
namespace QSsh {
|
||||||
|
namespace Internal {
|
||||||
|
|
||||||
|
struct SshChannelExitSignal;
|
||||||
|
struct SshChannelExitStatus;
|
||||||
|
class SshIncomingPacket;
|
||||||
|
class SshSendFacility;
|
||||||
|
|
||||||
|
class AbstractSshChannel : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
enum ChannelState {
|
||||||
|
Inactive, SessionRequested, SessionEstablished, CloseRequested, Closed
|
||||||
|
};
|
||||||
|
|
||||||
|
quint32 localChannelId() const { return m_localChannel; }
|
||||||
|
quint32 remoteChannel() const { return m_remoteChannel; }
|
||||||
|
|
||||||
|
virtual void handleChannelSuccess() = 0;
|
||||||
|
virtual void handleChannelFailure() = 0;
|
||||||
|
|
||||||
|
void handleOpenSuccess(quint32 remoteChannelId, quint32 remoteWindowSize,
|
||||||
|
quint32 remoteMaxPacketSize);
|
||||||
|
void handleOpenFailure(const QString &reason);
|
||||||
|
void handleWindowAdjust(quint32 bytesToAdd);
|
||||||
|
void handleChannelEof();
|
||||||
|
void handleChannelClose();
|
||||||
|
void handleChannelData(const QByteArray &data);
|
||||||
|
void handleChannelExtendedData(quint32 type, const QByteArray &data);
|
||||||
|
void handleChannelRequest(const SshIncomingPacket &packet);
|
||||||
|
|
||||||
|
void closeChannel();
|
||||||
|
|
||||||
|
virtual ~AbstractSshChannel();
|
||||||
|
|
||||||
|
static const int ReplyTimeout = 10000; // milli seconds
|
||||||
|
ChannelState channelState() const { return m_state; }
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void timeout();
|
||||||
|
void eof();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
AbstractSshChannel(quint32 channelId, SshSendFacility &sendFacility);
|
||||||
|
|
||||||
|
void setChannelState(ChannelState state);
|
||||||
|
|
||||||
|
void requestSessionStart();
|
||||||
|
void sendData(const QByteArray &data);
|
||||||
|
|
||||||
|
static quint32 initialWindowSize();
|
||||||
|
static quint32 maxPacketSize();
|
||||||
|
|
||||||
|
quint32 maxDataSize() const;
|
||||||
|
void checkChannelActive();
|
||||||
|
|
||||||
|
SshSendFacility &m_sendFacility;
|
||||||
|
QTimer m_timeoutTimer;
|
||||||
|
|
||||||
|
private:
|
||||||
|
virtual void handleOpenSuccessInternal() = 0;
|
||||||
|
virtual void handleOpenFailureInternal(const QString &reason) = 0;
|
||||||
|
virtual void handleChannelDataInternal(const QByteArray &data) = 0;
|
||||||
|
virtual void handleChannelExtendedDataInternal(quint32 type,
|
||||||
|
const QByteArray &data) = 0;
|
||||||
|
virtual void handleExitStatus(const SshChannelExitStatus &exitStatus) = 0;
|
||||||
|
virtual void handleExitSignal(const SshChannelExitSignal &signal) = 0;
|
||||||
|
|
||||||
|
virtual void closeHook() = 0;
|
||||||
|
|
||||||
|
void flushSendBuffer();
|
||||||
|
int handleChannelOrExtendedChannelData(const QByteArray &data);
|
||||||
|
|
||||||
|
const quint32 m_localChannel;
|
||||||
|
quint32 m_remoteChannel;
|
||||||
|
quint32 m_localWindowSize;
|
||||||
|
quint32 m_remoteWindowSize;
|
||||||
|
quint32 m_remoteMaxPacketSize;
|
||||||
|
ChannelState m_state;
|
||||||
|
QByteArray m_sendBuffer;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Internal
|
||||||
|
} // namespace QSsh
|
||||||
328
client/3rd/QtSsh/src/ssh/sshchannelmanager.cpp
Normal file
328
client/3rd/QtSsh/src/ssh/sshchannelmanager.cpp
Normal file
|
|
@ -0,0 +1,328 @@
|
||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2016 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#include "sshchannelmanager_p.h"
|
||||||
|
|
||||||
|
#include "sftpchannel.h"
|
||||||
|
#include "sftpchannel_p.h"
|
||||||
|
#include "sshdirecttcpiptunnel.h"
|
||||||
|
#include "sshdirecttcpiptunnel_p.h"
|
||||||
|
#include "sshforwardedtcpiptunnel.h"
|
||||||
|
#include "sshforwardedtcpiptunnel_p.h"
|
||||||
|
#include "sshincomingpacket_p.h"
|
||||||
|
#include "sshlogging_p.h"
|
||||||
|
#include "sshremoteprocess.h"
|
||||||
|
#include "sshremoteprocess_p.h"
|
||||||
|
#include "sshsendfacility_p.h"
|
||||||
|
#include "sshtcpipforwardserver.h"
|
||||||
|
#include "sshtcpipforwardserver_p.h"
|
||||||
|
|
||||||
|
#include <QList>
|
||||||
|
|
||||||
|
namespace QSsh {
|
||||||
|
namespace Internal {
|
||||||
|
|
||||||
|
SshChannelManager::SshChannelManager(SshSendFacility &sendFacility,
|
||||||
|
QObject *parent)
|
||||||
|
: QObject(parent), m_sendFacility(sendFacility), m_nextLocalChannelId(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshChannelManager::handleChannelRequest(const SshIncomingPacket &packet)
|
||||||
|
{
|
||||||
|
lookupChannel(packet.extractRecipientChannel())
|
||||||
|
->handleChannelRequest(packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshChannelManager::handleChannelOpen(const SshIncomingPacket &packet)
|
||||||
|
{
|
||||||
|
SshChannelOpen channelOpen = packet.extractChannelOpen();
|
||||||
|
|
||||||
|
SshTcpIpForwardServer::Ptr server;
|
||||||
|
|
||||||
|
foreach (const SshTcpIpForwardServer::Ptr &candidate, m_listeningForwardServers) {
|
||||||
|
if (candidate->port() == channelOpen.remotePort
|
||||||
|
&& candidate->bindAddress().toUtf8() == channelOpen.remoteAddress) {
|
||||||
|
server = candidate;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
if (server.isNull()) {
|
||||||
|
// Apparently the server knows a remoteAddress we are not aware of. There are plenty of ways
|
||||||
|
// to make that happen: /etc/hosts on the server, different writings for localhost,
|
||||||
|
// different DNS servers, ...
|
||||||
|
// Rather than trying to figure that out, we just use the first listening forwarder with the
|
||||||
|
// same port.
|
||||||
|
foreach (const SshTcpIpForwardServer::Ptr &candidate, m_listeningForwardServers) {
|
||||||
|
if (candidate->port() == channelOpen.remotePort) {
|
||||||
|
server = candidate;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (server.isNull()) {
|
||||||
|
SshOpenFailureType reason = (channelOpen.remotePort == 0) ?
|
||||||
|
SSH_OPEN_UNKNOWN_CHANNEL_TYPE : SSH_OPEN_ADMINISTRATIVELY_PROHIBITED;
|
||||||
|
try {
|
||||||
|
m_sendFacility.sendChannelOpenFailurePacket(channelOpen.remoteChannel, reason,
|
||||||
|
QByteArray());
|
||||||
|
} catch (const std::exception &e) {
|
||||||
|
qCWarning(sshLog, "Botan error: %s", e.what());
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SshForwardedTcpIpTunnel::Ptr tunnel(new SshForwardedTcpIpTunnel(m_nextLocalChannelId++,
|
||||||
|
m_sendFacility));
|
||||||
|
tunnel->d->handleOpenSuccess(channelOpen.remoteChannel, channelOpen.remoteWindowSize,
|
||||||
|
channelOpen.remoteMaxPacketSize);
|
||||||
|
tunnel->open(QIODevice::ReadWrite);
|
||||||
|
server->setNewConnection(tunnel);
|
||||||
|
insertChannel(tunnel->d, tunnel);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshChannelManager::handleChannelOpenFailure(const SshIncomingPacket &packet)
|
||||||
|
{
|
||||||
|
const SshChannelOpenFailure &failure = packet.extractChannelOpenFailure();
|
||||||
|
ChannelIterator it = lookupChannelAsIterator(failure.localChannel);
|
||||||
|
try {
|
||||||
|
it.value()->handleOpenFailure(failure.reasonString);
|
||||||
|
} catch (const SshServerException &e) {
|
||||||
|
removeChannel(it);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
removeChannel(it);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshChannelManager::handleChannelOpenConfirmation(const SshIncomingPacket &packet)
|
||||||
|
{
|
||||||
|
const SshChannelOpenConfirmation &confirmation
|
||||||
|
= packet.extractChannelOpenConfirmation();
|
||||||
|
lookupChannel(confirmation.localChannel)->handleOpenSuccess(confirmation.remoteChannel,
|
||||||
|
confirmation.remoteWindowSize, confirmation.remoteMaxPacketSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshChannelManager::handleChannelSuccess(const SshIncomingPacket &packet)
|
||||||
|
{
|
||||||
|
lookupChannel(packet.extractRecipientChannel())->handleChannelSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshChannelManager::handleChannelFailure(const SshIncomingPacket &packet)
|
||||||
|
{
|
||||||
|
lookupChannel(packet.extractRecipientChannel())->handleChannelFailure();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshChannelManager::handleChannelWindowAdjust(const SshIncomingPacket &packet)
|
||||||
|
{
|
||||||
|
const SshChannelWindowAdjust adjust = packet.extractWindowAdjust();
|
||||||
|
lookupChannel(adjust.localChannel)->handleWindowAdjust(adjust.bytesToAdd);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshChannelManager::handleChannelData(const SshIncomingPacket &packet)
|
||||||
|
{
|
||||||
|
const SshChannelData &data = packet.extractChannelData();
|
||||||
|
lookupChannel(data.localChannel)->handleChannelData(data.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshChannelManager::handleChannelExtendedData(const SshIncomingPacket &packet)
|
||||||
|
{
|
||||||
|
const SshChannelExtendedData &data = packet.extractChannelExtendedData();
|
||||||
|
lookupChannel(data.localChannel)->handleChannelExtendedData(data.type, data.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshChannelManager::handleChannelEof(const SshIncomingPacket &packet)
|
||||||
|
{
|
||||||
|
AbstractSshChannel * const channel
|
||||||
|
= lookupChannel(packet.extractRecipientChannel(), true);
|
||||||
|
if (channel)
|
||||||
|
channel->handleChannelEof();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshChannelManager::handleChannelClose(const SshIncomingPacket &packet)
|
||||||
|
{
|
||||||
|
const quint32 channelId = packet.extractRecipientChannel();
|
||||||
|
|
||||||
|
ChannelIterator it = lookupChannelAsIterator(channelId, true);
|
||||||
|
if (it != m_channels.end()) {
|
||||||
|
it.value()->handleChannelClose();
|
||||||
|
removeChannel(it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshChannelManager::handleRequestSuccess(const SshIncomingPacket &packet)
|
||||||
|
{
|
||||||
|
if (m_waitingForwardServers.isEmpty()) {
|
||||||
|
throw SshServerException(SSH_DISCONNECT_PROTOCOL_ERROR,
|
||||||
|
"Unexpected request success packet.",
|
||||||
|
tr("Unexpected request success packet."));
|
||||||
|
}
|
||||||
|
SshTcpIpForwardServer::Ptr server = m_waitingForwardServers.takeFirst();
|
||||||
|
if (server->state() == SshTcpIpForwardServer::Closing) {
|
||||||
|
server->setClosed();
|
||||||
|
} else if (server->state() == SshTcpIpForwardServer::Initializing) {
|
||||||
|
quint16 port = server->port();
|
||||||
|
if (port == 0)
|
||||||
|
port = packet.extractRequestSuccess().bindPort;
|
||||||
|
server->setListening(port);
|
||||||
|
m_listeningForwardServers.append(server);
|
||||||
|
} else {
|
||||||
|
QSSH_ASSERT(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshChannelManager::handleRequestFailure(const SshIncomingPacket &packet)
|
||||||
|
{
|
||||||
|
Q_UNUSED(packet);
|
||||||
|
if (m_waitingForwardServers.isEmpty()) {
|
||||||
|
throw SshServerException(SSH_DISCONNECT_PROTOCOL_ERROR,
|
||||||
|
"Unexpected request failure packet.",
|
||||||
|
tr("Unexpected request failure packet."));
|
||||||
|
}
|
||||||
|
SshTcpIpForwardServer::Ptr tunnel = m_waitingForwardServers.takeFirst();
|
||||||
|
tunnel->setClosed();
|
||||||
|
}
|
||||||
|
|
||||||
|
SshChannelManager::ChannelIterator SshChannelManager::lookupChannelAsIterator(quint32 channelId,
|
||||||
|
bool allowNotFound)
|
||||||
|
{
|
||||||
|
ChannelIterator it = m_channels.find(channelId);
|
||||||
|
if (it == m_channels.end() && !allowNotFound) {
|
||||||
|
throw SshServerException(SSH_DISCONNECT_PROTOCOL_ERROR,
|
||||||
|
"Invalid channel id.",
|
||||||
|
tr("Invalid channel id %1").arg(channelId));
|
||||||
|
}
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
|
||||||
|
AbstractSshChannel *SshChannelManager::lookupChannel(quint32 channelId,
|
||||||
|
bool allowNotFound)
|
||||||
|
{
|
||||||
|
ChannelIterator it = lookupChannelAsIterator(channelId, allowNotFound);
|
||||||
|
return it == m_channels.end() ? 0 : it.value();
|
||||||
|
}
|
||||||
|
|
||||||
|
QSsh::SshRemoteProcess::Ptr SshChannelManager::createRemoteProcess(const QByteArray &command)
|
||||||
|
{
|
||||||
|
SshRemoteProcess::Ptr proc(new SshRemoteProcess(command, m_nextLocalChannelId++, m_sendFacility));
|
||||||
|
insertChannel(proc->d, proc);
|
||||||
|
return proc;
|
||||||
|
}
|
||||||
|
|
||||||
|
QSsh::SshRemoteProcess::Ptr SshChannelManager::createRemoteShell()
|
||||||
|
{
|
||||||
|
SshRemoteProcess::Ptr proc(new SshRemoteProcess(m_nextLocalChannelId++, m_sendFacility));
|
||||||
|
insertChannel(proc->d, proc);
|
||||||
|
return proc;
|
||||||
|
}
|
||||||
|
|
||||||
|
QSsh::SftpChannel::Ptr SshChannelManager::createSftpChannel()
|
||||||
|
{
|
||||||
|
SftpChannel::Ptr sftp(new SftpChannel(m_nextLocalChannelId++, m_sendFacility));
|
||||||
|
insertChannel(sftp->d, sftp);
|
||||||
|
return sftp;
|
||||||
|
}
|
||||||
|
|
||||||
|
SshDirectTcpIpTunnel::Ptr SshChannelManager::createDirectTunnel(const QString &originatingHost,
|
||||||
|
quint16 originatingPort, const QString &remoteHost, quint16 remotePort)
|
||||||
|
{
|
||||||
|
SshDirectTcpIpTunnel::Ptr tunnel(new SshDirectTcpIpTunnel(m_nextLocalChannelId++,
|
||||||
|
originatingHost, originatingPort, remoteHost, remotePort, m_sendFacility));
|
||||||
|
insertChannel(tunnel->d, tunnel);
|
||||||
|
return tunnel;
|
||||||
|
}
|
||||||
|
|
||||||
|
SshTcpIpForwardServer::Ptr SshChannelManager::createForwardServer(const QString &remoteHost,
|
||||||
|
quint16 remotePort)
|
||||||
|
{
|
||||||
|
SshTcpIpForwardServer::Ptr server(new SshTcpIpForwardServer(remoteHost, remotePort,
|
||||||
|
m_sendFacility));
|
||||||
|
connect(server.data(), &SshTcpIpForwardServer::stateChanged,
|
||||||
|
this, [this, server](SshTcpIpForwardServer::State state) {
|
||||||
|
switch (state) {
|
||||||
|
case SshTcpIpForwardServer::Closing:
|
||||||
|
m_listeningForwardServers.removeOne(server);
|
||||||
|
// fall through
|
||||||
|
case SshTcpIpForwardServer::Initializing:
|
||||||
|
m_waitingForwardServers.append(server);
|
||||||
|
break;
|
||||||
|
case SshTcpIpForwardServer::Listening:
|
||||||
|
case SshTcpIpForwardServer::Inactive:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return server;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshChannelManager::insertChannel(AbstractSshChannel *priv,
|
||||||
|
const QSharedPointer<QObject> &pub)
|
||||||
|
{
|
||||||
|
connect(priv, &AbstractSshChannel::timeout, this, &SshChannelManager::timeout);
|
||||||
|
m_channels.insert(priv->localChannelId(), priv);
|
||||||
|
m_sessions.insert(priv, pub);
|
||||||
|
}
|
||||||
|
|
||||||
|
int SshChannelManager::closeAllChannels(CloseAllMode mode)
|
||||||
|
{
|
||||||
|
int count = 0;
|
||||||
|
for (ChannelIterator it = m_channels.begin(); it != m_channels.end(); ++it) {
|
||||||
|
AbstractSshChannel * const channel = it.value();
|
||||||
|
QSSH_ASSERT(channel->channelState() != AbstractSshChannel::Closed);
|
||||||
|
if (channel->channelState() != AbstractSshChannel::CloseRequested) {
|
||||||
|
channel->closeChannel();
|
||||||
|
++count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (mode == CloseAllAndReset) {
|
||||||
|
m_channels.clear();
|
||||||
|
m_sessions.clear();
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
int SshChannelManager::channelCount() const
|
||||||
|
{
|
||||||
|
return m_channels.count();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshChannelManager::removeChannel(ChannelIterator it)
|
||||||
|
{
|
||||||
|
if (it == m_channels.end()) {
|
||||||
|
throw SshClientException(SshInternalError,
|
||||||
|
QLatin1String("Internal error: Unexpected channel lookup failure"));
|
||||||
|
}
|
||||||
|
const int removeCount = m_sessions.remove(it.value());
|
||||||
|
if (removeCount != 1) {
|
||||||
|
throw SshClientException(SshInternalError,
|
||||||
|
QString::fromLatin1("Internal error: Unexpected session count %1 for channel.")
|
||||||
|
.arg(removeCount));
|
||||||
|
}
|
||||||
|
m_channels.erase(it);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Internal
|
||||||
|
} // namespace QSsh
|
||||||
99
client/3rd/QtSsh/src/ssh/sshchannelmanager_p.h
Normal file
99
client/3rd/QtSsh/src/ssh/sshchannelmanager_p.h
Normal file
|
|
@ -0,0 +1,99 @@
|
||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2016 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QHash>
|
||||||
|
#include <QObject>
|
||||||
|
#include <QSharedPointer>
|
||||||
|
|
||||||
|
namespace QSsh {
|
||||||
|
class SftpChannel;
|
||||||
|
class SshDirectTcpIpTunnel;
|
||||||
|
class SshRemoteProcess;
|
||||||
|
class SshTcpIpForwardServer;
|
||||||
|
|
||||||
|
namespace Internal {
|
||||||
|
|
||||||
|
class AbstractSshChannel;
|
||||||
|
class SshIncomingPacket;
|
||||||
|
class SshSendFacility;
|
||||||
|
|
||||||
|
class SshChannelManager : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
SshChannelManager(SshSendFacility &sendFacility, QObject *parent);
|
||||||
|
|
||||||
|
QSharedPointer<SshRemoteProcess> createRemoteProcess(const QByteArray &command);
|
||||||
|
QSharedPointer<SshRemoteProcess> createRemoteShell();
|
||||||
|
QSharedPointer<SftpChannel> createSftpChannel();
|
||||||
|
QSharedPointer<SshDirectTcpIpTunnel> createDirectTunnel(const QString &originatingHost,
|
||||||
|
quint16 originatingPort, const QString &remoteHost, quint16 remotePort);
|
||||||
|
QSharedPointer<SshTcpIpForwardServer> createForwardServer(const QString &remoteHost,
|
||||||
|
quint16 remotePort);
|
||||||
|
|
||||||
|
int channelCount() const;
|
||||||
|
enum CloseAllMode { CloseAllRegular, CloseAllAndReset };
|
||||||
|
int closeAllChannels(CloseAllMode mode);
|
||||||
|
|
||||||
|
void handleChannelRequest(const SshIncomingPacket &packet);
|
||||||
|
void handleChannelOpen(const SshIncomingPacket &packet);
|
||||||
|
void handleChannelOpenFailure(const SshIncomingPacket &packet);
|
||||||
|
void handleChannelOpenConfirmation(const SshIncomingPacket &packet);
|
||||||
|
void handleChannelSuccess(const SshIncomingPacket &packet);
|
||||||
|
void handleChannelFailure(const SshIncomingPacket &packet);
|
||||||
|
void handleChannelWindowAdjust(const SshIncomingPacket &packet);
|
||||||
|
void handleChannelData(const SshIncomingPacket &packet);
|
||||||
|
void handleChannelExtendedData(const SshIncomingPacket &packet);
|
||||||
|
void handleChannelEof(const SshIncomingPacket &packet);
|
||||||
|
void handleChannelClose(const SshIncomingPacket &packet);
|
||||||
|
void handleRequestSuccess(const SshIncomingPacket &packet);
|
||||||
|
void handleRequestFailure(const SshIncomingPacket &packet);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void timeout();
|
||||||
|
|
||||||
|
private:
|
||||||
|
typedef QHash<quint32, AbstractSshChannel *>::Iterator ChannelIterator;
|
||||||
|
|
||||||
|
ChannelIterator lookupChannelAsIterator(quint32 channelId,
|
||||||
|
bool allowNotFound = false);
|
||||||
|
AbstractSshChannel *lookupChannel(quint32 channelId,
|
||||||
|
bool allowNotFound = false);
|
||||||
|
void removeChannel(ChannelIterator it);
|
||||||
|
void insertChannel(AbstractSshChannel *priv,
|
||||||
|
const QSharedPointer<QObject> &pub);
|
||||||
|
|
||||||
|
SshSendFacility &m_sendFacility;
|
||||||
|
QHash<quint32, AbstractSshChannel *> m_channels;
|
||||||
|
QHash<AbstractSshChannel *, QSharedPointer<QObject> > m_sessions;
|
||||||
|
quint32 m_nextLocalChannelId;
|
||||||
|
QList<QSharedPointer<SshTcpIpForwardServer>> m_waitingForwardServers;
|
||||||
|
QList<QSharedPointer<SshTcpIpForwardServer>> m_listeningForwardServers;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Internal
|
||||||
|
} // namespace QSsh
|
||||||
864
client/3rd/QtSsh/src/ssh/sshconnection.cpp
Normal file
864
client/3rd/QtSsh/src/ssh/sshconnection.cpp
Normal file
|
|
@ -0,0 +1,864 @@
|
||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2016 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#include "sshconnection.h"
|
||||||
|
#include "sshconnection_p.h"
|
||||||
|
|
||||||
|
#include "sftpchannel.h"
|
||||||
|
#include "sshcapabilities_p.h"
|
||||||
|
#include "sshchannelmanager_p.h"
|
||||||
|
#include "sshcryptofacility_p.h"
|
||||||
|
#include "sshdirecttcpiptunnel.h"
|
||||||
|
#include "sshtcpipforwardserver.h"
|
||||||
|
#include "sshexception_p.h"
|
||||||
|
#include "sshinit_p.h"
|
||||||
|
#include "sshkeyexchange_p.h"
|
||||||
|
#include "sshlogging_p.h"
|
||||||
|
#include "sshremoteprocess.h"
|
||||||
|
|
||||||
|
#include <botan/botan.h>
|
||||||
|
|
||||||
|
#include <QFile>
|
||||||
|
#include <QMutex>
|
||||||
|
#include <QMutexLocker>
|
||||||
|
#include <QNetworkProxy>
|
||||||
|
#include <QRegExp>
|
||||||
|
#include <QTcpSocket>
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\class QSsh::SshConnection
|
||||||
|
|
||||||
|
\brief The SshConnection class provides an SSH connection, implementing
|
||||||
|
protocol version 2.0.
|
||||||
|
|
||||||
|
It can spawn channels for remote execution and SFTP operations (version 3).
|
||||||
|
It operates asynchronously (non-blocking) and is not thread-safe.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace QSsh {
|
||||||
|
|
||||||
|
const QByteArray ClientId("SSH-2.0-QtCreator\r\n");
|
||||||
|
|
||||||
|
SshConnectionParameters::SshConnectionParameters() :
|
||||||
|
timeout(0), authenticationType(AuthenticationTypePublicKey), port(0),
|
||||||
|
hostKeyCheckingMode(SshHostKeyCheckingNone)
|
||||||
|
{
|
||||||
|
options |= SshIgnoreDefaultProxy;
|
||||||
|
options |= SshEnableStrictConformanceChecks;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool equals(const SshConnectionParameters &p1, const SshConnectionParameters &p2)
|
||||||
|
{
|
||||||
|
return p1.host == p2.host && p1.userName == p2.userName
|
||||||
|
&& p1.authenticationType == p2.authenticationType
|
||||||
|
&& (p1.authenticationType == SshConnectionParameters::AuthenticationTypePassword ?
|
||||||
|
p1.password == p2.password : p1.privateKeyFile == p2.privateKeyFile)
|
||||||
|
&& p1.hostKeyCheckingMode == p2.hostKeyCheckingMode
|
||||||
|
&& p1.timeout == p2.timeout && p1.port == p2.port;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const SshConnectionParameters &p1, const SshConnectionParameters &p2)
|
||||||
|
{
|
||||||
|
return equals(p1, p2);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator!=(const SshConnectionParameters &p1, const SshConnectionParameters &p2)
|
||||||
|
{
|
||||||
|
return !equals(p1, p2);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
SshConnection::SshConnection(const SshConnectionParameters &serverInfo, QObject *parent)
|
||||||
|
: QObject(parent)
|
||||||
|
{
|
||||||
|
Internal::initSsh();
|
||||||
|
qRegisterMetaType<QSsh::SshError>("QSsh::SshError");
|
||||||
|
qRegisterMetaType<QSsh::SftpJobId>("QSsh::SftpJobId");
|
||||||
|
qRegisterMetaType<QSsh::SftpFileInfo>("QSsh::SftpFileInfo");
|
||||||
|
qRegisterMetaType<QList <QSsh::SftpFileInfo> >("QList<QSsh::SftpFileInfo>");
|
||||||
|
|
||||||
|
d = new Internal::SshConnectionPrivate(this, serverInfo);
|
||||||
|
connect(d, &Internal::SshConnectionPrivate::connected, this, &SshConnection::connected,
|
||||||
|
Qt::QueuedConnection);
|
||||||
|
connect(d, &Internal::SshConnectionPrivate::dataAvailable, this,
|
||||||
|
&SshConnection::dataAvailable, Qt::QueuedConnection);
|
||||||
|
connect(d, &Internal::SshConnectionPrivate::disconnected, this, &SshConnection::disconnected,
|
||||||
|
Qt::QueuedConnection);
|
||||||
|
connect(d, &Internal::SshConnectionPrivate::error, this,
|
||||||
|
&SshConnection::error, Qt::QueuedConnection);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshConnection::connectToHost()
|
||||||
|
{
|
||||||
|
d->connectToHost();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshConnection::disconnectFromHost()
|
||||||
|
{
|
||||||
|
d->closeConnection(Internal::SSH_DISCONNECT_BY_APPLICATION, SshNoError, "",
|
||||||
|
QString());
|
||||||
|
}
|
||||||
|
|
||||||
|
SshConnection::State SshConnection::state() const
|
||||||
|
{
|
||||||
|
switch (d->state()) {
|
||||||
|
case Internal::SocketUnconnected:
|
||||||
|
return Unconnected;
|
||||||
|
case Internal::ConnectionEstablished:
|
||||||
|
return Connected;
|
||||||
|
default:
|
||||||
|
return Connecting;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SshError SshConnection::errorState() const
|
||||||
|
{
|
||||||
|
return d->errorState();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString SshConnection::errorString() const
|
||||||
|
{
|
||||||
|
return d->errorString();
|
||||||
|
}
|
||||||
|
|
||||||
|
SshConnectionParameters SshConnection::connectionParameters() const
|
||||||
|
{
|
||||||
|
return d->m_connParams;
|
||||||
|
}
|
||||||
|
|
||||||
|
SshConnectionInfo SshConnection::connectionInfo() const
|
||||||
|
{
|
||||||
|
QSSH_ASSERT_AND_RETURN_VALUE(state() == Connected, SshConnectionInfo());
|
||||||
|
|
||||||
|
return SshConnectionInfo(d->m_socket->localAddress(), d->m_socket->localPort(),
|
||||||
|
d->m_socket->peerAddress(), d->m_socket->peerPort());
|
||||||
|
}
|
||||||
|
|
||||||
|
SshConnection::~SshConnection()
|
||||||
|
{
|
||||||
|
disconnect();
|
||||||
|
disconnectFromHost();
|
||||||
|
delete d;
|
||||||
|
}
|
||||||
|
|
||||||
|
QSharedPointer<SshRemoteProcess> SshConnection::createRemoteProcess(const QByteArray &command)
|
||||||
|
{
|
||||||
|
QSSH_ASSERT_AND_RETURN_VALUE(state() == Connected, QSharedPointer<SshRemoteProcess>());
|
||||||
|
return d->createRemoteProcess(command);
|
||||||
|
}
|
||||||
|
|
||||||
|
QSharedPointer<SshRemoteProcess> SshConnection::createRemoteShell()
|
||||||
|
{
|
||||||
|
QSSH_ASSERT_AND_RETURN_VALUE(state() == Connected, QSharedPointer<SshRemoteProcess>());
|
||||||
|
return d->createRemoteShell();
|
||||||
|
}
|
||||||
|
|
||||||
|
QSharedPointer<SftpChannel> SshConnection::createSftpChannel()
|
||||||
|
{
|
||||||
|
QSSH_ASSERT_AND_RETURN_VALUE(state() == Connected, QSharedPointer<SftpChannel>());
|
||||||
|
return d->createSftpChannel();
|
||||||
|
}
|
||||||
|
|
||||||
|
SshDirectTcpIpTunnel::Ptr SshConnection::createDirectTunnel(const QString &originatingHost,
|
||||||
|
quint16 originatingPort, const QString &remoteHost, quint16 remotePort)
|
||||||
|
{
|
||||||
|
QSSH_ASSERT_AND_RETURN_VALUE(state() == Connected, SshDirectTcpIpTunnel::Ptr());
|
||||||
|
return d->createDirectTunnel(originatingHost, originatingPort, remoteHost, remotePort);
|
||||||
|
}
|
||||||
|
|
||||||
|
QSharedPointer<SshTcpIpForwardServer> SshConnection::createForwardServer(const QString &remoteHost,
|
||||||
|
quint16 remotePort)
|
||||||
|
{
|
||||||
|
QSSH_ASSERT_AND_RETURN_VALUE(state() == Connected, SshTcpIpForwardServer::Ptr());
|
||||||
|
return d->createForwardServer(remoteHost, remotePort);
|
||||||
|
}
|
||||||
|
|
||||||
|
int SshConnection::closeAllChannels()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
return d->m_channelManager->closeAllChannels(Internal::SshChannelManager::CloseAllRegular);
|
||||||
|
} catch (const std::exception &e) {
|
||||||
|
qCWarning(Internal::sshLog, "%s: %s", Q_FUNC_INFO, e.what());
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int SshConnection::channelCount() const
|
||||||
|
{
|
||||||
|
return d->m_channelManager->channelCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Internal {
|
||||||
|
|
||||||
|
SshConnectionPrivate::SshConnectionPrivate(SshConnection *conn,
|
||||||
|
const SshConnectionParameters &serverInfo)
|
||||||
|
: m_socket(new QTcpSocket(this)), m_state(SocketUnconnected),
|
||||||
|
m_sendFacility(m_socket),
|
||||||
|
m_channelManager(new SshChannelManager(m_sendFacility, this)),
|
||||||
|
m_connParams(serverInfo), m_error(SshNoError), m_ignoreNextPacket(false),
|
||||||
|
m_conn(conn)
|
||||||
|
{
|
||||||
|
setupPacketHandlers();
|
||||||
|
m_socket->setProxy((m_connParams.options & SshIgnoreDefaultProxy)
|
||||||
|
? QNetworkProxy::NoProxy : QNetworkProxy::DefaultProxy);
|
||||||
|
m_timeoutTimer.setSingleShot(true);
|
||||||
|
m_timeoutTimer.setInterval(m_connParams.timeout * 1000);
|
||||||
|
m_keepAliveTimer.setSingleShot(true);
|
||||||
|
m_keepAliveTimer.setInterval(10000);
|
||||||
|
connect(m_channelManager, &SshChannelManager::timeout,
|
||||||
|
this, &SshConnectionPrivate::handleTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
SshConnectionPrivate::~SshConnectionPrivate()
|
||||||
|
{
|
||||||
|
disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshConnectionPrivate::setupPacketHandlers()
|
||||||
|
{
|
||||||
|
typedef SshConnectionPrivate This;
|
||||||
|
|
||||||
|
setupPacketHandler(SSH_MSG_KEXINIT, StateList() << SocketConnected
|
||||||
|
<< ConnectionEstablished, &This::handleKeyExchangeInitPacket);
|
||||||
|
setupPacketHandler(SSH_MSG_KEXDH_REPLY, StateList() << SocketConnected
|
||||||
|
<< ConnectionEstablished, &This::handleKeyExchangeReplyPacket);
|
||||||
|
|
||||||
|
setupPacketHandler(SSH_MSG_NEWKEYS, StateList() << SocketConnected
|
||||||
|
<< ConnectionEstablished, &This::handleNewKeysPacket);
|
||||||
|
setupPacketHandler(SSH_MSG_SERVICE_ACCEPT,
|
||||||
|
StateList() << UserAuthServiceRequested,
|
||||||
|
&This::handleServiceAcceptPacket);
|
||||||
|
if (m_connParams.authenticationType == SshConnectionParameters::AuthenticationTypePassword
|
||||||
|
|| m_connParams.authenticationType == SshConnectionParameters::AuthenticationTypeTryAllPasswordBasedMethods) {
|
||||||
|
setupPacketHandler(SSH_MSG_USERAUTH_PASSWD_CHANGEREQ,
|
||||||
|
StateList() << UserAuthRequested, &This::handlePasswordExpiredPacket);
|
||||||
|
}
|
||||||
|
setupPacketHandler(SSH_MSG_GLOBAL_REQUEST,
|
||||||
|
StateList() << ConnectionEstablished, &This::handleGlobalRequest);
|
||||||
|
|
||||||
|
const StateList authReqList = StateList() << UserAuthRequested;
|
||||||
|
setupPacketHandler(SSH_MSG_USERAUTH_BANNER, authReqList,
|
||||||
|
&This::handleUserAuthBannerPacket);
|
||||||
|
setupPacketHandler(SSH_MSG_USERAUTH_SUCCESS, authReqList,
|
||||||
|
&This::handleUserAuthSuccessPacket);
|
||||||
|
setupPacketHandler(SSH_MSG_USERAUTH_FAILURE, authReqList,
|
||||||
|
&This::handleUserAuthFailurePacket);
|
||||||
|
if (m_connParams.authenticationType == SshConnectionParameters::AuthenticationTypeKeyboardInteractive
|
||||||
|
|| m_connParams.authenticationType == SshConnectionParameters::AuthenticationTypeTryAllPasswordBasedMethods) {
|
||||||
|
setupPacketHandler(SSH_MSG_USERAUTH_INFO_REQUEST, authReqList,
|
||||||
|
&This::handleUserAuthInfoRequestPacket);
|
||||||
|
}
|
||||||
|
|
||||||
|
const StateList connectedList
|
||||||
|
= StateList() << ConnectionEstablished;
|
||||||
|
setupPacketHandler(SSH_MSG_CHANNEL_REQUEST, connectedList,
|
||||||
|
&This::handleChannelRequest);
|
||||||
|
setupPacketHandler(SSH_MSG_CHANNEL_OPEN, connectedList,
|
||||||
|
&This::handleChannelOpen);
|
||||||
|
setupPacketHandler(SSH_MSG_CHANNEL_OPEN_FAILURE, connectedList,
|
||||||
|
&This::handleChannelOpenFailure);
|
||||||
|
setupPacketHandler(SSH_MSG_CHANNEL_OPEN_CONFIRMATION, connectedList,
|
||||||
|
&This::handleChannelOpenConfirmation);
|
||||||
|
setupPacketHandler(SSH_MSG_CHANNEL_SUCCESS, connectedList,
|
||||||
|
&This::handleChannelSuccess);
|
||||||
|
setupPacketHandler(SSH_MSG_CHANNEL_FAILURE, connectedList,
|
||||||
|
&This::handleChannelFailure);
|
||||||
|
setupPacketHandler(SSH_MSG_CHANNEL_WINDOW_ADJUST, connectedList,
|
||||||
|
&This::handleChannelWindowAdjust);
|
||||||
|
setupPacketHandler(SSH_MSG_CHANNEL_DATA, connectedList,
|
||||||
|
&This::handleChannelData);
|
||||||
|
setupPacketHandler(SSH_MSG_CHANNEL_EXTENDED_DATA, connectedList,
|
||||||
|
&This::handleChannelExtendedData);
|
||||||
|
|
||||||
|
const StateList connectedOrClosedList
|
||||||
|
= StateList() << SocketUnconnected << ConnectionEstablished;
|
||||||
|
setupPacketHandler(SSH_MSG_CHANNEL_EOF, connectedOrClosedList,
|
||||||
|
&This::handleChannelEof);
|
||||||
|
setupPacketHandler(SSH_MSG_CHANNEL_CLOSE, connectedOrClosedList,
|
||||||
|
&This::handleChannelClose);
|
||||||
|
|
||||||
|
setupPacketHandler(SSH_MSG_DISCONNECT, StateList() << SocketConnected
|
||||||
|
<< UserAuthServiceRequested << UserAuthRequested
|
||||||
|
<< ConnectionEstablished, &This::handleDisconnect);
|
||||||
|
|
||||||
|
setupPacketHandler(SSH_MSG_UNIMPLEMENTED,
|
||||||
|
StateList() << ConnectionEstablished, &This::handleUnimplementedPacket);
|
||||||
|
|
||||||
|
setupPacketHandler(SSH_MSG_REQUEST_SUCCESS, connectedList,
|
||||||
|
&This::handleRequestSuccess);
|
||||||
|
setupPacketHandler(SSH_MSG_REQUEST_FAILURE, connectedList,
|
||||||
|
&This::handleRequestFailure);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshConnectionPrivate::setupPacketHandler(SshPacketType type,
|
||||||
|
const SshConnectionPrivate::StateList &states,
|
||||||
|
SshConnectionPrivate::PacketHandler handler)
|
||||||
|
{
|
||||||
|
m_packetHandlers.insert(type, HandlerInStates(states, handler));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshConnectionPrivate::handleSocketConnected()
|
||||||
|
{
|
||||||
|
m_state = SocketConnected;
|
||||||
|
sendData(ClientId);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshConnectionPrivate::handleIncomingData()
|
||||||
|
{
|
||||||
|
if (m_state == SocketUnconnected)
|
||||||
|
return; // For stuff queued in the event loop after we've called closeConnection();
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (!canUseSocket())
|
||||||
|
return;
|
||||||
|
m_incomingData += m_socket->readAll();
|
||||||
|
qCDebug(sshLog, "state = %d, remote data size = %d", m_state, m_incomingData.count());
|
||||||
|
if (m_serverId.isEmpty())
|
||||||
|
handleServerId();
|
||||||
|
handlePackets();
|
||||||
|
} catch (const SshServerException &e) {
|
||||||
|
closeConnection(e.error, SshProtocolError, e.errorStringServer,
|
||||||
|
tr("SSH Protocol error: %1").arg(e.errorStringUser));
|
||||||
|
} catch (const SshClientException &e) {
|
||||||
|
closeConnection(SSH_DISCONNECT_BY_APPLICATION, e.error, "",
|
||||||
|
e.errorString);
|
||||||
|
} catch (const std::exception &e) {
|
||||||
|
closeConnection(SSH_DISCONNECT_BY_APPLICATION, SshInternalError, "",
|
||||||
|
tr("Botan library exception: %1").arg(QString::fromLatin1(e.what())));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RFC 4253, 4.2.
|
||||||
|
void SshConnectionPrivate::handleServerId()
|
||||||
|
{
|
||||||
|
qCDebug(sshLog, "%s: incoming data size = %d, incoming data = '%s'",
|
||||||
|
Q_FUNC_INFO, m_incomingData.count(), m_incomingData.data());
|
||||||
|
const int newLinePos = m_incomingData.indexOf('\n');
|
||||||
|
if (newLinePos == -1)
|
||||||
|
return; // Not enough data yet.
|
||||||
|
|
||||||
|
// Lines not starting with "SSH-" are ignored.
|
||||||
|
if (!m_incomingData.startsWith("SSH-")) {
|
||||||
|
m_incomingData.remove(0, newLinePos + 1);
|
||||||
|
m_serverHasSentDataBeforeId = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newLinePos > 255 - 1) {
|
||||||
|
throw SshServerException(SSH_DISCONNECT_PROTOCOL_ERROR,
|
||||||
|
"Identification string too long.",
|
||||||
|
tr("Server identification string is %n characters long, but the maximum "
|
||||||
|
"allowed length is 255.", 0, newLinePos + 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool hasCarriageReturn = m_incomingData.at(newLinePos - 1) == '\r';
|
||||||
|
m_serverId = m_incomingData.left(newLinePos);
|
||||||
|
if (hasCarriageReturn)
|
||||||
|
m_serverId.chop(1);
|
||||||
|
m_incomingData.remove(0, newLinePos + 1);
|
||||||
|
|
||||||
|
if (m_serverId.contains('\0')) {
|
||||||
|
throw SshServerException(SSH_DISCONNECT_PROTOCOL_ERROR,
|
||||||
|
"Identification string contains illegal NUL character.",
|
||||||
|
tr("Server identification string contains illegal NUL character."));
|
||||||
|
}
|
||||||
|
|
||||||
|
// "printable US-ASCII characters, with the exception of whitespace characters
|
||||||
|
// and the minus sign"
|
||||||
|
QString legalString = QLatin1String("[]!\"#$!&'()*+,./0-9:;<=>?@A-Z[\\\\^_`a-z{|}~]+");
|
||||||
|
const QRegExp versionIdpattern(QString::fromLatin1("SSH-(%1)-%1(?: .+)?").arg(legalString));
|
||||||
|
if (!versionIdpattern.exactMatch(QString::fromLatin1(m_serverId))) {
|
||||||
|
throw SshServerException(SSH_DISCONNECT_PROTOCOL_ERROR,
|
||||||
|
"Identification string is invalid.",
|
||||||
|
tr("Server Identification string \"%1\" is invalid.")
|
||||||
|
.arg(QString::fromLatin1(m_serverId)));
|
||||||
|
}
|
||||||
|
const QString serverProtoVersion = versionIdpattern.cap(1);
|
||||||
|
if (serverProtoVersion != QLatin1String("2.0") && serverProtoVersion != QLatin1String("1.99")) {
|
||||||
|
throw SshServerException(SSH_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED,
|
||||||
|
"Invalid protocol version.",
|
||||||
|
tr("Server protocol version is \"%1\", but needs to be 2.0 or 1.99.")
|
||||||
|
.arg(serverProtoVersion));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_connParams.options & SshEnableStrictConformanceChecks) {
|
||||||
|
if (serverProtoVersion == QLatin1String("2.0") && !hasCarriageReturn) {
|
||||||
|
throw SshServerException(SSH_DISCONNECT_PROTOCOL_ERROR,
|
||||||
|
"Identification string is invalid.",
|
||||||
|
tr("Server identification string is invalid (missing carriage return)."));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (serverProtoVersion == QLatin1String("1.99") && m_serverHasSentDataBeforeId) {
|
||||||
|
throw SshServerException(SSH_DISCONNECT_PROTOCOL_ERROR,
|
||||||
|
"No extra data preceding identification string allowed for 1.99.",
|
||||||
|
tr("Server reports protocol version 1.99, but sends data "
|
||||||
|
"before the identification string, which is not allowed."));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_keyExchange.reset(new SshKeyExchange(m_connParams, m_sendFacility));
|
||||||
|
m_keyExchange->sendKexInitPacket(m_serverId);
|
||||||
|
m_keyExchangeState = KexInitSent;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshConnectionPrivate::handlePackets()
|
||||||
|
{
|
||||||
|
m_incomingPacket.consumeData(m_incomingData);
|
||||||
|
while (m_incomingPacket.isComplete()) {
|
||||||
|
handleCurrentPacket();
|
||||||
|
m_incomingPacket.clear();
|
||||||
|
m_incomingPacket.consumeData(m_incomingData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshConnectionPrivate::handleCurrentPacket()
|
||||||
|
{
|
||||||
|
Q_ASSERT(m_incomingPacket.isComplete());
|
||||||
|
Q_ASSERT(m_keyExchangeState == DhInitSent || !m_ignoreNextPacket);
|
||||||
|
|
||||||
|
if (m_ignoreNextPacket) {
|
||||||
|
m_ignoreNextPacket = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QHash<SshPacketType, HandlerInStates>::ConstIterator it
|
||||||
|
= m_packetHandlers.constFind(m_incomingPacket.type());
|
||||||
|
if (it == m_packetHandlers.constEnd()) {
|
||||||
|
m_sendFacility.sendMsgUnimplementedPacket(m_incomingPacket.serverSeqNr());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!it.value().first.contains(m_state)) {
|
||||||
|
handleUnexpectedPacket();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
(this->*it.value().second)();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshConnectionPrivate::handleKeyExchangeInitPacket()
|
||||||
|
{
|
||||||
|
if (m_keyExchangeState != NoKeyExchange
|
||||||
|
&& m_keyExchangeState != KexInitSent) {
|
||||||
|
throw SshServerException(SSH_DISCONNECT_PROTOCOL_ERROR,
|
||||||
|
"Unexpected packet.", tr("Unexpected packet of type %1.")
|
||||||
|
.arg(m_incomingPacket.type()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Server-initiated re-exchange.
|
||||||
|
if (m_keyExchangeState == NoKeyExchange) {
|
||||||
|
m_keyExchange.reset(new SshKeyExchange(m_connParams, m_sendFacility));
|
||||||
|
m_keyExchange->sendKexInitPacket(m_serverId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the server sends a guessed packet, the guess must be wrong,
|
||||||
|
// because the algorithms we support require us to initiate the
|
||||||
|
// key exchange.
|
||||||
|
if (m_keyExchange->sendDhInitPacket(m_incomingPacket))
|
||||||
|
m_ignoreNextPacket = true;
|
||||||
|
|
||||||
|
m_keyExchangeState = DhInitSent;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshConnectionPrivate::handleKeyExchangeReplyPacket()
|
||||||
|
{
|
||||||
|
if (m_keyExchangeState != DhInitSent) {
|
||||||
|
throw SshServerException(SSH_DISCONNECT_PROTOCOL_ERROR,
|
||||||
|
"Unexpected packet.", tr("Unexpected packet of type %1.")
|
||||||
|
.arg(m_incomingPacket.type()));
|
||||||
|
}
|
||||||
|
|
||||||
|
m_keyExchange->sendNewKeysPacket(m_incomingPacket,
|
||||||
|
ClientId.left(ClientId.size() - 2));
|
||||||
|
m_sendFacility.recreateKeys(*m_keyExchange);
|
||||||
|
m_keyExchangeState = NewKeysSent;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshConnectionPrivate::handleNewKeysPacket()
|
||||||
|
{
|
||||||
|
if (m_keyExchangeState != NewKeysSent) {
|
||||||
|
throw SshServerException(SSH_DISCONNECT_PROTOCOL_ERROR,
|
||||||
|
"Unexpected packet.", tr("Unexpected packet of type %1.")
|
||||||
|
.arg(m_incomingPacket.type()));
|
||||||
|
}
|
||||||
|
|
||||||
|
m_incomingPacket.recreateKeys(*m_keyExchange);
|
||||||
|
m_keyExchange.reset();
|
||||||
|
m_keyExchangeState = NoKeyExchange;
|
||||||
|
|
||||||
|
if (m_state == SocketConnected) {
|
||||||
|
m_sendFacility.sendUserAuthServiceRequestPacket();
|
||||||
|
m_state = UserAuthServiceRequested;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshConnectionPrivate::handleServiceAcceptPacket()
|
||||||
|
{
|
||||||
|
switch (m_connParams.authenticationType) {
|
||||||
|
case SshConnectionParameters::AuthenticationTypeTryAllPasswordBasedMethods:
|
||||||
|
m_triedAllPasswordBasedMethods = false;
|
||||||
|
// Fall-through.
|
||||||
|
case SshConnectionParameters::AuthenticationTypePassword:
|
||||||
|
m_sendFacility.sendUserAuthByPasswordRequestPacket(m_connParams.userName.toUtf8(),
|
||||||
|
SshCapabilities::SshConnectionService, m_connParams.password.toUtf8());
|
||||||
|
break;
|
||||||
|
case SshConnectionParameters::AuthenticationTypeKeyboardInteractive:
|
||||||
|
m_sendFacility.sendUserAuthByKeyboardInteractiveRequestPacket(m_connParams.userName.toUtf8(),
|
||||||
|
SshCapabilities::SshConnectionService);
|
||||||
|
break;
|
||||||
|
case SshConnectionParameters::AuthenticationTypePublicKey:
|
||||||
|
m_sendFacility.sendUserAuthByPublicKeyRequestPacket(m_connParams.userName.toUtf8(),
|
||||||
|
SshCapabilities::SshConnectionService);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
m_state = UserAuthRequested;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshConnectionPrivate::handlePasswordExpiredPacket()
|
||||||
|
{
|
||||||
|
if (m_connParams.authenticationType == SshConnectionParameters::AuthenticationTypeTryAllPasswordBasedMethods
|
||||||
|
&& m_triedAllPasswordBasedMethods) {
|
||||||
|
// This means we just tried to authorize via "keyboard-interactive", in which case
|
||||||
|
// this type of packet is not allowed.
|
||||||
|
handleUnexpectedPacket();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
throw SshClientException(SshAuthenticationError, tr("Password expired."));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshConnectionPrivate::handleUserAuthInfoRequestPacket()
|
||||||
|
{
|
||||||
|
if (m_connParams.authenticationType == SshConnectionParameters::AuthenticationTypeTryAllPasswordBasedMethods
|
||||||
|
&& !m_triedAllPasswordBasedMethods) {
|
||||||
|
// This means we just tried to authorize via "password", in which case
|
||||||
|
// this type of packet is not allowed.
|
||||||
|
handleUnexpectedPacket();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const SshUserAuthInfoRequestPacket requestPacket
|
||||||
|
= m_incomingPacket.extractUserAuthInfoRequest();
|
||||||
|
QStringList responses;
|
||||||
|
responses.reserve(requestPacket.prompts.count());
|
||||||
|
|
||||||
|
// Not very interactive, admittedly, but we don't want to be for now.
|
||||||
|
for (int i = 0; i < requestPacket.prompts.count(); ++i)
|
||||||
|
responses << m_connParams.password;
|
||||||
|
m_sendFacility.sendUserAuthInfoResponsePacket(responses);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshConnectionPrivate::handleUserAuthBannerPacket()
|
||||||
|
{
|
||||||
|
emit dataAvailable(m_incomingPacket.extractUserAuthBanner().message);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshConnectionPrivate::handleUnexpectedPacket()
|
||||||
|
{
|
||||||
|
throw SshServerException(SSH_DISCONNECT_PROTOCOL_ERROR,
|
||||||
|
"Unexpected packet.", tr("Unexpected packet of type %1.")
|
||||||
|
.arg(m_incomingPacket.type()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshConnectionPrivate::handleGlobalRequest()
|
||||||
|
{
|
||||||
|
m_sendFacility.sendRequestFailurePacket();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshConnectionPrivate::handleUserAuthSuccessPacket()
|
||||||
|
{
|
||||||
|
m_state = ConnectionEstablished;
|
||||||
|
m_timeoutTimer.stop();
|
||||||
|
emit connected();
|
||||||
|
m_lastInvalidMsgSeqNr = InvalidSeqNr;
|
||||||
|
connect(&m_keepAliveTimer, &QTimer::timeout, this, &SshConnectionPrivate::sendKeepAlivePacket);
|
||||||
|
m_keepAliveTimer.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshConnectionPrivate::handleUserAuthFailurePacket()
|
||||||
|
{
|
||||||
|
// TODO: Evaluate "authentications that can continue" field and act on it.
|
||||||
|
if (m_connParams.authenticationType
|
||||||
|
== SshConnectionParameters::AuthenticationTypeTryAllPasswordBasedMethods
|
||||||
|
&& !m_triedAllPasswordBasedMethods) {
|
||||||
|
m_triedAllPasswordBasedMethods = true;
|
||||||
|
m_sendFacility.sendUserAuthByKeyboardInteractiveRequestPacket(
|
||||||
|
m_connParams.userName.toUtf8(),
|
||||||
|
SshCapabilities::SshConnectionService);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_timeoutTimer.stop();
|
||||||
|
const QString errorMsg = m_connParams.authenticationType == SshConnectionParameters::AuthenticationTypePublicKey
|
||||||
|
? tr("Server rejected key.") : tr("Server rejected password.");
|
||||||
|
throw SshClientException(SshAuthenticationError, errorMsg);
|
||||||
|
}
|
||||||
|
void SshConnectionPrivate::handleDebugPacket()
|
||||||
|
{
|
||||||
|
const SshDebug &msg = m_incomingPacket.extractDebug();
|
||||||
|
if (msg.display)
|
||||||
|
emit dataAvailable(msg.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshConnectionPrivate::handleUnimplementedPacket()
|
||||||
|
{
|
||||||
|
const SshUnimplemented &msg = m_incomingPacket.extractUnimplemented();
|
||||||
|
if (msg.invalidMsgSeqNr != m_lastInvalidMsgSeqNr) {
|
||||||
|
throw SshServerException(SSH_DISCONNECT_PROTOCOL_ERROR,
|
||||||
|
"Unexpected packet", tr("The server sent an unexpected SSH packet "
|
||||||
|
"of type SSH_MSG_UNIMPLEMENTED."));
|
||||||
|
}
|
||||||
|
m_lastInvalidMsgSeqNr = InvalidSeqNr;
|
||||||
|
m_timeoutTimer.stop();
|
||||||
|
m_keepAliveTimer.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshConnectionPrivate::handleChannelRequest()
|
||||||
|
{
|
||||||
|
m_channelManager->handleChannelRequest(m_incomingPacket);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshConnectionPrivate::handleChannelOpen()
|
||||||
|
{
|
||||||
|
m_channelManager->handleChannelOpen(m_incomingPacket);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshConnectionPrivate::handleChannelOpenFailure()
|
||||||
|
{
|
||||||
|
m_channelManager->handleChannelOpenFailure(m_incomingPacket);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshConnectionPrivate::handleChannelOpenConfirmation()
|
||||||
|
{
|
||||||
|
m_channelManager->handleChannelOpenConfirmation(m_incomingPacket);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshConnectionPrivate::handleChannelSuccess()
|
||||||
|
{
|
||||||
|
m_channelManager->handleChannelSuccess(m_incomingPacket);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshConnectionPrivate::handleChannelFailure()
|
||||||
|
{
|
||||||
|
m_channelManager->handleChannelFailure(m_incomingPacket);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshConnectionPrivate::handleChannelWindowAdjust()
|
||||||
|
{
|
||||||
|
m_channelManager->handleChannelWindowAdjust(m_incomingPacket);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshConnectionPrivate::handleChannelData()
|
||||||
|
{
|
||||||
|
m_channelManager->handleChannelData(m_incomingPacket);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshConnectionPrivate::handleChannelExtendedData()
|
||||||
|
{
|
||||||
|
m_channelManager->handleChannelExtendedData(m_incomingPacket);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshConnectionPrivate::handleChannelEof()
|
||||||
|
{
|
||||||
|
m_channelManager->handleChannelEof(m_incomingPacket);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshConnectionPrivate::handleChannelClose()
|
||||||
|
{
|
||||||
|
m_channelManager->handleChannelClose(m_incomingPacket);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshConnectionPrivate::handleDisconnect()
|
||||||
|
{
|
||||||
|
const SshDisconnect msg = m_incomingPacket.extractDisconnect();
|
||||||
|
throw SshServerException(SSH_DISCONNECT_CONNECTION_LOST,
|
||||||
|
"", tr("Server closed connection: %1").arg(msg.description));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshConnectionPrivate::handleRequestSuccess()
|
||||||
|
{
|
||||||
|
m_channelManager->handleRequestSuccess(m_incomingPacket);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshConnectionPrivate::handleRequestFailure()
|
||||||
|
{
|
||||||
|
m_channelManager->handleRequestFailure(m_incomingPacket);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshConnectionPrivate::sendData(const QByteArray &data)
|
||||||
|
{
|
||||||
|
if (canUseSocket())
|
||||||
|
m_socket->write(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshConnectionPrivate::handleSocketDisconnected()
|
||||||
|
{
|
||||||
|
closeConnection(SSH_DISCONNECT_CONNECTION_LOST, SshClosedByServerError,
|
||||||
|
"Connection closed unexpectedly.",
|
||||||
|
tr("Connection closed unexpectedly."));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshConnectionPrivate::handleSocketError()
|
||||||
|
{
|
||||||
|
if (m_error == SshNoError) {
|
||||||
|
closeConnection(SSH_DISCONNECT_CONNECTION_LOST, SshSocketError,
|
||||||
|
"Network error", m_socket->errorString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshConnectionPrivate::handleTimeout()
|
||||||
|
{
|
||||||
|
closeConnection(SSH_DISCONNECT_BY_APPLICATION, SshTimeoutError, "",
|
||||||
|
tr("Timeout waiting for reply from server."));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshConnectionPrivate::sendKeepAlivePacket()
|
||||||
|
{
|
||||||
|
// This type of message is not allowed during key exchange.
|
||||||
|
if (m_keyExchangeState != NoKeyExchange) {
|
||||||
|
m_keepAliveTimer.start();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_ASSERT(m_lastInvalidMsgSeqNr == InvalidSeqNr);
|
||||||
|
m_lastInvalidMsgSeqNr = m_sendFacility.nextClientSeqNr();
|
||||||
|
m_sendFacility.sendInvalidPacket();
|
||||||
|
m_timeoutTimer.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshConnectionPrivate::connectToHost()
|
||||||
|
{
|
||||||
|
QSSH_ASSERT_AND_RETURN(m_state == SocketUnconnected);
|
||||||
|
|
||||||
|
m_incomingData.clear();
|
||||||
|
m_incomingPacket.reset();
|
||||||
|
m_sendFacility.reset();
|
||||||
|
m_error = SshNoError;
|
||||||
|
m_ignoreNextPacket = false;
|
||||||
|
m_errorString.clear();
|
||||||
|
m_serverId.clear();
|
||||||
|
m_serverHasSentDataBeforeId = false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (m_connParams.authenticationType == SshConnectionParameters::AuthenticationTypePublicKey)
|
||||||
|
createPrivateKey();
|
||||||
|
} catch (const SshClientException &ex) {
|
||||||
|
m_error = ex.error;
|
||||||
|
m_errorString = ex.errorString;
|
||||||
|
emit error(m_error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
connect(m_socket, &QAbstractSocket::connected,
|
||||||
|
this, &SshConnectionPrivate::handleSocketConnected);
|
||||||
|
connect(m_socket, &QIODevice::readyRead,
|
||||||
|
this, &SshConnectionPrivate::handleIncomingData);
|
||||||
|
connect(m_socket,
|
||||||
|
static_cast<void (QAbstractSocket::*)(QAbstractSocket::SocketError)>(&QAbstractSocket::error),
|
||||||
|
this, &SshConnectionPrivate::handleSocketError);
|
||||||
|
connect(m_socket, &QAbstractSocket::disconnected,
|
||||||
|
this, &SshConnectionPrivate::handleSocketDisconnected);
|
||||||
|
connect(&m_timeoutTimer, &QTimer::timeout, this, &SshConnectionPrivate::handleTimeout);
|
||||||
|
m_state = SocketConnecting;
|
||||||
|
m_keyExchangeState = NoKeyExchange;
|
||||||
|
m_timeoutTimer.start();
|
||||||
|
m_socket->connectToHost(m_connParams.host, m_connParams.port);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshConnectionPrivate::closeConnection(SshErrorCode sshError,
|
||||||
|
SshError userError, const QByteArray &serverErrorString,
|
||||||
|
const QString &userErrorString)
|
||||||
|
{
|
||||||
|
// Prevent endless loops by recursive exceptions.
|
||||||
|
if (m_state == SocketUnconnected || m_error != SshNoError)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_error = userError;
|
||||||
|
m_errorString = userErrorString;
|
||||||
|
m_timeoutTimer.stop();
|
||||||
|
disconnect(m_socket, 0, this, 0);
|
||||||
|
disconnect(&m_timeoutTimer, 0, this, 0);
|
||||||
|
m_keepAliveTimer.stop();
|
||||||
|
disconnect(&m_keepAliveTimer, 0, this, 0);
|
||||||
|
try {
|
||||||
|
m_channelManager->closeAllChannels(SshChannelManager::CloseAllAndReset);
|
||||||
|
m_sendFacility.sendDisconnectPacket(sshError, serverErrorString);
|
||||||
|
} catch (...) {} // Nothing sensible to be done here.
|
||||||
|
if (m_error != SshNoError)
|
||||||
|
emit error(userError);
|
||||||
|
if (m_state == ConnectionEstablished)
|
||||||
|
emit disconnected();
|
||||||
|
if (canUseSocket())
|
||||||
|
m_socket->disconnectFromHost();
|
||||||
|
m_state = SocketUnconnected;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SshConnectionPrivate::canUseSocket() const
|
||||||
|
{
|
||||||
|
return m_socket->isValid()
|
||||||
|
&& m_socket->state() == QAbstractSocket::ConnectedState;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshConnectionPrivate::createPrivateKey()
|
||||||
|
{
|
||||||
|
if (m_connParams.privateKeyFile.isEmpty())
|
||||||
|
throw SshClientException(SshKeyFileError, tr("No private key file given."));
|
||||||
|
QFile keyFile(m_connParams.privateKeyFile);
|
||||||
|
if (!keyFile.open(QIODevice::ReadOnly)) {
|
||||||
|
throw SshClientException(SshKeyFileError,
|
||||||
|
tr("Private key file error: %1").arg(keyFile.errorString()));
|
||||||
|
}
|
||||||
|
m_sendFacility.createAuthenticationKey(keyFile.readAll());
|
||||||
|
}
|
||||||
|
|
||||||
|
QSharedPointer<SshRemoteProcess> SshConnectionPrivate::createRemoteProcess(const QByteArray &command)
|
||||||
|
{
|
||||||
|
return m_channelManager->createRemoteProcess(command);
|
||||||
|
}
|
||||||
|
|
||||||
|
QSharedPointer<SshRemoteProcess> SshConnectionPrivate::createRemoteShell()
|
||||||
|
{
|
||||||
|
return m_channelManager->createRemoteShell();
|
||||||
|
}
|
||||||
|
|
||||||
|
QSharedPointer<SftpChannel> SshConnectionPrivate::createSftpChannel()
|
||||||
|
{
|
||||||
|
return m_channelManager->createSftpChannel();
|
||||||
|
}
|
||||||
|
|
||||||
|
SshDirectTcpIpTunnel::Ptr SshConnectionPrivate::createDirectTunnel(const QString &originatingHost,
|
||||||
|
quint16 originatingPort, const QString &remoteHost, quint16 remotePort)
|
||||||
|
{
|
||||||
|
return m_channelManager->createDirectTunnel(originatingHost, originatingPort, remoteHost,
|
||||||
|
remotePort);
|
||||||
|
}
|
||||||
|
|
||||||
|
SshTcpIpForwardServer::Ptr SshConnectionPrivate::createForwardServer(const QString &bindAddress,
|
||||||
|
quint16 bindPort)
|
||||||
|
{
|
||||||
|
return m_channelManager->createForwardServer(bindAddress, bindPort);
|
||||||
|
}
|
||||||
|
|
||||||
|
const quint64 SshConnectionPrivate::InvalidSeqNr = static_cast<quint64>(-1);
|
||||||
|
|
||||||
|
} // namespace Internal
|
||||||
|
} // namespace QSsh
|
||||||
145
client/3rd/QtSsh/src/ssh/sshconnection.h
Normal file
145
client/3rd/QtSsh/src/ssh/sshconnection.h
Normal file
|
|
@ -0,0 +1,145 @@
|
||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2016 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "ssherrors.h"
|
||||||
|
#include "sshhostkeydatabase.h"
|
||||||
|
|
||||||
|
#include "ssh_global.h"
|
||||||
|
|
||||||
|
#include <QByteArray>
|
||||||
|
#include <QFlags>
|
||||||
|
#include <QObject>
|
||||||
|
#include <QSharedPointer>
|
||||||
|
#include <QString>
|
||||||
|
#include <QHostAddress>
|
||||||
|
|
||||||
|
namespace QSsh {
|
||||||
|
class SftpChannel;
|
||||||
|
class SshDirectTcpIpTunnel;
|
||||||
|
class SshRemoteProcess;
|
||||||
|
class SshTcpIpForwardServer;
|
||||||
|
|
||||||
|
namespace Internal { class SshConnectionPrivate; }
|
||||||
|
|
||||||
|
enum SshConnectionOption {
|
||||||
|
SshIgnoreDefaultProxy = 0x1,
|
||||||
|
SshEnableStrictConformanceChecks = 0x2
|
||||||
|
};
|
||||||
|
|
||||||
|
Q_DECLARE_FLAGS(SshConnectionOptions, SshConnectionOption)
|
||||||
|
|
||||||
|
enum SshHostKeyCheckingMode {
|
||||||
|
SshHostKeyCheckingNone,
|
||||||
|
SshHostKeyCheckingStrict,
|
||||||
|
SshHostKeyCheckingAllowNoMatch,
|
||||||
|
SshHostKeyCheckingAllowMismatch
|
||||||
|
};
|
||||||
|
|
||||||
|
class QSSH_EXPORT SshConnectionParameters
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum AuthenticationType {
|
||||||
|
AuthenticationTypePassword,
|
||||||
|
AuthenticationTypePublicKey,
|
||||||
|
AuthenticationTypeKeyboardInteractive,
|
||||||
|
|
||||||
|
// Some servers disable "password", others disable "keyboard-interactive".
|
||||||
|
AuthenticationTypeTryAllPasswordBasedMethods
|
||||||
|
};
|
||||||
|
|
||||||
|
SshConnectionParameters();
|
||||||
|
|
||||||
|
QString host;
|
||||||
|
QString userName;
|
||||||
|
QString password;
|
||||||
|
QString privateKeyFile;
|
||||||
|
int timeout; // In seconds.
|
||||||
|
AuthenticationType authenticationType;
|
||||||
|
quint16 port;
|
||||||
|
SshConnectionOptions options;
|
||||||
|
SshHostKeyCheckingMode hostKeyCheckingMode;
|
||||||
|
SshHostKeyDatabasePtr hostKeyDatabase;
|
||||||
|
};
|
||||||
|
|
||||||
|
QSSH_EXPORT bool operator==(const SshConnectionParameters &p1, const SshConnectionParameters &p2);
|
||||||
|
QSSH_EXPORT bool operator!=(const SshConnectionParameters &p1, const SshConnectionParameters &p2);
|
||||||
|
|
||||||
|
class QSSH_EXPORT SshConnectionInfo
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SshConnectionInfo() : localPort(0), peerPort(0) {}
|
||||||
|
SshConnectionInfo(const QHostAddress &la, quint16 lp, const QHostAddress &pa, quint16 pp)
|
||||||
|
: localAddress(la), localPort(lp), peerAddress(pa), peerPort(pp) {}
|
||||||
|
|
||||||
|
QHostAddress localAddress;
|
||||||
|
quint16 localPort;
|
||||||
|
QHostAddress peerAddress;
|
||||||
|
quint16 peerPort;
|
||||||
|
};
|
||||||
|
|
||||||
|
class QSSH_EXPORT SshConnection : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
enum State { Unconnected, Connecting, Connected };
|
||||||
|
|
||||||
|
explicit SshConnection(const SshConnectionParameters &serverInfo, QObject *parent = 0);
|
||||||
|
|
||||||
|
void connectToHost();
|
||||||
|
void disconnectFromHost();
|
||||||
|
State state() const;
|
||||||
|
SshError errorState() const;
|
||||||
|
QString errorString() const;
|
||||||
|
SshConnectionParameters connectionParameters() const;
|
||||||
|
SshConnectionInfo connectionInfo() const;
|
||||||
|
~SshConnection();
|
||||||
|
|
||||||
|
QSharedPointer<SshRemoteProcess> createRemoteProcess(const QByteArray &command);
|
||||||
|
QSharedPointer<SshRemoteProcess> createRemoteShell();
|
||||||
|
QSharedPointer<SftpChannel> createSftpChannel();
|
||||||
|
QSharedPointer<SshDirectTcpIpTunnel> createDirectTunnel(const QString &originatingHost,
|
||||||
|
quint16 originatingPort, const QString &remoteHost, quint16 remotePort);
|
||||||
|
QSharedPointer<SshTcpIpForwardServer> createForwardServer(const QString &remoteHost,
|
||||||
|
quint16 remotePort);
|
||||||
|
|
||||||
|
// -1 if an error occurred, number of channels closed otherwise.
|
||||||
|
int closeAllChannels();
|
||||||
|
|
||||||
|
int channelCount() const;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void connected();
|
||||||
|
void disconnected();
|
||||||
|
void dataAvailable(const QString &message);
|
||||||
|
void error(QSsh::SshError);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Internal::SshConnectionPrivate *d;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace QSsh
|
||||||
179
client/3rd/QtSsh/src/ssh/sshconnection_p.h
Normal file
179
client/3rd/QtSsh/src/ssh/sshconnection_p.h
Normal file
|
|
@ -0,0 +1,179 @@
|
||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2016 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "sshconnection.h"
|
||||||
|
#include "sshexception_p.h"
|
||||||
|
#include "sshincomingpacket_p.h"
|
||||||
|
#include "sshsendfacility_p.h"
|
||||||
|
|
||||||
|
#include <QHash>
|
||||||
|
#include <QList>
|
||||||
|
#include <QObject>
|
||||||
|
#include <QPair>
|
||||||
|
#include <QScopedPointer>
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
|
QT_BEGIN_NAMESPACE
|
||||||
|
class QTcpSocket;
|
||||||
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
|
namespace QSsh {
|
||||||
|
class SftpChannel;
|
||||||
|
class SshRemoteProcess;
|
||||||
|
class SshDirectTcpIpTunnel;
|
||||||
|
class SshTcpIpForwardServer;
|
||||||
|
|
||||||
|
namespace Internal {
|
||||||
|
class SshChannelManager;
|
||||||
|
|
||||||
|
// NOTE: When you add stuff here, don't forget to update m_packetHandlers.
|
||||||
|
enum SshStateInternal {
|
||||||
|
SocketUnconnected, // initial and after disconnect
|
||||||
|
SocketConnecting, // After connectToHost()
|
||||||
|
SocketConnected, // After socket's connected() signal
|
||||||
|
UserAuthServiceRequested,
|
||||||
|
UserAuthRequested,
|
||||||
|
ConnectionEstablished // After service has been started
|
||||||
|
// ...
|
||||||
|
};
|
||||||
|
|
||||||
|
enum SshKeyExchangeState {
|
||||||
|
NoKeyExchange,
|
||||||
|
KexInitSent,
|
||||||
|
DhInitSent,
|
||||||
|
NewKeysSent,
|
||||||
|
KeyExchangeSuccess // After server's DH_REPLY message
|
||||||
|
};
|
||||||
|
|
||||||
|
class SshConnectionPrivate : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
friend class QSsh::SshConnection;
|
||||||
|
public:
|
||||||
|
SshConnectionPrivate(SshConnection *conn,
|
||||||
|
const SshConnectionParameters &serverInfo);
|
||||||
|
~SshConnectionPrivate();
|
||||||
|
|
||||||
|
void connectToHost();
|
||||||
|
void closeConnection(SshErrorCode sshError, SshError userError,
|
||||||
|
const QByteArray &serverErrorString, const QString &userErrorString);
|
||||||
|
QSharedPointer<SshRemoteProcess> createRemoteProcess(const QByteArray &command);
|
||||||
|
QSharedPointer<SshRemoteProcess> createRemoteShell();
|
||||||
|
QSharedPointer<SftpChannel> createSftpChannel();
|
||||||
|
QSharedPointer<SshDirectTcpIpTunnel> createDirectTunnel(const QString &originatingHost,
|
||||||
|
quint16 originatingPort, const QString &remoteHost, quint16 remotePort);
|
||||||
|
QSharedPointer<SshTcpIpForwardServer> createForwardServer(const QString &remoteHost,
|
||||||
|
quint16 remotePort);
|
||||||
|
|
||||||
|
SshStateInternal state() const { return m_state; }
|
||||||
|
SshError errorState() const { return m_error; }
|
||||||
|
QString errorString() const { return m_errorString; }
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void connected();
|
||||||
|
void disconnected();
|
||||||
|
void dataAvailable(const QString &message);
|
||||||
|
void error(QSsh::SshError);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void handleSocketConnected();
|
||||||
|
void handleIncomingData();
|
||||||
|
void handleSocketError();
|
||||||
|
void handleSocketDisconnected();
|
||||||
|
void handleTimeout();
|
||||||
|
void sendKeepAlivePacket();
|
||||||
|
|
||||||
|
void handleServerId();
|
||||||
|
void handlePackets();
|
||||||
|
void handleCurrentPacket();
|
||||||
|
void handleKeyExchangeInitPacket();
|
||||||
|
void handleKeyExchangeReplyPacket();
|
||||||
|
void handleNewKeysPacket();
|
||||||
|
void handleServiceAcceptPacket();
|
||||||
|
void handlePasswordExpiredPacket();
|
||||||
|
void handleUserAuthInfoRequestPacket();
|
||||||
|
void handleUserAuthSuccessPacket();
|
||||||
|
void handleUserAuthFailurePacket();
|
||||||
|
void handleUserAuthBannerPacket();
|
||||||
|
void handleUnexpectedPacket();
|
||||||
|
void handleGlobalRequest();
|
||||||
|
void handleDebugPacket();
|
||||||
|
void handleUnimplementedPacket();
|
||||||
|
void handleChannelRequest();
|
||||||
|
void handleChannelOpen();
|
||||||
|
void handleChannelOpenFailure();
|
||||||
|
void handleChannelOpenConfirmation();
|
||||||
|
void handleChannelSuccess();
|
||||||
|
void handleChannelFailure();
|
||||||
|
void handleChannelWindowAdjust();
|
||||||
|
void handleChannelData();
|
||||||
|
void handleChannelExtendedData();
|
||||||
|
void handleChannelEof();
|
||||||
|
void handleChannelClose();
|
||||||
|
void handleDisconnect();
|
||||||
|
void handleRequestSuccess();
|
||||||
|
void handleRequestFailure();
|
||||||
|
|
||||||
|
bool canUseSocket() const;
|
||||||
|
void createPrivateKey();
|
||||||
|
|
||||||
|
void sendData(const QByteArray &data);
|
||||||
|
|
||||||
|
typedef void (SshConnectionPrivate::*PacketHandler)();
|
||||||
|
typedef QList<SshStateInternal> StateList;
|
||||||
|
void setupPacketHandlers();
|
||||||
|
void setupPacketHandler(SshPacketType type, const StateList &states,
|
||||||
|
PacketHandler handler);
|
||||||
|
|
||||||
|
typedef QPair<StateList, PacketHandler> HandlerInStates;
|
||||||
|
QHash<SshPacketType, HandlerInStates> m_packetHandlers;
|
||||||
|
|
||||||
|
static const quint64 InvalidSeqNr;
|
||||||
|
|
||||||
|
QTcpSocket *m_socket;
|
||||||
|
SshStateInternal m_state;
|
||||||
|
SshKeyExchangeState m_keyExchangeState;
|
||||||
|
SshIncomingPacket m_incomingPacket;
|
||||||
|
SshSendFacility m_sendFacility;
|
||||||
|
SshChannelManager * const m_channelManager;
|
||||||
|
const SshConnectionParameters m_connParams;
|
||||||
|
QByteArray m_incomingData;
|
||||||
|
SshError m_error;
|
||||||
|
QString m_errorString;
|
||||||
|
QScopedPointer<SshKeyExchange> m_keyExchange;
|
||||||
|
QTimer m_timeoutTimer;
|
||||||
|
QTimer m_keepAliveTimer;
|
||||||
|
bool m_ignoreNextPacket;
|
||||||
|
SshConnection *m_conn;
|
||||||
|
quint64 m_lastInvalidMsgSeqNr;
|
||||||
|
QByteArray m_serverId;
|
||||||
|
bool m_serverHasSentDataBeforeId;
|
||||||
|
bool m_triedAllPasswordBasedMethods;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Internal
|
||||||
|
} // namespace QSsh
|
||||||
270
client/3rd/QtSsh/src/ssh/sshconnectionmanager.cpp
Normal file
270
client/3rd/QtSsh/src/ssh/sshconnectionmanager.cpp
Normal file
|
|
@ -0,0 +1,270 @@
|
||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2016 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#include "sshconnectionmanager.h"
|
||||||
|
|
||||||
|
#include "sshconnection.h"
|
||||||
|
|
||||||
|
#include <QCoreApplication>
|
||||||
|
#include <QList>
|
||||||
|
#include <QMutex>
|
||||||
|
#include <QMutexLocker>
|
||||||
|
#include <QObject>
|
||||||
|
#include <QThread>
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
|
namespace QSsh {
|
||||||
|
namespace Internal {
|
||||||
|
class UnaquiredConnection {
|
||||||
|
public:
|
||||||
|
UnaquiredConnection(SshConnection *conn) : connection(conn), scheduledForRemoval(false) {}
|
||||||
|
|
||||||
|
SshConnection *connection;
|
||||||
|
bool scheduledForRemoval;
|
||||||
|
};
|
||||||
|
bool operator==(const UnaquiredConnection &c1, const UnaquiredConnection &c2) {
|
||||||
|
return c1.connection == c2.connection;
|
||||||
|
}
|
||||||
|
bool operator!=(const UnaquiredConnection &c1, const UnaquiredConnection &c2) {
|
||||||
|
return !(c1 == c2);
|
||||||
|
}
|
||||||
|
|
||||||
|
class SshConnectionManager : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
SshConnectionManager()
|
||||||
|
{
|
||||||
|
moveToThread(QCoreApplication::instance()->thread());
|
||||||
|
connect(&m_removalTimer, &QTimer::timeout,
|
||||||
|
this, &SshConnectionManager::removeInactiveConnections);
|
||||||
|
m_removalTimer.start(150000); // For a total timeout of five minutes.
|
||||||
|
}
|
||||||
|
|
||||||
|
~SshConnectionManager()
|
||||||
|
{
|
||||||
|
foreach (const UnaquiredConnection &connection, m_unacquiredConnections) {
|
||||||
|
disconnect(connection.connection, 0, this, 0);
|
||||||
|
delete connection.connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
QSSH_ASSERT(m_acquiredConnections.isEmpty());
|
||||||
|
QSSH_ASSERT(m_deprecatedConnections.isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
SshConnection *acquireConnection(const SshConnectionParameters &sshParams)
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&m_listMutex);
|
||||||
|
|
||||||
|
// Check in-use connections:
|
||||||
|
foreach (SshConnection * const connection, m_acquiredConnections) {
|
||||||
|
if (connection->connectionParameters() != sshParams)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (connection->thread() != QThread::currentThread())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (m_deprecatedConnections.contains(connection)) // we were asked to no longer use this one...
|
||||||
|
continue;
|
||||||
|
|
||||||
|
m_acquiredConnections.append(connection);
|
||||||
|
return connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check cached open connections:
|
||||||
|
foreach (const UnaquiredConnection &c, m_unacquiredConnections) {
|
||||||
|
SshConnection * const connection = c.connection;
|
||||||
|
if (connection->state() != SshConnection::Connected
|
||||||
|
|| connection->connectionParameters() != sshParams)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (connection->thread() != QThread::currentThread()) {
|
||||||
|
if (connection->channelCount() != 0)
|
||||||
|
continue;
|
||||||
|
QMetaObject::invokeMethod(this, "switchToCallerThread",
|
||||||
|
Qt::BlockingQueuedConnection,
|
||||||
|
Q_ARG(SshConnection *, connection),
|
||||||
|
Q_ARG(QObject *, QThread::currentThread()));
|
||||||
|
}
|
||||||
|
|
||||||
|
m_unacquiredConnections.removeOne(c);
|
||||||
|
m_acquiredConnections.append(connection);
|
||||||
|
return connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
// create a new connection:
|
||||||
|
SshConnection * const connection = new SshConnection(sshParams);
|
||||||
|
connect(connection, &SshConnection::disconnected,
|
||||||
|
this, &SshConnectionManager::cleanup);
|
||||||
|
m_acquiredConnections.append(connection);
|
||||||
|
|
||||||
|
return connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
void releaseConnection(SshConnection *connection)
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&m_listMutex);
|
||||||
|
|
||||||
|
const bool wasAquired = m_acquiredConnections.removeOne(connection);
|
||||||
|
QSSH_ASSERT_AND_RETURN(wasAquired);
|
||||||
|
if (m_acquiredConnections.contains(connection))
|
||||||
|
return;
|
||||||
|
|
||||||
|
bool doDelete = false;
|
||||||
|
connection->moveToThread(QCoreApplication::instance()->thread());
|
||||||
|
if (m_deprecatedConnections.removeOne(connection)
|
||||||
|
|| connection->state() != SshConnection::Connected) {
|
||||||
|
doDelete = true;
|
||||||
|
} else {
|
||||||
|
QSSH_ASSERT_AND_RETURN(!m_unacquiredConnections.contains(UnaquiredConnection(connection)));
|
||||||
|
|
||||||
|
// It can happen that two or more connections with the same parameters were acquired
|
||||||
|
// if the clients were running in different threads. Only keep one of them in
|
||||||
|
// such a case.
|
||||||
|
bool haveConnection = false;
|
||||||
|
foreach (const UnaquiredConnection &c, m_unacquiredConnections) {
|
||||||
|
if (c.connection->connectionParameters() == connection->connectionParameters()) {
|
||||||
|
haveConnection = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!haveConnection) {
|
||||||
|
connection->closeAllChannels(); // Clean up after neglectful clients.
|
||||||
|
m_unacquiredConnections.append(UnaquiredConnection(connection));
|
||||||
|
} else {
|
||||||
|
doDelete = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (doDelete) {
|
||||||
|
disconnect(connection, 0, this, 0);
|
||||||
|
m_deprecatedConnections.removeAll(connection);
|
||||||
|
connection->deleteLater();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void forceNewConnection(const SshConnectionParameters &sshParams)
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&m_listMutex);
|
||||||
|
|
||||||
|
for (int i = 0; i < m_unacquiredConnections.count(); ++i) {
|
||||||
|
SshConnection * const connection = m_unacquiredConnections.at(i).connection;
|
||||||
|
if (connection->connectionParameters() == sshParams) {
|
||||||
|
disconnect(connection, 0, this, 0);
|
||||||
|
delete connection;
|
||||||
|
m_unacquiredConnections.removeAt(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (SshConnection * const connection, m_acquiredConnections) {
|
||||||
|
if (connection->connectionParameters() == sshParams) {
|
||||||
|
if (!m_deprecatedConnections.contains(connection))
|
||||||
|
m_deprecatedConnections.append(connection);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Q_INVOKABLE void switchToCallerThread(SshConnection *connection, QObject *threadObj)
|
||||||
|
{
|
||||||
|
connection->moveToThread(qobject_cast<QThread *>(threadObj));
|
||||||
|
}
|
||||||
|
|
||||||
|
void cleanup()
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&m_listMutex);
|
||||||
|
|
||||||
|
SshConnection *currentConnection = qobject_cast<SshConnection *>(sender());
|
||||||
|
if (!currentConnection)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (m_unacquiredConnections.removeOne(UnaquiredConnection(currentConnection))) {
|
||||||
|
disconnect(currentConnection, 0, this, 0);
|
||||||
|
currentConnection->deleteLater();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void removeInactiveConnections()
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&m_listMutex);
|
||||||
|
for (int i = m_unacquiredConnections.count() - 1; i >= 0; --i) {
|
||||||
|
UnaquiredConnection &c = m_unacquiredConnections[i];
|
||||||
|
if (c.scheduledForRemoval) {
|
||||||
|
disconnect(c.connection, 0, this, 0);
|
||||||
|
c.connection->deleteLater();
|
||||||
|
m_unacquiredConnections.removeAt(i);
|
||||||
|
} else {
|
||||||
|
c.scheduledForRemoval = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
// We expect the number of concurrently open connections to be small.
|
||||||
|
// If that turns out to not be the case, we can still use a data
|
||||||
|
// structure with faster access.
|
||||||
|
QList<UnaquiredConnection> m_unacquiredConnections;
|
||||||
|
|
||||||
|
// Can contain the same connection more than once; this acts as a reference count.
|
||||||
|
QList<SshConnection *> m_acquiredConnections;
|
||||||
|
|
||||||
|
QList<SshConnection *> m_deprecatedConnections;
|
||||||
|
QMutex m_listMutex;
|
||||||
|
QTimer m_removalTimer;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Internal
|
||||||
|
|
||||||
|
static QMutex instanceMutex;
|
||||||
|
|
||||||
|
static Internal::SshConnectionManager &instance()
|
||||||
|
{
|
||||||
|
static Internal::SshConnectionManager manager;
|
||||||
|
return manager;
|
||||||
|
}
|
||||||
|
|
||||||
|
SshConnection *acquireConnection(const SshConnectionParameters &sshParams)
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&instanceMutex);
|
||||||
|
return instance().acquireConnection(sshParams);
|
||||||
|
}
|
||||||
|
|
||||||
|
void releaseConnection(SshConnection *connection)
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&instanceMutex);
|
||||||
|
instance().releaseConnection(connection);
|
||||||
|
}
|
||||||
|
|
||||||
|
void forceNewConnection(const SshConnectionParameters &sshParams)
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&instanceMutex);
|
||||||
|
instance().forceNewConnection(sshParams);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace QSsh
|
||||||
|
|
||||||
|
#include "sshconnectionmanager.moc"
|
||||||
41
client/3rd/QtSsh/src/ssh/sshconnectionmanager.h
Normal file
41
client/3rd/QtSsh/src/ssh/sshconnectionmanager.h
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2016 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "ssh_global.h"
|
||||||
|
|
||||||
|
namespace QSsh {
|
||||||
|
|
||||||
|
class SshConnection;
|
||||||
|
class SshConnectionParameters;
|
||||||
|
|
||||||
|
QSSH_EXPORT SshConnection *acquireConnection(const SshConnectionParameters &sshParams);
|
||||||
|
QSSH_EXPORT void releaseConnection(SshConnection *connection);
|
||||||
|
|
||||||
|
// Make sure the next acquireConnection with the given parameters will return a new connection.
|
||||||
|
QSSH_EXPORT void forceNewConnection(const SshConnectionParameters &sshParams);
|
||||||
|
|
||||||
|
} // namespace QSsh
|
||||||
439
client/3rd/QtSsh/src/ssh/sshcryptofacility.cpp
Normal file
439
client/3rd/QtSsh/src/ssh/sshcryptofacility.cpp
Normal file
|
|
@ -0,0 +1,439 @@
|
||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2016 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#include "sshcryptofacility_p.h"
|
||||||
|
|
||||||
|
#include "sshbotanconversions_p.h"
|
||||||
|
#include "sshcapabilities_p.h"
|
||||||
|
#include "sshexception_p.h"
|
||||||
|
#include "sshkeyexchange_p.h"
|
||||||
|
#include "sshkeypasswordretriever_p.h"
|
||||||
|
#include "sshlogging_p.h"
|
||||||
|
#include "sshpacket_p.h"
|
||||||
|
|
||||||
|
#include <botan/botan.h>
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QList>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
using namespace Botan;
|
||||||
|
|
||||||
|
namespace QSsh {
|
||||||
|
namespace Internal {
|
||||||
|
|
||||||
|
SshAbstractCryptoFacility::SshAbstractCryptoFacility()
|
||||||
|
: m_cipherBlockSize(0), m_macLength(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
SshAbstractCryptoFacility::~SshAbstractCryptoFacility() {}
|
||||||
|
|
||||||
|
void SshAbstractCryptoFacility::clearKeys()
|
||||||
|
{
|
||||||
|
m_cipherBlockSize = 0;
|
||||||
|
m_macLength = 0;
|
||||||
|
m_sessionId.clear();
|
||||||
|
m_pipe.reset(0);
|
||||||
|
m_hMac.reset(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
SshAbstractCryptoFacility::Mode SshAbstractCryptoFacility::getMode(const QByteArray &algoName)
|
||||||
|
{
|
||||||
|
if (algoName.endsWith("-ctr"))
|
||||||
|
return CtrMode;
|
||||||
|
if (algoName.endsWith("-cbc"))
|
||||||
|
return CbcMode;
|
||||||
|
throw SshClientException(SshInternalError, SSH_TR("Unexpected cipher \"%1\"")
|
||||||
|
.arg(QString::fromLatin1(algoName)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshAbstractCryptoFacility::recreateKeys(const SshKeyExchange &kex)
|
||||||
|
{
|
||||||
|
checkInvariant();
|
||||||
|
|
||||||
|
if (m_sessionId.isEmpty())
|
||||||
|
m_sessionId = kex.h();
|
||||||
|
Algorithm_Factory &af = global_state().algorithm_factory();
|
||||||
|
const QByteArray &rfcCryptAlgoName = cryptAlgoName(kex);
|
||||||
|
BlockCipher * const cipher
|
||||||
|
= af.prototype_block_cipher(botanCryptAlgoName(rfcCryptAlgoName))->clone();
|
||||||
|
|
||||||
|
m_cipherBlockSize = static_cast<quint32>(cipher->block_size());
|
||||||
|
const QByteArray ivData = generateHash(kex, ivChar(), m_cipherBlockSize);
|
||||||
|
const InitializationVector iv(convertByteArray(ivData), m_cipherBlockSize);
|
||||||
|
|
||||||
|
const quint32 keySize = static_cast<quint32>(cipher->key_spec().maximum_keylength());
|
||||||
|
const QByteArray cryptKeyData = generateHash(kex, keyChar(), keySize);
|
||||||
|
SymmetricKey cryptKey(convertByteArray(cryptKeyData), keySize);
|
||||||
|
Keyed_Filter * const cipherMode
|
||||||
|
= makeCipherMode(cipher, getMode(rfcCryptAlgoName), iv, cryptKey);
|
||||||
|
m_pipe.reset(new Pipe(cipherMode));
|
||||||
|
|
||||||
|
m_macLength = botanHMacKeyLen(hMacAlgoName(kex));
|
||||||
|
const QByteArray hMacKeyData = generateHash(kex, macChar(), macLength());
|
||||||
|
SymmetricKey hMacKey(convertByteArray(hMacKeyData), macLength());
|
||||||
|
const HashFunction * const hMacProto
|
||||||
|
= af.prototype_hash_function(botanHMacAlgoName(hMacAlgoName(kex)));
|
||||||
|
m_hMac.reset(new HMAC(hMacProto->clone()));
|
||||||
|
m_hMac->set_key(hMacKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshAbstractCryptoFacility::convert(QByteArray &data, quint32 offset,
|
||||||
|
quint32 dataSize) const
|
||||||
|
{
|
||||||
|
Q_ASSERT(offset + dataSize <= static_cast<quint32>(data.size()));
|
||||||
|
checkInvariant();
|
||||||
|
|
||||||
|
// Session id empty => No key exchange has happened yet.
|
||||||
|
if (dataSize == 0 || m_sessionId.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (dataSize % cipherBlockSize() != 0) {
|
||||||
|
throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
|
||||||
|
"Invalid packet size");
|
||||||
|
}
|
||||||
|
m_pipe->process_msg(reinterpret_cast<const byte *>(data.constData()) + offset,
|
||||||
|
dataSize);
|
||||||
|
// Can't use Pipe::LAST_MESSAGE because of a VC bug.
|
||||||
|
quint32 bytesRead = static_cast<quint32>(m_pipe->read(
|
||||||
|
reinterpret_cast<byte *>(data.data()) + offset, dataSize, m_pipe->message_count() - 1));
|
||||||
|
if (bytesRead != dataSize) {
|
||||||
|
throw SshClientException(SshInternalError,
|
||||||
|
QLatin1String("Internal error: Botan::Pipe::read() returned unexpected value"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Keyed_Filter *SshAbstractCryptoFacility::makeCtrCipherMode(BlockCipher *cipher,
|
||||||
|
const InitializationVector &iv, const SymmetricKey &key)
|
||||||
|
{
|
||||||
|
StreamCipher_Filter * const filter = new StreamCipher_Filter(new CTR_BE(cipher));
|
||||||
|
filter->set_key(key);
|
||||||
|
filter->set_iv(iv);
|
||||||
|
return filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray SshAbstractCryptoFacility::generateMac(const QByteArray &data,
|
||||||
|
quint32 dataSize) const
|
||||||
|
{
|
||||||
|
return m_sessionId.isEmpty()
|
||||||
|
? QByteArray()
|
||||||
|
: convertByteArray(m_hMac->process(reinterpret_cast<const byte *>(data.constData()),
|
||||||
|
dataSize));
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray SshAbstractCryptoFacility::generateHash(const SshKeyExchange &kex,
|
||||||
|
char c, quint32 length)
|
||||||
|
{
|
||||||
|
const QByteArray &k = kex.k();
|
||||||
|
const QByteArray &h = kex.h();
|
||||||
|
QByteArray data(k);
|
||||||
|
data.append(h).append(c).append(m_sessionId);
|
||||||
|
SecureVector<byte> key
|
||||||
|
= kex.hash()->process(convertByteArray(data), data.size());
|
||||||
|
while (key.size() < length) {
|
||||||
|
SecureVector<byte> tmpKey;
|
||||||
|
tmpKey += SecureVector<byte>(convertByteArray(k), k.size());
|
||||||
|
tmpKey += SecureVector<byte>(convertByteArray(h), h.size());
|
||||||
|
tmpKey += key;
|
||||||
|
key += kex.hash()->process(tmpKey);
|
||||||
|
}
|
||||||
|
return QByteArray(reinterpret_cast<const char *>(key.begin()), length);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshAbstractCryptoFacility::checkInvariant() const
|
||||||
|
{
|
||||||
|
Q_ASSERT(m_sessionId.isEmpty() == !m_pipe);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const QByteArray SshEncryptionFacility::PrivKeyFileStartLineRsa("-----BEGIN RSA PRIVATE KEY-----");
|
||||||
|
const QByteArray SshEncryptionFacility::PrivKeyFileStartLineDsa("-----BEGIN DSA PRIVATE KEY-----");
|
||||||
|
const QByteArray SshEncryptionFacility::PrivKeyFileEndLineRsa("-----END RSA PRIVATE KEY-----");
|
||||||
|
const QByteArray SshEncryptionFacility::PrivKeyFileEndLineDsa("-----END DSA PRIVATE KEY-----");
|
||||||
|
const QByteArray SshEncryptionFacility::PrivKeyFileStartLineEcdsa("-----BEGIN EC PRIVATE KEY-----");
|
||||||
|
const QByteArray SshEncryptionFacility::PrivKeyFileEndLineEcdsa("-----END EC PRIVATE KEY-----");
|
||||||
|
|
||||||
|
QByteArray SshEncryptionFacility::cryptAlgoName(const SshKeyExchange &kex) const
|
||||||
|
{
|
||||||
|
return kex.encryptionAlgo();
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray SshEncryptionFacility::hMacAlgoName(const SshKeyExchange &kex) const
|
||||||
|
{
|
||||||
|
return kex.hMacAlgoClientToServer();
|
||||||
|
}
|
||||||
|
|
||||||
|
Keyed_Filter *SshEncryptionFacility::makeCipherMode(BlockCipher *cipher, Mode mode,
|
||||||
|
const InitializationVector &iv, const SymmetricKey &key)
|
||||||
|
{
|
||||||
|
switch (mode) {
|
||||||
|
case CbcMode:
|
||||||
|
return new CBC_Encryption(cipher, new Null_Padding, key, iv);
|
||||||
|
case CtrMode:
|
||||||
|
return makeCtrCipherMode(cipher, iv, key);
|
||||||
|
}
|
||||||
|
return 0; // For dumb compilers.
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshEncryptionFacility::encrypt(QByteArray &data) const
|
||||||
|
{
|
||||||
|
convert(data, 0, data.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshEncryptionFacility::createAuthenticationKey(const QByteArray &privKeyFileContents)
|
||||||
|
{
|
||||||
|
if (privKeyFileContents == m_cachedPrivKeyContents)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_authKeyAlgoName.clear();
|
||||||
|
qCDebug(sshLog, "%s: Key not cached, reading", Q_FUNC_INFO);
|
||||||
|
QList<BigInt> pubKeyParams;
|
||||||
|
QList<BigInt> allKeyParams;
|
||||||
|
QString error1;
|
||||||
|
QString error2;
|
||||||
|
if (!createAuthenticationKeyFromPKCS8(privKeyFileContents, pubKeyParams, allKeyParams, error1)
|
||||||
|
&& !createAuthenticationKeyFromOpenSSL(privKeyFileContents, pubKeyParams, allKeyParams,
|
||||||
|
error2)) {
|
||||||
|
qCDebug(sshLog, "%s: %s\n\t%s\n", Q_FUNC_INFO, qPrintable(error1), qPrintable(error2));
|
||||||
|
throw SshClientException(SshKeyFileError, SSH_TR("Decoding of private key file failed: "
|
||||||
|
"Format not understood."));
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (const BigInt &b, allKeyParams) {
|
||||||
|
if (b.is_zero()) {
|
||||||
|
throw SshClientException(SshKeyFileError,
|
||||||
|
SSH_TR("Decoding of private key file failed: Invalid zero parameter."));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_authPubKeyBlob = AbstractSshPacket::encodeString(m_authKeyAlgoName);
|
||||||
|
auto * const ecdsaKey = dynamic_cast<ECDSA_PrivateKey *>(m_authKey.data());
|
||||||
|
if (ecdsaKey) {
|
||||||
|
m_authPubKeyBlob += AbstractSshPacket::encodeString(m_authKeyAlgoName.mid(11)); // Without "ecdsa-sha2-" prefix.
|
||||||
|
m_authPubKeyBlob += AbstractSshPacket::encodeString(
|
||||||
|
convertByteArray(EC2OSP(ecdsaKey->public_point(), PointGFp::UNCOMPRESSED)));
|
||||||
|
} else {
|
||||||
|
foreach (const BigInt &b, pubKeyParams)
|
||||||
|
m_authPubKeyBlob += AbstractSshPacket::encodeMpInt(b);
|
||||||
|
}
|
||||||
|
m_cachedPrivKeyContents = privKeyFileContents;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SshEncryptionFacility::createAuthenticationKeyFromPKCS8(const QByteArray &privKeyFileContents,
|
||||||
|
QList<BigInt> &pubKeyParams, QList<BigInt> &allKeyParams, QString &error)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
Pipe pipe;
|
||||||
|
pipe.process_msg(convertByteArray(privKeyFileContents), privKeyFileContents.size());
|
||||||
|
m_authKey.reset(PKCS8::load_key(pipe, m_rng, SshKeyPasswordRetriever()));
|
||||||
|
if (auto * const dsaKey = dynamic_cast<DSA_PrivateKey *>(m_authKey.data())) {
|
||||||
|
m_authKeyAlgoName = SshCapabilities::PubKeyDss;
|
||||||
|
pubKeyParams << dsaKey->group_p() << dsaKey->group_q()
|
||||||
|
<< dsaKey->group_g() << dsaKey->get_y();
|
||||||
|
allKeyParams << pubKeyParams << dsaKey->get_x();
|
||||||
|
} else if (auto * const rsaKey = dynamic_cast<RSA_PrivateKey *>(m_authKey.data())) {
|
||||||
|
m_authKeyAlgoName = SshCapabilities::PubKeyRsa;
|
||||||
|
pubKeyParams << rsaKey->get_e() << rsaKey->get_n();
|
||||||
|
allKeyParams << pubKeyParams << rsaKey->get_p() << rsaKey->get_q()
|
||||||
|
<< rsaKey->get_d();
|
||||||
|
} else if (auto * const ecdsaKey = dynamic_cast<ECDSA_PrivateKey *>(m_authKey.data())) {
|
||||||
|
const BigInt value = ecdsaKey->private_value();
|
||||||
|
m_authKeyAlgoName = SshCapabilities::ecdsaPubKeyAlgoForKeyWidth(
|
||||||
|
static_cast<int>(value.bytes()));
|
||||||
|
pubKeyParams << ecdsaKey->public_point().get_affine_x()
|
||||||
|
<< ecdsaKey->public_point().get_affine_y();
|
||||||
|
allKeyParams << pubKeyParams << value;
|
||||||
|
} else {
|
||||||
|
qCWarning(sshLog, "%s: Unexpected code flow, expected success or exception.",
|
||||||
|
Q_FUNC_INFO);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} catch (const std::exception &ex) {
|
||||||
|
error = QLatin1String(ex.what());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SshEncryptionFacility::createAuthenticationKeyFromOpenSSL(const QByteArray &privKeyFileContents,
|
||||||
|
QList<BigInt> &pubKeyParams, QList<BigInt> &allKeyParams, QString &error)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
bool syntaxOk = true;
|
||||||
|
QList<QByteArray> lines = privKeyFileContents.split('\n');
|
||||||
|
while (lines.last().isEmpty())
|
||||||
|
lines.removeLast();
|
||||||
|
if (lines.count() < 3) {
|
||||||
|
syntaxOk = false;
|
||||||
|
} else if (lines.first() == PrivKeyFileStartLineRsa) {
|
||||||
|
if (lines.last() != PrivKeyFileEndLineRsa)
|
||||||
|
syntaxOk = false;
|
||||||
|
else
|
||||||
|
m_authKeyAlgoName = SshCapabilities::PubKeyRsa;
|
||||||
|
} else if (lines.first() == PrivKeyFileStartLineDsa) {
|
||||||
|
if (lines.last() != PrivKeyFileEndLineDsa)
|
||||||
|
syntaxOk = false;
|
||||||
|
else
|
||||||
|
m_authKeyAlgoName = SshCapabilities::PubKeyDss;
|
||||||
|
} else if (lines.first() == PrivKeyFileStartLineEcdsa) {
|
||||||
|
if (lines.last() != PrivKeyFileEndLineEcdsa)
|
||||||
|
syntaxOk = false;
|
||||||
|
// m_authKeyAlgoName set below, as we don't know the size yet.
|
||||||
|
} else {
|
||||||
|
syntaxOk = false;
|
||||||
|
}
|
||||||
|
if (!syntaxOk) {
|
||||||
|
error = SSH_TR("Unexpected format.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray privateKeyBlob;
|
||||||
|
for (int i = 1; i < lines.size() - 1; ++i)
|
||||||
|
privateKeyBlob += lines.at(i);
|
||||||
|
privateKeyBlob = QByteArray::fromBase64(privateKeyBlob);
|
||||||
|
|
||||||
|
BER_Decoder decoder(convertByteArray(privateKeyBlob), privateKeyBlob.size());
|
||||||
|
BER_Decoder sequence = decoder.start_cons(SEQUENCE);
|
||||||
|
size_t version;
|
||||||
|
sequence.decode (version);
|
||||||
|
const size_t expectedVersion = m_authKeyAlgoName.isEmpty() ? 1 : 0;
|
||||||
|
if (version != expectedVersion) {
|
||||||
|
error = SSH_TR("Key encoding has version %1, expected %2.")
|
||||||
|
.arg(version).arg(expectedVersion);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_authKeyAlgoName == SshCapabilities::PubKeyDss) {
|
||||||
|
BigInt p, q, g, y, x;
|
||||||
|
sequence.decode (p).decode (q).decode (g).decode (y).decode (x);
|
||||||
|
DSA_PrivateKey * const dsaKey = new DSA_PrivateKey(m_rng, DL_Group(p, q, g), x);
|
||||||
|
m_authKey.reset(dsaKey);
|
||||||
|
pubKeyParams << p << q << g << y;
|
||||||
|
allKeyParams << pubKeyParams << x;
|
||||||
|
} else if (m_authKeyAlgoName == SshCapabilities::PubKeyRsa) {
|
||||||
|
BigInt p, q, e, d, n;
|
||||||
|
sequence.decode(n).decode(e).decode(d).decode(p).decode(q);
|
||||||
|
RSA_PrivateKey * const rsaKey = new RSA_PrivateKey(m_rng, p, q, e, d, n);
|
||||||
|
m_authKey.reset(rsaKey);
|
||||||
|
pubKeyParams << e << n;
|
||||||
|
allKeyParams << pubKeyParams << p << q << d;
|
||||||
|
} else {
|
||||||
|
BigInt privKey;
|
||||||
|
sequence.decode_octet_string_bigint(privKey);
|
||||||
|
m_authKeyAlgoName = SshCapabilities::ecdsaPubKeyAlgoForKeyWidth(
|
||||||
|
static_cast<int>(privKey.bytes()));
|
||||||
|
const EC_Group group(SshCapabilities::oid(m_authKeyAlgoName));
|
||||||
|
auto * const key = new ECDSA_PrivateKey(m_rng, group, privKey);
|
||||||
|
m_authKey.reset(key);
|
||||||
|
pubKeyParams << key->public_point().get_affine_x()
|
||||||
|
<< key->public_point().get_affine_y();
|
||||||
|
allKeyParams << pubKeyParams << privKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
sequence.discard_remaining();
|
||||||
|
sequence.verify_end();
|
||||||
|
} catch (const std::exception &ex) {
|
||||||
|
error = QLatin1String(ex.what());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray SshEncryptionFacility::authenticationAlgorithmName() const
|
||||||
|
{
|
||||||
|
Q_ASSERT(m_authKey);
|
||||||
|
return m_authKeyAlgoName;
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray SshEncryptionFacility::authenticationKeySignature(const QByteArray &data) const
|
||||||
|
{
|
||||||
|
Q_ASSERT(m_authKey);
|
||||||
|
|
||||||
|
QScopedPointer<PK_Signer> signer(new PK_Signer(*m_authKey,
|
||||||
|
botanEmsaAlgoName(m_authKeyAlgoName)));
|
||||||
|
QByteArray dataToSign = AbstractSshPacket::encodeString(sessionId()) + data;
|
||||||
|
QByteArray signature
|
||||||
|
= convertByteArray(signer->sign_message(convertByteArray(dataToSign),
|
||||||
|
dataToSign.size(), m_rng));
|
||||||
|
if (m_authKeyAlgoName.startsWith(SshCapabilities::PubKeyEcdsaPrefix)) {
|
||||||
|
// The Botan output is not quite in the format that SSH defines.
|
||||||
|
const int halfSize = signature.count() / 2;
|
||||||
|
const BigInt r = BigInt::decode(convertByteArray(signature), halfSize);
|
||||||
|
const BigInt s = BigInt::decode(convertByteArray(signature.mid(halfSize)), halfSize);
|
||||||
|
signature = AbstractSshPacket::encodeMpInt(r) + AbstractSshPacket::encodeMpInt(s);
|
||||||
|
}
|
||||||
|
return AbstractSshPacket::encodeString(m_authKeyAlgoName)
|
||||||
|
+ AbstractSshPacket::encodeString(signature);
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray SshEncryptionFacility::getRandomNumbers(int count) const
|
||||||
|
{
|
||||||
|
QByteArray data;
|
||||||
|
data.resize(count);
|
||||||
|
m_rng.randomize(convertByteArray(data), count);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
SshEncryptionFacility::~SshEncryptionFacility() {}
|
||||||
|
|
||||||
|
|
||||||
|
QByteArray SshDecryptionFacility::cryptAlgoName(const SshKeyExchange &kex) const
|
||||||
|
{
|
||||||
|
return kex.decryptionAlgo();
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray SshDecryptionFacility::hMacAlgoName(const SshKeyExchange &kex) const
|
||||||
|
{
|
||||||
|
return kex.hMacAlgoServerToClient();
|
||||||
|
}
|
||||||
|
|
||||||
|
Keyed_Filter *SshDecryptionFacility::makeCipherMode(BlockCipher *cipher, Mode mode, const InitializationVector &iv,
|
||||||
|
const SymmetricKey &key)
|
||||||
|
{
|
||||||
|
switch (mode) {
|
||||||
|
case CbcMode:
|
||||||
|
return new CBC_Decryption(cipher, new Null_Padding, key, iv);
|
||||||
|
case CtrMode:
|
||||||
|
return makeCtrCipherMode(cipher, iv, key);
|
||||||
|
}
|
||||||
|
return 0; // For dumb compilers.
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshDecryptionFacility::decrypt(QByteArray &data, quint32 offset,
|
||||||
|
quint32 dataSize) const
|
||||||
|
{
|
||||||
|
convert(data, offset, dataSize);
|
||||||
|
qCDebug(sshLog, "Decrypted data:");
|
||||||
|
const char * const start = data.constData() + offset;
|
||||||
|
const char * const end = start + dataSize;
|
||||||
|
for (const char *c = start; c < end; ++c)
|
||||||
|
qCDebug(sshLog, ) << "'" << *c << "' (0x" << (static_cast<int>(*c) & 0xff) << ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Internal
|
||||||
|
} // namespace QSsh
|
||||||
138
client/3rd/QtSsh/src/ssh/sshcryptofacility_p.h
Normal file
138
client/3rd/QtSsh/src/ssh/sshcryptofacility_p.h
Normal file
|
|
@ -0,0 +1,138 @@
|
||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2016 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <botan/botan.h>
|
||||||
|
|
||||||
|
#include <QByteArray>
|
||||||
|
#include <QScopedPointer>
|
||||||
|
|
||||||
|
namespace QSsh {
|
||||||
|
namespace Internal {
|
||||||
|
|
||||||
|
class SshKeyExchange;
|
||||||
|
|
||||||
|
class SshAbstractCryptoFacility
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~SshAbstractCryptoFacility();
|
||||||
|
|
||||||
|
void clearKeys();
|
||||||
|
void recreateKeys(const SshKeyExchange &kex);
|
||||||
|
QByteArray generateMac(const QByteArray &data, quint32 dataSize) const;
|
||||||
|
quint32 cipherBlockSize() const { return m_cipherBlockSize; }
|
||||||
|
quint32 macLength() const { return m_macLength; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
enum Mode { CbcMode, CtrMode };
|
||||||
|
|
||||||
|
SshAbstractCryptoFacility();
|
||||||
|
void convert(QByteArray &data, quint32 offset, quint32 dataSize) const;
|
||||||
|
QByteArray sessionId() const { return m_sessionId; }
|
||||||
|
Botan::Keyed_Filter *makeCtrCipherMode(Botan::BlockCipher *cipher,
|
||||||
|
const Botan::InitializationVector &iv, const Botan::SymmetricKey &key);
|
||||||
|
|
||||||
|
private:
|
||||||
|
SshAbstractCryptoFacility(const SshAbstractCryptoFacility &);
|
||||||
|
SshAbstractCryptoFacility &operator=(const SshAbstractCryptoFacility &);
|
||||||
|
|
||||||
|
virtual QByteArray cryptAlgoName(const SshKeyExchange &kex) const = 0;
|
||||||
|
virtual QByteArray hMacAlgoName(const SshKeyExchange &kex) const = 0;
|
||||||
|
virtual Botan::Keyed_Filter *makeCipherMode(Botan::BlockCipher *cipher,
|
||||||
|
Mode mode, const Botan::InitializationVector &iv, const Botan::SymmetricKey &key) = 0;
|
||||||
|
virtual char ivChar() const = 0;
|
||||||
|
virtual char keyChar() const = 0;
|
||||||
|
virtual char macChar() const = 0;
|
||||||
|
|
||||||
|
QByteArray generateHash(const SshKeyExchange &kex, char c, quint32 length);
|
||||||
|
void checkInvariant() const;
|
||||||
|
static Mode getMode(const QByteArray &algoName);
|
||||||
|
|
||||||
|
QByteArray m_sessionId;
|
||||||
|
QScopedPointer<Botan::Pipe> m_pipe;
|
||||||
|
QScopedPointer<Botan::HMAC> m_hMac;
|
||||||
|
quint32 m_cipherBlockSize;
|
||||||
|
quint32 m_macLength;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SshEncryptionFacility : public SshAbstractCryptoFacility
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void encrypt(QByteArray &data) const;
|
||||||
|
|
||||||
|
void createAuthenticationKey(const QByteArray &privKeyFileContents);
|
||||||
|
QByteArray authenticationAlgorithmName() const;
|
||||||
|
QByteArray authenticationPublicKey() const { return m_authPubKeyBlob; }
|
||||||
|
QByteArray authenticationKeySignature(const QByteArray &data) const;
|
||||||
|
QByteArray getRandomNumbers(int count) const;
|
||||||
|
|
||||||
|
~SshEncryptionFacility();
|
||||||
|
|
||||||
|
private:
|
||||||
|
virtual QByteArray cryptAlgoName(const SshKeyExchange &kex) const;
|
||||||
|
virtual QByteArray hMacAlgoName(const SshKeyExchange &kex) const;
|
||||||
|
virtual Botan::Keyed_Filter *makeCipherMode(Botan::BlockCipher *cipher,
|
||||||
|
Mode mode, const Botan::InitializationVector &iv, const Botan::SymmetricKey &key);
|
||||||
|
virtual char ivChar() const { return 'A'; }
|
||||||
|
virtual char keyChar() const { return 'C'; }
|
||||||
|
virtual char macChar() const { return 'E'; }
|
||||||
|
|
||||||
|
bool createAuthenticationKeyFromPKCS8(const QByteArray &privKeyFileContents,
|
||||||
|
QList<Botan::BigInt> &pubKeyParams, QList<Botan::BigInt> &allKeyParams, QString &error);
|
||||||
|
bool createAuthenticationKeyFromOpenSSL(const QByteArray &privKeyFileContents,
|
||||||
|
QList<Botan::BigInt> &pubKeyParams, QList<Botan::BigInt> &allKeyParams, QString &error);
|
||||||
|
|
||||||
|
static const QByteArray PrivKeyFileStartLineRsa;
|
||||||
|
static const QByteArray PrivKeyFileStartLineDsa;
|
||||||
|
static const QByteArray PrivKeyFileEndLineRsa;
|
||||||
|
static const QByteArray PrivKeyFileEndLineDsa;
|
||||||
|
static const QByteArray PrivKeyFileStartLineEcdsa;
|
||||||
|
static const QByteArray PrivKeyFileEndLineEcdsa;
|
||||||
|
|
||||||
|
QByteArray m_authKeyAlgoName;
|
||||||
|
QByteArray m_authPubKeyBlob;
|
||||||
|
QByteArray m_cachedPrivKeyContents;
|
||||||
|
QScopedPointer<Botan::Private_Key> m_authKey;
|
||||||
|
mutable Botan::AutoSeeded_RNG m_rng;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SshDecryptionFacility : public SshAbstractCryptoFacility
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void decrypt(QByteArray &data, quint32 offset, quint32 dataSize) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
virtual QByteArray cryptAlgoName(const SshKeyExchange &kex) const;
|
||||||
|
virtual QByteArray hMacAlgoName(const SshKeyExchange &kex) const;
|
||||||
|
virtual Botan::Keyed_Filter *makeCipherMode(Botan::BlockCipher *cipher,
|
||||||
|
Mode mode, const Botan::InitializationVector &iv, const Botan::SymmetricKey &key);
|
||||||
|
virtual char ivChar() const { return 'B'; }
|
||||||
|
virtual char keyChar() const { return 'D'; }
|
||||||
|
virtual char macChar() const { return 'F'; }
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Internal
|
||||||
|
} // namespace QSsh
|
||||||
122
client/3rd/QtSsh/src/ssh/sshdirecttcpiptunnel.cpp
Normal file
122
client/3rd/QtSsh/src/ssh/sshdirecttcpiptunnel.cpp
Normal file
|
|
@ -0,0 +1,122 @@
|
||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2016 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#include "sshdirecttcpiptunnel.h"
|
||||||
|
#include "sshdirecttcpiptunnel_p.h"
|
||||||
|
|
||||||
|
#include "sshincomingpacket_p.h"
|
||||||
|
#include "sshlogging_p.h"
|
||||||
|
#include "sshsendfacility_p.h"
|
||||||
|
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
|
namespace QSsh {
|
||||||
|
namespace Internal {
|
||||||
|
|
||||||
|
SshDirectTcpIpTunnelPrivate::SshDirectTcpIpTunnelPrivate(quint32 channelId,
|
||||||
|
const QString &originatingHost, quint16 originatingPort, const QString &remoteHost,
|
||||||
|
quint16 remotePort, SshSendFacility &sendFacility)
|
||||||
|
: SshTcpIpTunnelPrivate(channelId, sendFacility),
|
||||||
|
m_originatingHost(originatingHost),
|
||||||
|
m_originatingPort(originatingPort),
|
||||||
|
m_remoteHost(remoteHost),
|
||||||
|
m_remotePort(remotePort)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshDirectTcpIpTunnelPrivate::handleOpenSuccessInternal()
|
||||||
|
{
|
||||||
|
emit initialized();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Internal
|
||||||
|
|
||||||
|
using namespace Internal;
|
||||||
|
|
||||||
|
SshDirectTcpIpTunnel::SshDirectTcpIpTunnel(quint32 channelId, const QString &originatingHost,
|
||||||
|
quint16 originatingPort, const QString &remoteHost, quint16 remotePort,
|
||||||
|
SshSendFacility &sendFacility)
|
||||||
|
: d(new SshDirectTcpIpTunnelPrivate(channelId, originatingHost, originatingPort, remoteHost,
|
||||||
|
remotePort, sendFacility))
|
||||||
|
{
|
||||||
|
d->init(this);
|
||||||
|
connect(d, &SshDirectTcpIpTunnelPrivate::initialized,
|
||||||
|
this, &SshDirectTcpIpTunnel::initialized, Qt::QueuedConnection);
|
||||||
|
}
|
||||||
|
|
||||||
|
SshDirectTcpIpTunnel::~SshDirectTcpIpTunnel()
|
||||||
|
{
|
||||||
|
delete d;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SshDirectTcpIpTunnel::atEnd() const
|
||||||
|
{
|
||||||
|
return QIODevice::atEnd() && d->m_data.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
qint64 SshDirectTcpIpTunnel::bytesAvailable() const
|
||||||
|
{
|
||||||
|
return QIODevice::bytesAvailable() + d->m_data.count();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SshDirectTcpIpTunnel::canReadLine() const
|
||||||
|
{
|
||||||
|
return QIODevice::canReadLine() || d->m_data.contains('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshDirectTcpIpTunnel::close()
|
||||||
|
{
|
||||||
|
d->closeChannel();
|
||||||
|
QIODevice::close();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshDirectTcpIpTunnel::initialize()
|
||||||
|
{
|
||||||
|
QSSH_ASSERT_AND_RETURN(d->channelState() == AbstractSshChannel::Inactive);
|
||||||
|
|
||||||
|
try {
|
||||||
|
QIODevice::open(QIODevice::ReadWrite);
|
||||||
|
d->m_sendFacility.sendDirectTcpIpPacket(d->localChannelId(), d->initialWindowSize(),
|
||||||
|
d->maxPacketSize(), d->m_remoteHost.toUtf8(), d->m_remotePort,
|
||||||
|
d->m_originatingHost.toUtf8(), d->m_originatingPort);
|
||||||
|
d->setChannelState(AbstractSshChannel::SessionRequested);
|
||||||
|
d->m_timeoutTimer.start(d->ReplyTimeout);
|
||||||
|
} catch (const std::exception &e) { // Won't happen, but let's play it safe.
|
||||||
|
qCWarning(sshLog, "Botan error: %s", e.what());
|
||||||
|
d->closeChannel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
qint64 SshDirectTcpIpTunnel::readData(char *data, qint64 maxlen)
|
||||||
|
{
|
||||||
|
return d->readData(data, maxlen);
|
||||||
|
}
|
||||||
|
|
||||||
|
qint64 SshDirectTcpIpTunnel::writeData(const char *data, qint64 len)
|
||||||
|
{
|
||||||
|
return d->writeData(data, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace QSsh
|
||||||
79
client/3rd/QtSsh/src/ssh/sshdirecttcpiptunnel.h
Normal file
79
client/3rd/QtSsh/src/ssh/sshdirecttcpiptunnel.h
Normal file
|
|
@ -0,0 +1,79 @@
|
||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2016 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "ssh_global.h"
|
||||||
|
|
||||||
|
#include <QIODevice>
|
||||||
|
#include <QSharedPointer>
|
||||||
|
|
||||||
|
namespace QSsh {
|
||||||
|
|
||||||
|
namespace Internal {
|
||||||
|
class SshChannelManager;
|
||||||
|
class SshDirectTcpIpTunnelPrivate;
|
||||||
|
class SshSendFacility;
|
||||||
|
class SshTcpIpTunnelPrivate;
|
||||||
|
} // namespace Internal
|
||||||
|
|
||||||
|
class QSSH_EXPORT SshDirectTcpIpTunnel : public QIODevice
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
friend class Internal::SshChannelManager;
|
||||||
|
friend class Internal::SshTcpIpTunnelPrivate;
|
||||||
|
|
||||||
|
public:
|
||||||
|
typedef QSharedPointer<SshDirectTcpIpTunnel> Ptr;
|
||||||
|
|
||||||
|
~SshDirectTcpIpTunnel();
|
||||||
|
|
||||||
|
// QIODevice stuff
|
||||||
|
bool atEnd() const;
|
||||||
|
qint64 bytesAvailable() const;
|
||||||
|
bool canReadLine() const;
|
||||||
|
void close();
|
||||||
|
bool isSequential() const { return true; }
|
||||||
|
|
||||||
|
void initialize();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void initialized();
|
||||||
|
void error(const QString &reason);
|
||||||
|
|
||||||
|
private:
|
||||||
|
SshDirectTcpIpTunnel(quint32 channelId, const QString &originatingHost,
|
||||||
|
quint16 originatingPort, const QString &remoteHost, quint16 remotePort,
|
||||||
|
Internal::SshSendFacility &sendFacility);
|
||||||
|
|
||||||
|
// QIODevice stuff
|
||||||
|
qint64 readData(char *data, qint64 maxlen);
|
||||||
|
qint64 writeData(const char *data, qint64 len);
|
||||||
|
|
||||||
|
Internal::SshDirectTcpIpTunnelPrivate * const d;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace QSsh
|
||||||
59
client/3rd/QtSsh/src/ssh/sshdirecttcpiptunnel_p.h
Normal file
59
client/3rd/QtSsh/src/ssh/sshdirecttcpiptunnel_p.h
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2016 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "sshtcpiptunnel_p.h"
|
||||||
|
|
||||||
|
namespace QSsh {
|
||||||
|
class SshDirectTcpIpTunnel;
|
||||||
|
|
||||||
|
namespace Internal {
|
||||||
|
|
||||||
|
class SshDirectTcpIpTunnelPrivate : public SshTcpIpTunnelPrivate
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
friend class QSsh::SshDirectTcpIpTunnel;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit SshDirectTcpIpTunnelPrivate(quint32 channelId, const QString &originatingHost,
|
||||||
|
quint16 originatingPort, const QString &remoteHost, quint16 remotePort,
|
||||||
|
SshSendFacility &sendFacility);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void initialized();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void handleOpenSuccessInternal();
|
||||||
|
|
||||||
|
const QString m_originatingHost;
|
||||||
|
const quint16 m_originatingPort;
|
||||||
|
const QString m_remoteHost;
|
||||||
|
const quint16 m_remotePort;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Internal
|
||||||
|
} // namespace QSsh
|
||||||
37
client/3rd/QtSsh/src/ssh/ssherrors.h
Normal file
37
client/3rd/QtSsh/src/ssh/ssherrors.h
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2016 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#define SSHERRORS_P_H
|
||||||
|
|
||||||
|
namespace QSsh {
|
||||||
|
|
||||||
|
enum SshError {
|
||||||
|
SshNoError, SshSocketError, SshTimeoutError, SshProtocolError,
|
||||||
|
SshHostKeyError, SshKeyFileError, SshAuthenticationError,
|
||||||
|
SshClosedByServerError, SshInternalError
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace QSsh
|
||||||
82
client/3rd/QtSsh/src/ssh/sshexception_p.h
Normal file
82
client/3rd/QtSsh/src/ssh/sshexception_p.h
Normal file
|
|
@ -0,0 +1,82 @@
|
||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2016 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "ssherrors.h"
|
||||||
|
|
||||||
|
#include <QByteArray>
|
||||||
|
#include <QCoreApplication>
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
namespace QSsh {
|
||||||
|
namespace Internal {
|
||||||
|
|
||||||
|
enum SshErrorCode {
|
||||||
|
SSH_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT = 1,
|
||||||
|
SSH_DISCONNECT_PROTOCOL_ERROR = 2,
|
||||||
|
SSH_DISCONNECT_KEY_EXCHANGE_FAILED = 3,
|
||||||
|
SSH_DISCONNECT_RESERVED = 4,
|
||||||
|
SSH_DISCONNECT_MAC_ERROR = 5,
|
||||||
|
SSH_DISCONNECT_COMPRESSION_ERROR = 6,
|
||||||
|
SSH_DISCONNECT_SERVICE_NOT_AVAILABLE = 7,
|
||||||
|
SSH_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED = 8,
|
||||||
|
SSH_DISCONNECT_HOST_KEY_NOT_VERIFIABLE = 9,
|
||||||
|
SSH_DISCONNECT_CONNECTION_LOST = 10,
|
||||||
|
SSH_DISCONNECT_BY_APPLICATION = 11,
|
||||||
|
SSH_DISCONNECT_TOO_MANY_CONNECTIONS = 12,
|
||||||
|
SSH_DISCONNECT_AUTH_CANCELLED_BY_USER = 13,
|
||||||
|
SSH_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE = 14,
|
||||||
|
SSH_DISCONNECT_ILLEGAL_USER_NAME = 15
|
||||||
|
};
|
||||||
|
|
||||||
|
#define SSH_TR(string) QCoreApplication::translate("SshConnection", string)
|
||||||
|
|
||||||
|
#define SSH_SERVER_EXCEPTION(error, errorString) \
|
||||||
|
SshServerException((error), (errorString), SSH_TR(errorString))
|
||||||
|
|
||||||
|
struct SshServerException
|
||||||
|
{
|
||||||
|
SshServerException(SshErrorCode error, const QByteArray &errorStringServer,
|
||||||
|
const QString &errorStringUser)
|
||||||
|
: error(error), errorStringServer(errorStringServer),
|
||||||
|
errorStringUser(errorStringUser) {}
|
||||||
|
|
||||||
|
const SshErrorCode error;
|
||||||
|
const QByteArray errorStringServer;
|
||||||
|
const QString errorStringUser;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SshClientException
|
||||||
|
{
|
||||||
|
SshClientException(SshError error, const QString &errorString)
|
||||||
|
: error(error), errorString(errorString) {}
|
||||||
|
|
||||||
|
const SshError error;
|
||||||
|
const QString errorString;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Internal
|
||||||
|
} // namespace QSsh
|
||||||
100
client/3rd/QtSsh/src/ssh/sshforwardedtcpiptunnel.cpp
Normal file
100
client/3rd/QtSsh/src/ssh/sshforwardedtcpiptunnel.cpp
Normal file
|
|
@ -0,0 +1,100 @@
|
||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2016 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#include "sshforwardedtcpiptunnel.h"
|
||||||
|
#include "sshforwardedtcpiptunnel_p.h"
|
||||||
|
#include "sshlogging_p.h"
|
||||||
|
#include "sshsendfacility_p.h"
|
||||||
|
|
||||||
|
namespace QSsh {
|
||||||
|
|
||||||
|
namespace Internal {
|
||||||
|
SshForwardedTcpIpTunnelPrivate::SshForwardedTcpIpTunnelPrivate(quint32 channelId,
|
||||||
|
SshSendFacility &sendFacility) :
|
||||||
|
SshTcpIpTunnelPrivate(channelId, sendFacility)
|
||||||
|
{
|
||||||
|
setChannelState(SessionRequested);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshForwardedTcpIpTunnelPrivate::handleOpenSuccessInternal()
|
||||||
|
{
|
||||||
|
QSSH_ASSERT_AND_RETURN(channelState() == AbstractSshChannel::SessionEstablished);
|
||||||
|
|
||||||
|
try {
|
||||||
|
m_sendFacility.sendChannelOpenConfirmationPacket(remoteChannel(), localChannelId(),
|
||||||
|
initialWindowSize(), maxPacketSize());
|
||||||
|
} catch (const std::exception &e) { // Won't happen, but let's play it safe.
|
||||||
|
qCWarning(sshLog, "Botan error: %s", e.what());
|
||||||
|
closeChannel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Internal
|
||||||
|
|
||||||
|
using namespace Internal;
|
||||||
|
|
||||||
|
SshForwardedTcpIpTunnel::SshForwardedTcpIpTunnel(quint32 channelId, SshSendFacility &sendFacility) :
|
||||||
|
d(new SshForwardedTcpIpTunnelPrivate(channelId, sendFacility))
|
||||||
|
{
|
||||||
|
d->init(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
SshForwardedTcpIpTunnel::~SshForwardedTcpIpTunnel()
|
||||||
|
{
|
||||||
|
delete d;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SshForwardedTcpIpTunnel::atEnd() const
|
||||||
|
{
|
||||||
|
return QIODevice::atEnd() && d->m_data.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
qint64 SshForwardedTcpIpTunnel::bytesAvailable() const
|
||||||
|
{
|
||||||
|
return QIODevice::bytesAvailable() + d->m_data.count();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SshForwardedTcpIpTunnel::canReadLine() const
|
||||||
|
{
|
||||||
|
return QIODevice::canReadLine() || d->m_data.contains('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshForwardedTcpIpTunnel::close()
|
||||||
|
{
|
||||||
|
d->closeChannel();
|
||||||
|
QIODevice::close();
|
||||||
|
}
|
||||||
|
|
||||||
|
qint64 SshForwardedTcpIpTunnel::readData(char *data, qint64 maxlen)
|
||||||
|
{
|
||||||
|
return d->readData(data, maxlen);
|
||||||
|
}
|
||||||
|
|
||||||
|
qint64 SshForwardedTcpIpTunnel::writeData(const char *data, qint64 len)
|
||||||
|
{
|
||||||
|
return d->writeData(data, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace QSsh
|
||||||
70
client/3rd/QtSsh/src/ssh/sshforwardedtcpiptunnel.h
Normal file
70
client/3rd/QtSsh/src/ssh/sshforwardedtcpiptunnel.h
Normal file
|
|
@ -0,0 +1,70 @@
|
||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2016 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "ssh_global.h"
|
||||||
|
#include <QIODevice>
|
||||||
|
#include <QSharedPointer>
|
||||||
|
|
||||||
|
namespace QSsh {
|
||||||
|
|
||||||
|
namespace Internal {
|
||||||
|
class SshChannelManager;
|
||||||
|
class SshForwardedTcpIpTunnelPrivate;
|
||||||
|
class SshSendFacility;
|
||||||
|
class SshTcpIpTunnelPrivate;
|
||||||
|
} // namespace Internal
|
||||||
|
|
||||||
|
class QSSH_EXPORT SshForwardedTcpIpTunnel : public QIODevice
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
friend class Internal::SshChannelManager;
|
||||||
|
friend class Internal::SshTcpIpTunnelPrivate;
|
||||||
|
|
||||||
|
public:
|
||||||
|
typedef QSharedPointer<SshForwardedTcpIpTunnel> Ptr;
|
||||||
|
~SshForwardedTcpIpTunnel() override;
|
||||||
|
|
||||||
|
// QIODevice stuff
|
||||||
|
bool atEnd() const override;
|
||||||
|
qint64 bytesAvailable() const override;
|
||||||
|
bool canReadLine() const override;
|
||||||
|
void close() override;
|
||||||
|
bool isSequential() const override { return true; }
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void error(const QString &reason);
|
||||||
|
|
||||||
|
private:
|
||||||
|
SshForwardedTcpIpTunnel(quint32 channelId, Internal::SshSendFacility &sendFacility);
|
||||||
|
|
||||||
|
// QIODevice stuff
|
||||||
|
qint64 readData(char *data, qint64 maxlen) override;
|
||||||
|
qint64 writeData(const char *data, qint64 len) override;
|
||||||
|
|
||||||
|
Internal::SshForwardedTcpIpTunnelPrivate * const d;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace QSsh
|
||||||
44
client/3rd/QtSsh/src/ssh/sshforwardedtcpiptunnel_p.h
Normal file
44
client/3rd/QtSsh/src/ssh/sshforwardedtcpiptunnel_p.h
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2016 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "sshforwardedtcpiptunnel.h"
|
||||||
|
#include "sshtcpiptunnel_p.h"
|
||||||
|
|
||||||
|
namespace QSsh {
|
||||||
|
namespace Internal {
|
||||||
|
|
||||||
|
class SshForwardedTcpIpTunnelPrivate : public SshTcpIpTunnelPrivate
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
friend class QSsh::SshForwardedTcpIpTunnel;
|
||||||
|
public:
|
||||||
|
SshForwardedTcpIpTunnelPrivate(quint32 channelId, SshSendFacility &sendFacility);
|
||||||
|
void handleOpenSuccessInternal() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Internal
|
||||||
|
} // namespace QSsh
|
||||||
118
client/3rd/QtSsh/src/ssh/sshhostkeydatabase.cpp
Normal file
118
client/3rd/QtSsh/src/ssh/sshhostkeydatabase.cpp
Normal file
|
|
@ -0,0 +1,118 @@
|
||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2016 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#include "sshhostkeydatabase.h"
|
||||||
|
|
||||||
|
#include "sshlogging_p.h"
|
||||||
|
|
||||||
|
#include <QByteArray>
|
||||||
|
#include <QCoreApplication>
|
||||||
|
#include <QDir>
|
||||||
|
#include <QFile>
|
||||||
|
#include <QHash>
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
namespace QSsh {
|
||||||
|
|
||||||
|
class SshHostKeyDatabase::SshHostKeyDatabasePrivate
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
QHash<QString, QByteArray> hostKeys;
|
||||||
|
};
|
||||||
|
|
||||||
|
SshHostKeyDatabase::SshHostKeyDatabase() : d(new SshHostKeyDatabasePrivate)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
SshHostKeyDatabase::~SshHostKeyDatabase()
|
||||||
|
{
|
||||||
|
delete d;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SshHostKeyDatabase::load(const QString &filePath, QString *error)
|
||||||
|
{
|
||||||
|
QFile file(filePath);
|
||||||
|
if (!file.open(QIODevice::ReadOnly)) {
|
||||||
|
if (error) {
|
||||||
|
*error = QCoreApplication::translate("QSsh::Ssh",
|
||||||
|
"Failed to open key file \"%1\" for reading: %2")
|
||||||
|
.arg(QDir::toNativeSeparators(filePath), file.errorString());
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
d->hostKeys.clear();
|
||||||
|
const QByteArray content = file.readAll().trimmed();
|
||||||
|
if (content.isEmpty())
|
||||||
|
return true;
|
||||||
|
foreach (const QByteArray &line, content.split('\n')) {
|
||||||
|
const QList<QByteArray> &lineData = line.trimmed().split(' ');
|
||||||
|
if (lineData.count() != 2) {
|
||||||
|
qCDebug(Internal::sshLog, "Unexpected line \"%s\" in file \"%s\".", line.constData(),
|
||||||
|
qPrintable(filePath));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
d->hostKeys.insert(QString::fromUtf8(lineData.first()),
|
||||||
|
QByteArray::fromHex(lineData.last()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SshHostKeyDatabase::store(const QString &filePath, QString *error) const
|
||||||
|
{
|
||||||
|
QFile file(filePath);
|
||||||
|
if (!file.open(QIODevice::WriteOnly)) {
|
||||||
|
if (error) {
|
||||||
|
*error = QCoreApplication::translate("QSsh::Ssh",
|
||||||
|
"Failed to open key file \"%1\" for writing: %2")
|
||||||
|
.arg(QDir::toNativeSeparators(filePath), file.errorString());
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
file.resize(0);
|
||||||
|
for (auto it = d->hostKeys.constBegin(); it != d->hostKeys.constEnd(); ++it)
|
||||||
|
file.write(it.key().toUtf8() + ' ' + it.value().toHex() + '\n');
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
SshHostKeyDatabase::KeyLookupResult SshHostKeyDatabase::matchHostKey(const QString &hostName,
|
||||||
|
const QByteArray &key) const
|
||||||
|
{
|
||||||
|
auto it = d->hostKeys.constFind(hostName);
|
||||||
|
if (it == d->hostKeys.constEnd())
|
||||||
|
return KeyLookupNoMatch;
|
||||||
|
if (it.value() == key)
|
||||||
|
return KeyLookupMatch;
|
||||||
|
return KeyLookupMismatch;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshHostKeyDatabase::insertHostKey(const QString &hostName, const QByteArray &key)
|
||||||
|
{
|
||||||
|
d->hostKeys.insert(hostName, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace QSsh
|
||||||
66
client/3rd/QtSsh/src/ssh/sshhostkeydatabase.h
Normal file
66
client/3rd/QtSsh/src/ssh/sshhostkeydatabase.h
Normal file
|
|
@ -0,0 +1,66 @@
|
||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2016 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "ssh_global.h"
|
||||||
|
|
||||||
|
#include <QSharedPointer>
|
||||||
|
|
||||||
|
QT_BEGIN_NAMESPACE
|
||||||
|
class QByteArray;
|
||||||
|
class QString;
|
||||||
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
|
namespace QSsh {
|
||||||
|
class SshHostKeyDatabase;
|
||||||
|
typedef QSharedPointer<SshHostKeyDatabase> SshHostKeyDatabasePtr;
|
||||||
|
|
||||||
|
class QSSH_EXPORT SshHostKeyDatabase
|
||||||
|
{
|
||||||
|
friend class QSharedPointer<SshHostKeyDatabase>; // To give create() access to our constructor.
|
||||||
|
|
||||||
|
public:
|
||||||
|
enum KeyLookupResult {
|
||||||
|
KeyLookupMatch,
|
||||||
|
KeyLookupNoMatch,
|
||||||
|
KeyLookupMismatch
|
||||||
|
};
|
||||||
|
|
||||||
|
~SshHostKeyDatabase();
|
||||||
|
|
||||||
|
bool load(const QString &filePath, QString *error = 0);
|
||||||
|
bool store(const QString &filePath, QString *error = 0) const;
|
||||||
|
KeyLookupResult matchHostKey(const QString &hostName, const QByteArray &key) const;
|
||||||
|
void insertHostKey(const QString &hostName, const QByteArray &key);
|
||||||
|
|
||||||
|
private:
|
||||||
|
SshHostKeyDatabase();
|
||||||
|
|
||||||
|
class SshHostKeyDatabasePrivate;
|
||||||
|
SshHostKeyDatabasePrivate * const d;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace QSsh
|
||||||
552
client/3rd/QtSsh/src/ssh/sshincomingpacket.cpp
Normal file
552
client/3rd/QtSsh/src/ssh/sshincomingpacket.cpp
Normal file
|
|
@ -0,0 +1,552 @@
|
||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2016 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#include "sshincomingpacket_p.h"
|
||||||
|
|
||||||
|
#include "ssh_global.h"
|
||||||
|
#include "sshbotanconversions_p.h"
|
||||||
|
#include "sshcapabilities_p.h"
|
||||||
|
#include "sshlogging_p.h"
|
||||||
|
|
||||||
|
namespace QSsh {
|
||||||
|
namespace Internal {
|
||||||
|
|
||||||
|
const QByteArray SshIncomingPacket::ExitStatusType("exit-status");
|
||||||
|
const QByteArray SshIncomingPacket::ExitSignalType("exit-signal");
|
||||||
|
const QByteArray SshIncomingPacket::ForwardedTcpIpType("forwarded-tcpip");
|
||||||
|
|
||||||
|
SshIncomingPacket::SshIncomingPacket() : m_serverSeqNr(0) { }
|
||||||
|
|
||||||
|
quint32 SshIncomingPacket::cipherBlockSize() const
|
||||||
|
{
|
||||||
|
return qMax(m_decrypter.cipherBlockSize(), 8U);
|
||||||
|
}
|
||||||
|
|
||||||
|
quint32 SshIncomingPacket::macLength() const
|
||||||
|
{
|
||||||
|
return m_decrypter.macLength();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshIncomingPacket::recreateKeys(const SshKeyExchange &keyExchange)
|
||||||
|
{
|
||||||
|
m_decrypter.recreateKeys(keyExchange);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshIncomingPacket::reset()
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
m_serverSeqNr = 0;
|
||||||
|
m_decrypter.clearKeys();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshIncomingPacket::consumeData(QByteArray &newData)
|
||||||
|
{
|
||||||
|
qCDebug(sshLog, "%s: current data size = %d, new data size = %d",
|
||||||
|
Q_FUNC_INFO, m_data.size(), newData.size());
|
||||||
|
|
||||||
|
if (isComplete() || newData.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Until we have reached the minimum packet size, we cannot decrypt the
|
||||||
|
* length field.
|
||||||
|
*/
|
||||||
|
const quint32 minSize = minPacketSize();
|
||||||
|
if (currentDataSize() < minSize) {
|
||||||
|
const int bytesToTake
|
||||||
|
= qMin<quint32>(minSize - currentDataSize(), newData.size());
|
||||||
|
moveFirstBytes(m_data, newData, bytesToTake);
|
||||||
|
qCDebug(sshLog, "Took %d bytes from new data", bytesToTake);
|
||||||
|
if (currentDataSize() < minSize)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (4 + length() + macLength() < currentDataSize())
|
||||||
|
throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR, "Server sent invalid packet.");
|
||||||
|
|
||||||
|
const int bytesToTake
|
||||||
|
= qMin<quint32>(length() + 4 + macLength() - currentDataSize(),
|
||||||
|
newData.size());
|
||||||
|
moveFirstBytes(m_data, newData, bytesToTake);
|
||||||
|
qCDebug(sshLog, "Took %d bytes from new data", bytesToTake);
|
||||||
|
if (isComplete()) {
|
||||||
|
qCDebug(sshLog, "Message complete. Overall size: %u, payload size: %u",
|
||||||
|
m_data.size(), m_length - paddingLength() - 1);
|
||||||
|
decrypt();
|
||||||
|
++m_serverSeqNr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshIncomingPacket::decrypt()
|
||||||
|
{
|
||||||
|
Q_ASSERT(isComplete());
|
||||||
|
const quint32 netDataLength = length() + 4;
|
||||||
|
m_decrypter.decrypt(m_data, cipherBlockSize(),
|
||||||
|
netDataLength - cipherBlockSize());
|
||||||
|
const QByteArray &mac = m_data.mid(netDataLength, macLength());
|
||||||
|
if (mac != generateMac(m_decrypter, m_serverSeqNr)) {
|
||||||
|
throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_MAC_ERROR,
|
||||||
|
"Message authentication failed.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshIncomingPacket::moveFirstBytes(QByteArray &target, QByteArray &source,
|
||||||
|
int n)
|
||||||
|
{
|
||||||
|
target.append(source.left(n));
|
||||||
|
source.remove(0, n);
|
||||||
|
}
|
||||||
|
|
||||||
|
SshKeyExchangeInit SshIncomingPacket::extractKeyExchangeInitData() const
|
||||||
|
{
|
||||||
|
Q_ASSERT(isComplete());
|
||||||
|
Q_ASSERT(type() == SSH_MSG_KEXINIT);
|
||||||
|
|
||||||
|
SshKeyExchangeInit exchangeData;
|
||||||
|
try {
|
||||||
|
quint32 offset = TypeOffset + 1;
|
||||||
|
std::memcpy(exchangeData.cookie, &m_data.constData()[offset],
|
||||||
|
sizeof exchangeData.cookie);
|
||||||
|
offset += sizeof exchangeData.cookie;
|
||||||
|
exchangeData.keyAlgorithms
|
||||||
|
= SshPacketParser::asNameList(m_data, &offset);
|
||||||
|
exchangeData.serverHostKeyAlgorithms
|
||||||
|
= SshPacketParser::asNameList(m_data, &offset);
|
||||||
|
exchangeData.encryptionAlgorithmsClientToServer
|
||||||
|
= SshPacketParser::asNameList(m_data, &offset);
|
||||||
|
exchangeData.encryptionAlgorithmsServerToClient
|
||||||
|
= SshPacketParser::asNameList(m_data, &offset);
|
||||||
|
exchangeData.macAlgorithmsClientToServer
|
||||||
|
= SshPacketParser::asNameList(m_data, &offset);
|
||||||
|
exchangeData.macAlgorithmsServerToClient
|
||||||
|
= SshPacketParser::asNameList(m_data, &offset);
|
||||||
|
exchangeData.compressionAlgorithmsClientToServer
|
||||||
|
= SshPacketParser::asNameList(m_data, &offset);
|
||||||
|
exchangeData.compressionAlgorithmsServerToClient
|
||||||
|
= SshPacketParser::asNameList(m_data, &offset);
|
||||||
|
exchangeData.languagesClientToServer
|
||||||
|
= SshPacketParser::asNameList(m_data, &offset);
|
||||||
|
exchangeData.languagesServerToClient
|
||||||
|
= SshPacketParser::asNameList(m_data, &offset);
|
||||||
|
exchangeData.firstKexPacketFollows
|
||||||
|
= SshPacketParser::asBool(m_data, &offset);
|
||||||
|
} catch (const SshPacketParseException &) {
|
||||||
|
throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
|
||||||
|
"Key exchange failed: Server sent invalid SSH_MSG_KEXINIT packet.");
|
||||||
|
}
|
||||||
|
return exchangeData;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void getHostKeySpecificReplyData(SshKeyExchangeReply &replyData,
|
||||||
|
const QByteArray &hostKeyAlgo, const QByteArray &input)
|
||||||
|
{
|
||||||
|
quint32 offset = 0;
|
||||||
|
if (hostKeyAlgo == SshCapabilities::PubKeyDss || hostKeyAlgo == SshCapabilities::PubKeyRsa) {
|
||||||
|
// DSS: p and q, RSA: e and n
|
||||||
|
replyData.hostKeyParameters << SshPacketParser::asBigInt(input, &offset);
|
||||||
|
replyData.hostKeyParameters << SshPacketParser::asBigInt(input, &offset);
|
||||||
|
|
||||||
|
// g and y
|
||||||
|
if (hostKeyAlgo == SshCapabilities::PubKeyDss) {
|
||||||
|
replyData.hostKeyParameters << SshPacketParser::asBigInt(input, &offset);
|
||||||
|
replyData.hostKeyParameters << SshPacketParser::asBigInt(input, &offset);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
QSSH_ASSERT_AND_RETURN(hostKeyAlgo.startsWith(SshCapabilities::PubKeyEcdsaPrefix));
|
||||||
|
if (SshPacketParser::asString(input, &offset)
|
||||||
|
!= hostKeyAlgo.mid(11)) { // Without "ecdsa-sha2-" prefix.
|
||||||
|
throw SshPacketParseException();
|
||||||
|
}
|
||||||
|
replyData.q = SshPacketParser::asString(input, &offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static QByteArray &padToWidth(QByteArray &data, int targetWidth)
|
||||||
|
{
|
||||||
|
return data.prepend(QByteArray(targetWidth - data.count(), 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
SshKeyExchangeReply SshIncomingPacket::extractKeyExchangeReply(const QByteArray &kexAlgo,
|
||||||
|
const QByteArray &hostKeyAlgo) const
|
||||||
|
{
|
||||||
|
Q_ASSERT(isComplete());
|
||||||
|
Q_ASSERT(type() == SSH_MSG_KEXDH_REPLY);
|
||||||
|
|
||||||
|
try {
|
||||||
|
SshKeyExchangeReply replyData;
|
||||||
|
quint32 topLevelOffset = TypeOffset + 1;
|
||||||
|
replyData.k_s = SshPacketParser::asString(m_data, &topLevelOffset);
|
||||||
|
quint32 k_sOffset = 0;
|
||||||
|
if (SshPacketParser::asString(replyData.k_s, &k_sOffset) != hostKeyAlgo)
|
||||||
|
throw SshPacketParseException();
|
||||||
|
getHostKeySpecificReplyData(replyData, hostKeyAlgo, replyData.k_s.mid(k_sOffset));
|
||||||
|
|
||||||
|
if (kexAlgo == SshCapabilities::DiffieHellmanGroup1Sha1) {
|
||||||
|
replyData.f = SshPacketParser::asBigInt(m_data, &topLevelOffset);
|
||||||
|
} else {
|
||||||
|
QSSH_ASSERT_AND_RETURN_VALUE(kexAlgo.startsWith(SshCapabilities::EcdhKexNamePrefix),
|
||||||
|
SshKeyExchangeReply());
|
||||||
|
replyData.q_s = SshPacketParser::asString(m_data, &topLevelOffset);
|
||||||
|
}
|
||||||
|
const QByteArray fullSignature = SshPacketParser::asString(m_data, &topLevelOffset);
|
||||||
|
quint32 sigOffset = 0;
|
||||||
|
if (SshPacketParser::asString(fullSignature, &sigOffset) != hostKeyAlgo)
|
||||||
|
throw SshPacketParseException();
|
||||||
|
replyData.signatureBlob = SshPacketParser::asString(fullSignature, &sigOffset);
|
||||||
|
if (hostKeyAlgo.startsWith(SshCapabilities::PubKeyEcdsaPrefix)) {
|
||||||
|
// Botan's PK_Verifier wants the signature in this format.
|
||||||
|
quint32 blobOffset = 0;
|
||||||
|
const Botan::BigInt r = SshPacketParser::asBigInt(replyData.signatureBlob, &blobOffset);
|
||||||
|
const Botan::BigInt s = SshPacketParser::asBigInt(replyData.signatureBlob, &blobOffset);
|
||||||
|
const int width = SshCapabilities::ecdsaIntegerWidthInBytes(hostKeyAlgo);
|
||||||
|
QByteArray encodedR = convertByteArray(Botan::BigInt::encode(r));
|
||||||
|
replyData.signatureBlob = padToWidth(encodedR, width);
|
||||||
|
QByteArray encodedS = convertByteArray(Botan::BigInt::encode(s));
|
||||||
|
replyData.signatureBlob += padToWidth(encodedS, width);
|
||||||
|
}
|
||||||
|
replyData.k_s.prepend(m_data.mid(TypeOffset + 1, 4));
|
||||||
|
return replyData;
|
||||||
|
} catch (const SshPacketParseException &) {
|
||||||
|
throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
|
||||||
|
"Key exchange failed: "
|
||||||
|
"Server sent invalid key exchange reply packet.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SshDisconnect SshIncomingPacket::extractDisconnect() const
|
||||||
|
{
|
||||||
|
Q_ASSERT(isComplete());
|
||||||
|
Q_ASSERT(type() == SSH_MSG_DISCONNECT);
|
||||||
|
|
||||||
|
SshDisconnect msg;
|
||||||
|
try {
|
||||||
|
quint32 offset = TypeOffset + 1;
|
||||||
|
msg.reasonCode = SshPacketParser::asUint32(m_data, &offset);
|
||||||
|
msg.description = SshPacketParser::asUserString(m_data, &offset);
|
||||||
|
msg.language = SshPacketParser::asString(m_data, &offset);
|
||||||
|
} catch (const SshPacketParseException &) {
|
||||||
|
throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
|
||||||
|
"Invalid SSH_MSG_DISCONNECT.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
SshUserAuthBanner SshIncomingPacket::extractUserAuthBanner() const
|
||||||
|
{
|
||||||
|
Q_ASSERT(isComplete());
|
||||||
|
Q_ASSERT(type() == SSH_MSG_USERAUTH_BANNER);
|
||||||
|
|
||||||
|
try {
|
||||||
|
SshUserAuthBanner msg;
|
||||||
|
quint32 offset = TypeOffset + 1;
|
||||||
|
msg.message = SshPacketParser::asUserString(m_data, &offset);
|
||||||
|
msg.language = SshPacketParser::asString(m_data, &offset);
|
||||||
|
return msg;
|
||||||
|
} catch (const SshPacketParseException &) {
|
||||||
|
throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
|
||||||
|
"Invalid SSH_MSG_USERAUTH_BANNER.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SshUserAuthInfoRequestPacket SshIncomingPacket::extractUserAuthInfoRequest() const
|
||||||
|
{
|
||||||
|
Q_ASSERT(isComplete());
|
||||||
|
Q_ASSERT(type() == SSH_MSG_USERAUTH_INFO_REQUEST);
|
||||||
|
|
||||||
|
try {
|
||||||
|
SshUserAuthInfoRequestPacket msg;
|
||||||
|
quint32 offset = TypeOffset + 1;
|
||||||
|
msg.name = SshPacketParser::asUserString(m_data, &offset);
|
||||||
|
msg.instruction = SshPacketParser::asUserString(m_data, &offset);
|
||||||
|
msg.languageTag = SshPacketParser::asString(m_data, &offset);
|
||||||
|
const quint32 promptCount = SshPacketParser::asUint32(m_data, &offset);
|
||||||
|
msg.prompts.reserve(promptCount);
|
||||||
|
msg.echos.reserve(promptCount);
|
||||||
|
for (quint32 i = 0; i < promptCount; ++i) {
|
||||||
|
msg.prompts << SshPacketParser::asUserString(m_data, &offset);
|
||||||
|
msg.echos << SshPacketParser::asBool(m_data, &offset);
|
||||||
|
}
|
||||||
|
return msg;
|
||||||
|
} catch (const SshPacketParseException &) {
|
||||||
|
throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
|
||||||
|
"Invalid SSH_MSG_USERAUTH_INFO_REQUEST.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SshDebug SshIncomingPacket::extractDebug() const
|
||||||
|
{
|
||||||
|
Q_ASSERT(isComplete());
|
||||||
|
Q_ASSERT(type() == SSH_MSG_DEBUG);
|
||||||
|
|
||||||
|
try {
|
||||||
|
SshDebug msg;
|
||||||
|
quint32 offset = TypeOffset + 1;
|
||||||
|
msg.display = SshPacketParser::asBool(m_data, &offset);
|
||||||
|
msg.message = SshPacketParser::asUserString(m_data, &offset);
|
||||||
|
msg.language = SshPacketParser::asString(m_data, &offset);
|
||||||
|
return msg;
|
||||||
|
} catch (const SshPacketParseException &) {
|
||||||
|
throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
|
||||||
|
"Invalid SSH_MSG_DEBUG.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SshRequestSuccess SshIncomingPacket::extractRequestSuccess() const
|
||||||
|
{
|
||||||
|
Q_ASSERT(isComplete());
|
||||||
|
Q_ASSERT(type() == SSH_MSG_REQUEST_SUCCESS);
|
||||||
|
|
||||||
|
try {
|
||||||
|
SshRequestSuccess msg;
|
||||||
|
quint32 offset = TypeOffset + 1;
|
||||||
|
msg.bindPort = SshPacketParser::asUint32(m_data, &offset);
|
||||||
|
return msg;
|
||||||
|
} catch (const SshPacketParseException &) {
|
||||||
|
throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
|
||||||
|
"Invalid SSH_MSG_REQUEST_SUCCESS.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SshUnimplemented SshIncomingPacket::extractUnimplemented() const
|
||||||
|
{
|
||||||
|
Q_ASSERT(isComplete());
|
||||||
|
Q_ASSERT(type() == SSH_MSG_UNIMPLEMENTED);
|
||||||
|
|
||||||
|
try {
|
||||||
|
SshUnimplemented msg;
|
||||||
|
quint32 offset = TypeOffset + 1;
|
||||||
|
msg.invalidMsgSeqNr = SshPacketParser::asUint32(m_data, &offset);
|
||||||
|
return msg;
|
||||||
|
} catch (const SshPacketParseException &) {
|
||||||
|
throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
|
||||||
|
"Invalid SSH_MSG_UNIMPLEMENTED.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SshChannelOpen SshIncomingPacket::extractChannelOpen() const
|
||||||
|
{
|
||||||
|
Q_ASSERT(isComplete());
|
||||||
|
Q_ASSERT(type() == SSH_MSG_CHANNEL_OPEN);
|
||||||
|
|
||||||
|
SshChannelOpen open;
|
||||||
|
try {
|
||||||
|
quint32 offset = TypeOffset + 1;
|
||||||
|
QByteArray type = SshPacketParser::asString(m_data, &offset);
|
||||||
|
open.remoteChannel = SshPacketParser::asUint32(m_data, &offset);
|
||||||
|
open.remoteWindowSize = SshPacketParser::asUint32(m_data, &offset);
|
||||||
|
open.remoteMaxPacketSize = SshPacketParser::asUint32(m_data, &offset);
|
||||||
|
if (type == ForwardedTcpIpType) {
|
||||||
|
open.remoteAddress = SshPacketParser::asString(m_data, &offset);
|
||||||
|
open.remotePort = SshPacketParser::asUint32(m_data, &offset);
|
||||||
|
} else {
|
||||||
|
open.remotePort = 0;
|
||||||
|
}
|
||||||
|
} catch (const SshPacketParseException &) {
|
||||||
|
throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
|
||||||
|
"Server sent invalid SSH_MSG_CHANNEL_OPEN packet.");
|
||||||
|
}
|
||||||
|
return open;
|
||||||
|
}
|
||||||
|
|
||||||
|
SshChannelOpenFailure SshIncomingPacket::extractChannelOpenFailure() const
|
||||||
|
{
|
||||||
|
Q_ASSERT(isComplete());
|
||||||
|
Q_ASSERT(type() == SSH_MSG_CHANNEL_OPEN_FAILURE);
|
||||||
|
|
||||||
|
SshChannelOpenFailure openFailure;
|
||||||
|
try {
|
||||||
|
quint32 offset = TypeOffset + 1;
|
||||||
|
openFailure.localChannel = SshPacketParser::asUint32(m_data, &offset);
|
||||||
|
openFailure.reasonCode = SshPacketParser::asUint32(m_data, &offset);
|
||||||
|
openFailure.reasonString = QString::fromLocal8Bit(SshPacketParser::asString(m_data, &offset));
|
||||||
|
openFailure.language = SshPacketParser::asString(m_data, &offset);
|
||||||
|
} catch (const SshPacketParseException &) {
|
||||||
|
throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
|
||||||
|
"Server sent invalid SSH_MSG_CHANNEL_OPEN_FAILURE packet.");
|
||||||
|
}
|
||||||
|
return openFailure;
|
||||||
|
}
|
||||||
|
|
||||||
|
SshChannelOpenConfirmation SshIncomingPacket::extractChannelOpenConfirmation() const
|
||||||
|
{
|
||||||
|
Q_ASSERT(isComplete());
|
||||||
|
Q_ASSERT(type() == SSH_MSG_CHANNEL_OPEN_CONFIRMATION);
|
||||||
|
|
||||||
|
SshChannelOpenConfirmation confirmation;
|
||||||
|
try {
|
||||||
|
quint32 offset = TypeOffset + 1;
|
||||||
|
confirmation.localChannel = SshPacketParser::asUint32(m_data, &offset);
|
||||||
|
confirmation.remoteChannel = SshPacketParser::asUint32(m_data, &offset);
|
||||||
|
confirmation.remoteWindowSize = SshPacketParser::asUint32(m_data, &offset);
|
||||||
|
confirmation.remoteMaxPacketSize = SshPacketParser::asUint32(m_data, &offset);
|
||||||
|
} catch (const SshPacketParseException &) {
|
||||||
|
throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
|
||||||
|
"Server sent invalid SSH_MSG_CHANNEL_OPEN_CONFIRMATION packet.");
|
||||||
|
}
|
||||||
|
return confirmation;
|
||||||
|
}
|
||||||
|
|
||||||
|
SshChannelWindowAdjust SshIncomingPacket::extractWindowAdjust() const
|
||||||
|
{
|
||||||
|
Q_ASSERT(isComplete());
|
||||||
|
Q_ASSERT(type() == SSH_MSG_CHANNEL_WINDOW_ADJUST);
|
||||||
|
|
||||||
|
SshChannelWindowAdjust adjust;
|
||||||
|
try {
|
||||||
|
quint32 offset = TypeOffset + 1;
|
||||||
|
adjust.localChannel = SshPacketParser::asUint32(m_data, &offset);
|
||||||
|
adjust.bytesToAdd = SshPacketParser::asUint32(m_data, &offset);
|
||||||
|
} catch (const SshPacketParseException &) {
|
||||||
|
throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
|
||||||
|
"Invalid SSH_MSG_CHANNEL_WINDOW_ADJUST packet.");
|
||||||
|
}
|
||||||
|
return adjust;
|
||||||
|
}
|
||||||
|
|
||||||
|
SshChannelData SshIncomingPacket::extractChannelData() const
|
||||||
|
{
|
||||||
|
Q_ASSERT(isComplete());
|
||||||
|
Q_ASSERT(type() == SSH_MSG_CHANNEL_DATA);
|
||||||
|
|
||||||
|
SshChannelData data;
|
||||||
|
try {
|
||||||
|
quint32 offset = TypeOffset + 1;
|
||||||
|
data.localChannel = SshPacketParser::asUint32(m_data, &offset);
|
||||||
|
data.data = SshPacketParser::asString(m_data, &offset);
|
||||||
|
} catch (const SshPacketParseException &) {
|
||||||
|
throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
|
||||||
|
"Invalid SSH_MSG_CHANNEL_DATA packet.");
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
SshChannelExtendedData SshIncomingPacket::extractChannelExtendedData() const
|
||||||
|
{
|
||||||
|
Q_ASSERT(isComplete());
|
||||||
|
Q_ASSERT(type() == SSH_MSG_CHANNEL_EXTENDED_DATA);
|
||||||
|
|
||||||
|
SshChannelExtendedData data;
|
||||||
|
try {
|
||||||
|
quint32 offset = TypeOffset + 1;
|
||||||
|
data.localChannel = SshPacketParser::asUint32(m_data, &offset);
|
||||||
|
data.type = SshPacketParser::asUint32(m_data, &offset);
|
||||||
|
data.data = SshPacketParser::asString(m_data, &offset);
|
||||||
|
} catch (const SshPacketParseException &) {
|
||||||
|
throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
|
||||||
|
"Invalid SSH_MSG_CHANNEL_EXTENDED_DATA packet.");
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
SshChannelExitStatus SshIncomingPacket::extractChannelExitStatus() const
|
||||||
|
{
|
||||||
|
Q_ASSERT(isComplete());
|
||||||
|
Q_ASSERT(type() == SSH_MSG_CHANNEL_REQUEST);
|
||||||
|
|
||||||
|
SshChannelExitStatus exitStatus;
|
||||||
|
try {
|
||||||
|
quint32 offset = TypeOffset + 1;
|
||||||
|
exitStatus.localChannel = SshPacketParser::asUint32(m_data, &offset);
|
||||||
|
const QByteArray &type = SshPacketParser::asString(m_data, &offset);
|
||||||
|
Q_ASSERT(type == ExitStatusType);
|
||||||
|
Q_UNUSED(type);
|
||||||
|
if (SshPacketParser::asBool(m_data, &offset))
|
||||||
|
throw SshPacketParseException();
|
||||||
|
exitStatus.exitStatus = SshPacketParser::asUint32(m_data, &offset);
|
||||||
|
} catch (const SshPacketParseException &) {
|
||||||
|
throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
|
||||||
|
"Invalid exit-status packet.");
|
||||||
|
}
|
||||||
|
return exitStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
SshChannelExitSignal SshIncomingPacket::extractChannelExitSignal() const
|
||||||
|
{
|
||||||
|
Q_ASSERT(isComplete());
|
||||||
|
Q_ASSERT(type() == SSH_MSG_CHANNEL_REQUEST);
|
||||||
|
|
||||||
|
SshChannelExitSignal exitSignal;
|
||||||
|
try {
|
||||||
|
quint32 offset = TypeOffset + 1;
|
||||||
|
exitSignal.localChannel = SshPacketParser::asUint32(m_data, &offset);
|
||||||
|
const QByteArray &type = SshPacketParser::asString(m_data, &offset);
|
||||||
|
Q_ASSERT(type == ExitSignalType);
|
||||||
|
Q_UNUSED(type);
|
||||||
|
if (SshPacketParser::asBool(m_data, &offset))
|
||||||
|
throw SshPacketParseException();
|
||||||
|
exitSignal.signal = SshPacketParser::asString(m_data, &offset);
|
||||||
|
exitSignal.coreDumped = SshPacketParser::asBool(m_data, &offset);
|
||||||
|
exitSignal.error = SshPacketParser::asUserString(m_data, &offset);
|
||||||
|
exitSignal.language = SshPacketParser::asString(m_data, &offset);
|
||||||
|
} catch (const SshPacketParseException &) {
|
||||||
|
throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
|
||||||
|
"Invalid exit-signal packet.");
|
||||||
|
}
|
||||||
|
return exitSignal;
|
||||||
|
}
|
||||||
|
|
||||||
|
quint32 SshIncomingPacket::extractRecipientChannel() const
|
||||||
|
{
|
||||||
|
Q_ASSERT(isComplete());
|
||||||
|
|
||||||
|
try {
|
||||||
|
quint32 offset = TypeOffset + 1;
|
||||||
|
return SshPacketParser::asUint32(m_data, &offset);
|
||||||
|
} catch (const SshPacketParseException &) {
|
||||||
|
throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
|
||||||
|
"Server sent invalid packet.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray SshIncomingPacket::extractChannelRequestType() const
|
||||||
|
{
|
||||||
|
Q_ASSERT(isComplete());
|
||||||
|
Q_ASSERT(type() == SSH_MSG_CHANNEL_REQUEST);
|
||||||
|
|
||||||
|
try {
|
||||||
|
quint32 offset = TypeOffset + 1;
|
||||||
|
SshPacketParser::asUint32(m_data, &offset);
|
||||||
|
return SshPacketParser::asString(m_data, &offset);
|
||||||
|
} catch (const SshPacketParseException &) {
|
||||||
|
throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
|
||||||
|
"Invalid SSH_MSG_CHANNEL_REQUEST packet.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshIncomingPacket::calculateLength() const
|
||||||
|
{
|
||||||
|
Q_ASSERT(currentDataSize() >= minPacketSize());
|
||||||
|
qCDebug(sshLog, "Length field before decryption: %d-%d-%d-%d", m_data.at(0) & 0xff,
|
||||||
|
m_data.at(1) & 0xff, m_data.at(2) & 0xff, m_data.at(3) & 0xff);
|
||||||
|
m_decrypter.decrypt(m_data, 0, cipherBlockSize());
|
||||||
|
qCDebug(sshLog, "Length field after decryption: %d-%d-%d-%d", m_data.at(0) & 0xff, m_data.at(1) & 0xff, m_data.at(2) & 0xff, m_data.at(3) & 0xff);
|
||||||
|
qCDebug(sshLog, "message type = %d", m_data.at(TypeOffset));
|
||||||
|
m_length = SshPacketParser::asUint32(m_data, static_cast<quint32>(0));
|
||||||
|
qCDebug(sshLog, "decrypted length is %u", m_length);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Internal
|
||||||
|
} // namespace QSsh
|
||||||
213
client/3rd/QtSsh/src/ssh/sshincomingpacket_p.h
Normal file
213
client/3rd/QtSsh/src/ssh/sshincomingpacket_p.h
Normal file
|
|
@ -0,0 +1,213 @@
|
||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2016 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "sshpacket_p.h"
|
||||||
|
|
||||||
|
#include "sshcryptofacility_p.h"
|
||||||
|
#include "sshpacketparser_p.h"
|
||||||
|
|
||||||
|
#include <QStringList>
|
||||||
|
|
||||||
|
namespace QSsh {
|
||||||
|
namespace Internal {
|
||||||
|
|
||||||
|
class SshKeyExchange;
|
||||||
|
|
||||||
|
struct SshKeyExchangeInit
|
||||||
|
{
|
||||||
|
char cookie[16];
|
||||||
|
SshNameList keyAlgorithms;
|
||||||
|
SshNameList serverHostKeyAlgorithms;
|
||||||
|
SshNameList encryptionAlgorithmsClientToServer;
|
||||||
|
SshNameList encryptionAlgorithmsServerToClient;
|
||||||
|
SshNameList macAlgorithmsClientToServer;
|
||||||
|
SshNameList macAlgorithmsServerToClient;
|
||||||
|
SshNameList compressionAlgorithmsClientToServer;
|
||||||
|
SshNameList compressionAlgorithmsServerToClient;
|
||||||
|
SshNameList languagesClientToServer;
|
||||||
|
SshNameList languagesServerToClient;
|
||||||
|
bool firstKexPacketFollows;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SshKeyExchangeReply
|
||||||
|
{
|
||||||
|
QByteArray k_s;
|
||||||
|
QList<Botan::BigInt> hostKeyParameters; // DSS: p, q, g, y. RSA: e, n.
|
||||||
|
QByteArray q; // For ECDSA host keys only.
|
||||||
|
Botan::BigInt f; // For DH only.
|
||||||
|
QByteArray q_s; // For ECDH only.
|
||||||
|
QByteArray signatureBlob;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SshDisconnect
|
||||||
|
{
|
||||||
|
quint32 reasonCode;
|
||||||
|
QString description;
|
||||||
|
QByteArray language;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SshUserAuthBanner
|
||||||
|
{
|
||||||
|
QString message;
|
||||||
|
QByteArray language;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SshUserAuthInfoRequestPacket
|
||||||
|
{
|
||||||
|
QString name;
|
||||||
|
QString instruction;
|
||||||
|
QByteArray languageTag;
|
||||||
|
QStringList prompts;
|
||||||
|
QList<bool> echos;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SshDebug
|
||||||
|
{
|
||||||
|
bool display;
|
||||||
|
QString message;
|
||||||
|
QByteArray language;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SshUnimplemented
|
||||||
|
{
|
||||||
|
quint32 invalidMsgSeqNr;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SshRequestSuccess
|
||||||
|
{
|
||||||
|
quint32 bindPort;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SshChannelOpen
|
||||||
|
{
|
||||||
|
quint32 remoteChannel;
|
||||||
|
quint32 remoteWindowSize;
|
||||||
|
quint32 remoteMaxPacketSize;
|
||||||
|
QByteArray remoteAddress;
|
||||||
|
quint32 remotePort;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SshChannelOpenFailure
|
||||||
|
{
|
||||||
|
quint32 localChannel;
|
||||||
|
quint32 reasonCode;
|
||||||
|
QString reasonString;
|
||||||
|
QByteArray language;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SshChannelOpenConfirmation
|
||||||
|
{
|
||||||
|
quint32 localChannel;
|
||||||
|
quint32 remoteChannel;
|
||||||
|
quint32 remoteWindowSize;
|
||||||
|
quint32 remoteMaxPacketSize;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SshChannelWindowAdjust
|
||||||
|
{
|
||||||
|
quint32 localChannel;
|
||||||
|
quint32 bytesToAdd;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SshChannelData
|
||||||
|
{
|
||||||
|
quint32 localChannel;
|
||||||
|
QByteArray data;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SshChannelExtendedData
|
||||||
|
{
|
||||||
|
quint32 localChannel;
|
||||||
|
quint32 type;
|
||||||
|
QByteArray data;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SshChannelExitStatus
|
||||||
|
{
|
||||||
|
quint32 localChannel;
|
||||||
|
quint32 exitStatus;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SshChannelExitSignal
|
||||||
|
{
|
||||||
|
quint32 localChannel;
|
||||||
|
QByteArray signal;
|
||||||
|
bool coreDumped;
|
||||||
|
QString error;
|
||||||
|
QByteArray language;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SshIncomingPacket : public AbstractSshPacket
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SshIncomingPacket();
|
||||||
|
|
||||||
|
void consumeData(QByteArray &data);
|
||||||
|
void recreateKeys(const SshKeyExchange &keyExchange);
|
||||||
|
void reset();
|
||||||
|
|
||||||
|
SshKeyExchangeInit extractKeyExchangeInitData() const;
|
||||||
|
SshKeyExchangeReply extractKeyExchangeReply(const QByteArray &kexAlgo,
|
||||||
|
const QByteArray &hostKeyAlgo) const;
|
||||||
|
SshDisconnect extractDisconnect() const;
|
||||||
|
SshUserAuthBanner extractUserAuthBanner() const;
|
||||||
|
SshUserAuthInfoRequestPacket extractUserAuthInfoRequest() const;
|
||||||
|
SshDebug extractDebug() const;
|
||||||
|
SshRequestSuccess extractRequestSuccess() const;
|
||||||
|
SshUnimplemented extractUnimplemented() const;
|
||||||
|
|
||||||
|
SshChannelOpen extractChannelOpen() const;
|
||||||
|
SshChannelOpenFailure extractChannelOpenFailure() const;
|
||||||
|
SshChannelOpenConfirmation extractChannelOpenConfirmation() const;
|
||||||
|
SshChannelWindowAdjust extractWindowAdjust() const;
|
||||||
|
SshChannelData extractChannelData() const;
|
||||||
|
SshChannelExtendedData extractChannelExtendedData() const;
|
||||||
|
SshChannelExitStatus extractChannelExitStatus() const;
|
||||||
|
SshChannelExitSignal extractChannelExitSignal() const;
|
||||||
|
quint32 extractRecipientChannel() const;
|
||||||
|
QByteArray extractChannelRequestType() const;
|
||||||
|
|
||||||
|
quint32 serverSeqNr() const { return m_serverSeqNr; }
|
||||||
|
|
||||||
|
static const QByteArray ExitStatusType;
|
||||||
|
static const QByteArray ExitSignalType;
|
||||||
|
static const QByteArray ForwardedTcpIpType;
|
||||||
|
|
||||||
|
private:
|
||||||
|
virtual quint32 cipherBlockSize() const;
|
||||||
|
virtual quint32 macLength() const;
|
||||||
|
virtual void calculateLength() const;
|
||||||
|
|
||||||
|
void decrypt();
|
||||||
|
void moveFirstBytes(QByteArray &target, QByteArray &source, int n);
|
||||||
|
|
||||||
|
quint32 m_serverSeqNr;
|
||||||
|
SshDecryptionFacility m_decrypter;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Internal
|
||||||
|
} // namespace QSsh
|
||||||
49
client/3rd/QtSsh/src/ssh/sshinit.cpp
Normal file
49
client/3rd/QtSsh/src/ssh/sshinit.cpp
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2016 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#include "sshinit_p.h"
|
||||||
|
|
||||||
|
#include <botan/botan.h>
|
||||||
|
|
||||||
|
#include <QMutex>
|
||||||
|
#include <QMutexLocker>
|
||||||
|
|
||||||
|
namespace QSsh {
|
||||||
|
namespace Internal {
|
||||||
|
|
||||||
|
static bool initialized = false;
|
||||||
|
static QMutex initMutex;
|
||||||
|
|
||||||
|
void initSsh()
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&initMutex);
|
||||||
|
if (!initialized) {
|
||||||
|
Botan::LibraryInitializer::initialize("thread_safe=true");
|
||||||
|
initialized = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Internal
|
||||||
|
} // namespace QSsh
|
||||||
32
client/3rd/QtSsh/src/ssh/sshinit_p.h
Normal file
32
client/3rd/QtSsh/src/ssh/sshinit_p.h
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2016 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
namespace QSsh {
|
||||||
|
namespace Internal {
|
||||||
|
|
||||||
|
void initSsh();
|
||||||
|
|
||||||
|
} // namespace Internal
|
||||||
|
} // namespace QSsh
|
||||||
174
client/3rd/QtSsh/src/ssh/sshkeycreationdialog.cpp
Normal file
174
client/3rd/QtSsh/src/ssh/sshkeycreationdialog.cpp
Normal file
|
|
@ -0,0 +1,174 @@
|
||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2016 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#include "sshkeycreationdialog.h"
|
||||||
|
#include "ui_sshkeycreationdialog.h"
|
||||||
|
|
||||||
|
#include "sshkeygenerator.h"
|
||||||
|
|
||||||
|
#include <QDir>
|
||||||
|
#include <QFile>
|
||||||
|
#include <QFileDialog>
|
||||||
|
#include <QFileInfo>
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QMessageBox>
|
||||||
|
#include <QStandardPaths>
|
||||||
|
|
||||||
|
namespace QSsh {
|
||||||
|
|
||||||
|
SshKeyCreationDialog::SshKeyCreationDialog(QWidget *parent)
|
||||||
|
: QDialog(parent), m_keyGenerator(0), m_ui(new Ui::SshKeyCreationDialog)
|
||||||
|
{
|
||||||
|
m_ui->setupUi(this);
|
||||||
|
// Not using Utils::PathChooser::browseButtonLabel to avoid dependency
|
||||||
|
#ifdef Q_OS_MAC
|
||||||
|
m_ui->privateKeyFileButton->setText(tr("Choose..."));
|
||||||
|
#else
|
||||||
|
m_ui->privateKeyFileButton->setText(tr("Browse..."));
|
||||||
|
#endif
|
||||||
|
const QString defaultPath = QStandardPaths::writableLocation(QStandardPaths::HomeLocation)
|
||||||
|
+ QLatin1String("/.ssh/qtc_id");
|
||||||
|
setPrivateKeyFile(defaultPath);
|
||||||
|
|
||||||
|
connect(m_ui->rsa, &QRadioButton::toggled,
|
||||||
|
this, &SshKeyCreationDialog::keyTypeChanged);
|
||||||
|
connect(m_ui->dsa, &QRadioButton::toggled,
|
||||||
|
this, &SshKeyCreationDialog::keyTypeChanged);
|
||||||
|
connect(m_ui->privateKeyFileButton, &QPushButton::clicked,
|
||||||
|
this, &SshKeyCreationDialog::handleBrowseButtonClicked);
|
||||||
|
connect(m_ui->generateButton, &QPushButton::clicked,
|
||||||
|
this, &SshKeyCreationDialog::generateKeys);
|
||||||
|
keyTypeChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
SshKeyCreationDialog::~SshKeyCreationDialog()
|
||||||
|
{
|
||||||
|
delete m_keyGenerator;
|
||||||
|
delete m_ui;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshKeyCreationDialog::keyTypeChanged()
|
||||||
|
{
|
||||||
|
m_ui->comboBox->clear();
|
||||||
|
QStringList keySizes;
|
||||||
|
if (m_ui->rsa->isChecked())
|
||||||
|
keySizes << QLatin1String("1024") << QLatin1String("2048") << QLatin1String("4096");
|
||||||
|
else if (m_ui->ecdsa->isChecked())
|
||||||
|
keySizes << QLatin1String("256") << QLatin1String("384") << QLatin1String("521");
|
||||||
|
else if (m_ui->dsa->isChecked())
|
||||||
|
keySizes << QLatin1String("1024");
|
||||||
|
m_ui->comboBox->addItems(keySizes);
|
||||||
|
if (!keySizes.isEmpty())
|
||||||
|
m_ui->comboBox->setCurrentIndex(0);
|
||||||
|
m_ui->comboBox->setEnabled(!keySizes.isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshKeyCreationDialog::generateKeys()
|
||||||
|
{
|
||||||
|
if (userForbidsOverwriting())
|
||||||
|
return;
|
||||||
|
|
||||||
|
const SshKeyGenerator::KeyType keyType = m_ui->rsa->isChecked()
|
||||||
|
? SshKeyGenerator::Rsa : m_ui->dsa->isChecked()
|
||||||
|
? SshKeyGenerator::Dsa : SshKeyGenerator::Ecdsa;
|
||||||
|
|
||||||
|
if (!m_keyGenerator)
|
||||||
|
m_keyGenerator = new SshKeyGenerator;
|
||||||
|
|
||||||
|
QApplication::setOverrideCursor(Qt::BusyCursor);
|
||||||
|
const bool success = m_keyGenerator->generateKeys(keyType, SshKeyGenerator::Mixed,
|
||||||
|
m_ui->comboBox->currentText().toUShort());
|
||||||
|
QApplication::restoreOverrideCursor();
|
||||||
|
|
||||||
|
if (success)
|
||||||
|
saveKeys();
|
||||||
|
else
|
||||||
|
QMessageBox::critical(this, tr("Key Generation Failed"), m_keyGenerator->error());
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshKeyCreationDialog::handleBrowseButtonClicked()
|
||||||
|
{
|
||||||
|
const QString filePath = QFileDialog::getSaveFileName(this, tr("Choose Private Key File Name"));
|
||||||
|
if (!filePath.isEmpty())
|
||||||
|
setPrivateKeyFile(filePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshKeyCreationDialog::setPrivateKeyFile(const QString &filePath)
|
||||||
|
{
|
||||||
|
m_ui->privateKeyFileValueLabel->setText(filePath);
|
||||||
|
m_ui->generateButton->setEnabled(!privateKeyFilePath().isEmpty());
|
||||||
|
m_ui->publicKeyFileLabel->setText(filePath + QLatin1String(".pub"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshKeyCreationDialog::saveKeys()
|
||||||
|
{
|
||||||
|
const QString parentDir = QFileInfo(privateKeyFilePath()).dir().path();
|
||||||
|
if (!QDir::root().mkpath(parentDir)) {
|
||||||
|
QMessageBox::critical(this, tr("Cannot Save Key File"),
|
||||||
|
tr("Failed to create directory: \"%1\".").arg(parentDir));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QFile privateKeyFile(privateKeyFilePath());
|
||||||
|
if (!privateKeyFile.open(QIODevice::WriteOnly)
|
||||||
|
|| !privateKeyFile.write(m_keyGenerator->privateKey())) {
|
||||||
|
QMessageBox::critical(this, tr("Cannot Save Private Key File"),
|
||||||
|
tr("The private key file could not be saved: %1").arg(privateKeyFile.errorString()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
QFile::setPermissions(privateKeyFilePath(), QFile::ReadOwner | QFile::WriteOwner);
|
||||||
|
|
||||||
|
QFile publicKeyFile(publicKeyFilePath());
|
||||||
|
if (!publicKeyFile.open(QIODevice::WriteOnly)
|
||||||
|
|| !publicKeyFile.write(m_keyGenerator->publicKey())) {
|
||||||
|
QMessageBox::critical(this, tr("Cannot Save Public Key File"),
|
||||||
|
tr("The public key file could not be saved: %1").arg(publicKeyFile.errorString()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
accept();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SshKeyCreationDialog::userForbidsOverwriting()
|
||||||
|
{
|
||||||
|
if (!QFileInfo::exists(privateKeyFilePath()) && !QFileInfo::exists(publicKeyFilePath()))
|
||||||
|
return false;
|
||||||
|
const QMessageBox::StandardButton reply = QMessageBox::question(this, tr("File Exists"),
|
||||||
|
tr("There already is a file of that name. Do you want to overwrite it?"),
|
||||||
|
QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
|
||||||
|
return reply != QMessageBox::Yes;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString SshKeyCreationDialog::privateKeyFilePath() const
|
||||||
|
{
|
||||||
|
return m_ui->privateKeyFileValueLabel->text();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString SshKeyCreationDialog::publicKeyFilePath() const
|
||||||
|
{
|
||||||
|
return m_ui->publicKeyFileLabel->text();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace QSsh
|
||||||
60
client/3rd/QtSsh/src/ssh/sshkeycreationdialog.h
Normal file
60
client/3rd/QtSsh/src/ssh/sshkeycreationdialog.h
Normal file
|
|
@ -0,0 +1,60 @@
|
||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2016 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "ssh_global.h"
|
||||||
|
|
||||||
|
#include <QDialog>
|
||||||
|
|
||||||
|
namespace QSsh {
|
||||||
|
class SshKeyGenerator;
|
||||||
|
|
||||||
|
namespace Ui { class SshKeyCreationDialog; }
|
||||||
|
|
||||||
|
class QSSH_EXPORT SshKeyCreationDialog : public QDialog
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
SshKeyCreationDialog(QWidget *parent = 0);
|
||||||
|
~SshKeyCreationDialog();
|
||||||
|
|
||||||
|
QString privateKeyFilePath() const;
|
||||||
|
QString publicKeyFilePath() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void keyTypeChanged();
|
||||||
|
void generateKeys();
|
||||||
|
void handleBrowseButtonClicked();
|
||||||
|
void setPrivateKeyFile(const QString &filePath);
|
||||||
|
void saveKeys();
|
||||||
|
bool userForbidsOverwriting();
|
||||||
|
|
||||||
|
private:
|
||||||
|
SshKeyGenerator *m_keyGenerator;
|
||||||
|
Ui::SshKeyCreationDialog *m_ui;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace QSsh
|
||||||
264
client/3rd/QtSsh/src/ssh/sshkeycreationdialog.ui
Normal file
264
client/3rd/QtSsh/src/ssh/sshkeycreationdialog.ui
Normal file
|
|
@ -0,0 +1,264 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>QSsh::SshKeyCreationDialog</class>
|
||||||
|
<widget class="QDialog" name="QSsh::SshKeyCreationDialog">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>380</width>
|
||||||
|
<height>231</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>SSH Key Configuration</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QGroupBox" name="groupBox">
|
||||||
|
<property name="title">
|
||||||
|
<string>Options</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QFormLayout" name="formLayout">
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QLabel" name="keyAlgo">
|
||||||
|
<property name="text">
|
||||||
|
<string>Key algorithm:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1">
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||||
|
<item>
|
||||||
|
<widget class="QRadioButton" name="rsa">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>&RSA</string>
|
||||||
|
</property>
|
||||||
|
<property name="checked">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QRadioButton" name="dsa">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>&DSA</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QRadioButton" name="ecdsa">
|
||||||
|
<property name="text">
|
||||||
|
<string>ECDSA</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="horizontalSpacer_3">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>40</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QLabel" name="keySize">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Key &size:</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>comboBox</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||||
|
<item>
|
||||||
|
<widget class="QComboBox" name="comboBox">
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="horizontalSpacer_2">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>40</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0">
|
||||||
|
<widget class="QLabel" name="privateKeyFileLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>Private key file:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="1">
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_4">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="privateKeyFileValueLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="privateKeyFileButton">
|
||||||
|
<property name="text">
|
||||||
|
<string>Browse...</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="horizontalSpacer_4">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>40</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="0">
|
||||||
|
<widget class="QLabel" name="label">
|
||||||
|
<property name="text">
|
||||||
|
<string>Public key file:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="1">
|
||||||
|
<widget class="QLabel" name="publicKeyFileLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="verticalSpacer">
|
||||||
|
<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>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
|
<property name="spacing">
|
||||||
|
<number>6</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="generateButton">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>&Generate And Save Key Pair</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="closeButton">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>&Cancel</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="horizontalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>0</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections>
|
||||||
|
<connection>
|
||||||
|
<sender>closeButton</sender>
|
||||||
|
<signal>clicked()</signal>
|
||||||
|
<receiver>QSsh::SshKeyCreationDialog</receiver>
|
||||||
|
<slot>close()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>195</x>
|
||||||
|
<y>184</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>381</x>
|
||||||
|
<y>107</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
</connections>
|
||||||
|
</ui>
|
||||||
273
client/3rd/QtSsh/src/ssh/sshkeyexchange.cpp
Normal file
273
client/3rd/QtSsh/src/ssh/sshkeyexchange.cpp
Normal file
|
|
@ -0,0 +1,273 @@
|
||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2016 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#include "sshkeyexchange_p.h"
|
||||||
|
|
||||||
|
#include "ssh_global.h"
|
||||||
|
#include "sshbotanconversions_p.h"
|
||||||
|
#include "sshcapabilities_p.h"
|
||||||
|
#include "sshlogging_p.h"
|
||||||
|
#include "sshsendfacility_p.h"
|
||||||
|
#include "sshexception_p.h"
|
||||||
|
#include "sshincomingpacket_p.h"
|
||||||
|
|
||||||
|
#include <botan/botan.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
using namespace Botan;
|
||||||
|
|
||||||
|
namespace QSsh {
|
||||||
|
namespace Internal {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
// For debugging
|
||||||
|
void printNameList(const char *listName, const SshNameList &list)
|
||||||
|
{
|
||||||
|
qCDebug(sshLog, "%s:", listName);
|
||||||
|
foreach (const QByteArray &name, list.names)
|
||||||
|
qCDebug(sshLog, "%s", name.constData());
|
||||||
|
}
|
||||||
|
|
||||||
|
void printData(const char *name, const QByteArray &data)
|
||||||
|
{
|
||||||
|
qCDebug(sshLog, "The client thinks the %s has length %d and is: %s", name, data.count(),
|
||||||
|
data.toHex().constData());
|
||||||
|
}
|
||||||
|
|
||||||
|
} // anonymous namespace
|
||||||
|
|
||||||
|
SshKeyExchange::SshKeyExchange(const SshConnectionParameters &connParams,
|
||||||
|
SshSendFacility &sendFacility)
|
||||||
|
: m_connParams(connParams), m_sendFacility(sendFacility)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
SshKeyExchange::~SshKeyExchange() {}
|
||||||
|
|
||||||
|
void SshKeyExchange::sendKexInitPacket(const QByteArray &serverId)
|
||||||
|
{
|
||||||
|
m_serverId = serverId;
|
||||||
|
m_clientKexInitPayload = m_sendFacility.sendKeyExchangeInitPacket();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SshKeyExchange::sendDhInitPacket(const SshIncomingPacket &serverKexInit)
|
||||||
|
{
|
||||||
|
qCDebug(sshLog, "server requests key exchange");
|
||||||
|
serverKexInit.printRawBytes();
|
||||||
|
SshKeyExchangeInit kexInitParams
|
||||||
|
= serverKexInit.extractKeyExchangeInitData();
|
||||||
|
|
||||||
|
printNameList("Key Algorithms", kexInitParams.keyAlgorithms);
|
||||||
|
printNameList("Server Host Key Algorithms", kexInitParams.serverHostKeyAlgorithms);
|
||||||
|
printNameList("Encryption algorithms client to server", kexInitParams.encryptionAlgorithmsClientToServer);
|
||||||
|
printNameList("Encryption algorithms server to client", kexInitParams.encryptionAlgorithmsServerToClient);
|
||||||
|
printNameList("MAC algorithms client to server", kexInitParams.macAlgorithmsClientToServer);
|
||||||
|
printNameList("MAC algorithms server to client", kexInitParams.macAlgorithmsServerToClient);
|
||||||
|
printNameList("Compression algorithms client to server", kexInitParams.compressionAlgorithmsClientToServer);
|
||||||
|
printNameList("Compression algorithms client to server", kexInitParams.compressionAlgorithmsClientToServer);
|
||||||
|
printNameList("Languages client to server", kexInitParams.languagesClientToServer);
|
||||||
|
printNameList("Languages server to client", kexInitParams.languagesServerToClient);
|
||||||
|
qCDebug(sshLog, "First packet follows: %d", kexInitParams.firstKexPacketFollows);
|
||||||
|
|
||||||
|
m_kexAlgoName = SshCapabilities::findBestMatch(SshCapabilities::KeyExchangeMethods,
|
||||||
|
kexInitParams.keyAlgorithms.names);
|
||||||
|
m_serverHostKeyAlgo = SshCapabilities::findBestMatch(SshCapabilities::PublicKeyAlgorithms,
|
||||||
|
kexInitParams.serverHostKeyAlgorithms.names);
|
||||||
|
determineHashingAlgorithm(kexInitParams, true);
|
||||||
|
determineHashingAlgorithm(kexInitParams, false);
|
||||||
|
|
||||||
|
m_encryptionAlgo
|
||||||
|
= SshCapabilities::findBestMatch(SshCapabilities::EncryptionAlgorithms,
|
||||||
|
kexInitParams.encryptionAlgorithmsClientToServer.names);
|
||||||
|
m_decryptionAlgo
|
||||||
|
= SshCapabilities::findBestMatch(SshCapabilities::EncryptionAlgorithms,
|
||||||
|
kexInitParams.encryptionAlgorithmsServerToClient.names);
|
||||||
|
SshCapabilities::findBestMatch(SshCapabilities::CompressionAlgorithms,
|
||||||
|
kexInitParams.compressionAlgorithmsClientToServer.names);
|
||||||
|
SshCapabilities::findBestMatch(SshCapabilities::CompressionAlgorithms,
|
||||||
|
kexInitParams.compressionAlgorithmsServerToClient.names);
|
||||||
|
|
||||||
|
AutoSeeded_RNG rng;
|
||||||
|
if (m_kexAlgoName.startsWith(SshCapabilities::EcdhKexNamePrefix)) {
|
||||||
|
m_ecdhKey.reset(new ECDH_PrivateKey(rng, EC_Group(botanKeyExchangeAlgoName(m_kexAlgoName))));
|
||||||
|
m_sendFacility.sendKeyEcdhInitPacket(convertByteArray(m_ecdhKey->public_value()));
|
||||||
|
} else {
|
||||||
|
m_dhKey.reset(new DH_PrivateKey(rng, DL_Group(botanKeyExchangeAlgoName(m_kexAlgoName))));
|
||||||
|
m_sendFacility.sendKeyDhInitPacket(m_dhKey->get_y());
|
||||||
|
}
|
||||||
|
|
||||||
|
m_serverKexInitPayload = serverKexInit.payLoad();
|
||||||
|
return kexInitParams.firstKexPacketFollows;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshKeyExchange::sendNewKeysPacket(const SshIncomingPacket &dhReply,
|
||||||
|
const QByteArray &clientId)
|
||||||
|
{
|
||||||
|
|
||||||
|
const SshKeyExchangeReply &reply
|
||||||
|
= dhReply.extractKeyExchangeReply(m_kexAlgoName, m_serverHostKeyAlgo);
|
||||||
|
if (m_dhKey && (reply.f <= 0 || reply.f >= m_dhKey->group_p())) {
|
||||||
|
throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
|
||||||
|
"Server sent invalid f.");
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray concatenatedData = AbstractSshPacket::encodeString(clientId);
|
||||||
|
concatenatedData += AbstractSshPacket::encodeString(m_serverId);
|
||||||
|
concatenatedData += AbstractSshPacket::encodeString(m_clientKexInitPayload);
|
||||||
|
concatenatedData += AbstractSshPacket::encodeString(m_serverKexInitPayload);
|
||||||
|
concatenatedData += reply.k_s;
|
||||||
|
|
||||||
|
printData("Client Id", AbstractSshPacket::encodeString(clientId));
|
||||||
|
printData("Server Id", AbstractSshPacket::encodeString(m_serverId));
|
||||||
|
printData("Client Payload", AbstractSshPacket::encodeString(m_clientKexInitPayload));
|
||||||
|
printData("Server payload", AbstractSshPacket::encodeString(m_serverKexInitPayload));
|
||||||
|
printData("K_S", reply.k_s);
|
||||||
|
|
||||||
|
SecureVector<byte> encodedK;
|
||||||
|
if (m_dhKey) {
|
||||||
|
concatenatedData += AbstractSshPacket::encodeMpInt(m_dhKey->get_y());
|
||||||
|
concatenatedData += AbstractSshPacket::encodeMpInt(reply.f);
|
||||||
|
DH_KA_Operation dhOp(*m_dhKey);
|
||||||
|
SecureVector<byte> encodedF = BigInt::encode(reply.f);
|
||||||
|
encodedK = dhOp.agree(encodedF, encodedF.size());
|
||||||
|
printData("y", AbstractSshPacket::encodeMpInt(m_dhKey->get_y()));
|
||||||
|
printData("f", AbstractSshPacket::encodeMpInt(reply.f));
|
||||||
|
m_dhKey.reset(nullptr);
|
||||||
|
} else {
|
||||||
|
Q_ASSERT(m_ecdhKey);
|
||||||
|
concatenatedData // Q_C.
|
||||||
|
+= AbstractSshPacket::encodeString(convertByteArray(m_ecdhKey->public_value()));
|
||||||
|
concatenatedData += AbstractSshPacket::encodeString(reply.q_s);
|
||||||
|
ECDH_KA_Operation ecdhOp(*m_ecdhKey);
|
||||||
|
encodedK = ecdhOp.agree(convertByteArray(reply.q_s), reply.q_s.count());
|
||||||
|
m_ecdhKey.reset(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
const BigInt k = BigInt::decode(encodedK);
|
||||||
|
m_k = AbstractSshPacket::encodeMpInt(k); // Roundtrip, as Botan encodes BigInts somewhat differently.
|
||||||
|
printData("K", m_k);
|
||||||
|
concatenatedData += m_k;
|
||||||
|
printData("Concatenated data", concatenatedData);
|
||||||
|
|
||||||
|
m_hash.reset(get_hash(botanHMacAlgoName(hashAlgoForKexAlgo())));
|
||||||
|
const SecureVector<byte> &hashResult = m_hash->process(convertByteArray(concatenatedData),
|
||||||
|
concatenatedData.size());
|
||||||
|
m_h = convertByteArray(hashResult);
|
||||||
|
printData("H", m_h);
|
||||||
|
|
||||||
|
QScopedPointer<Public_Key> sigKey;
|
||||||
|
if (m_serverHostKeyAlgo == SshCapabilities::PubKeyDss) {
|
||||||
|
const DL_Group group(reply.hostKeyParameters.at(0), reply.hostKeyParameters.at(1),
|
||||||
|
reply.hostKeyParameters.at(2));
|
||||||
|
DSA_PublicKey * const dsaKey
|
||||||
|
= new DSA_PublicKey(group, reply.hostKeyParameters.at(3));
|
||||||
|
sigKey.reset(dsaKey);
|
||||||
|
} else if (m_serverHostKeyAlgo == SshCapabilities::PubKeyRsa) {
|
||||||
|
RSA_PublicKey * const rsaKey
|
||||||
|
= new RSA_PublicKey(reply.hostKeyParameters.at(1), reply.hostKeyParameters.at(0));
|
||||||
|
sigKey.reset(rsaKey);
|
||||||
|
} else {
|
||||||
|
QSSH_ASSERT_AND_RETURN(m_serverHostKeyAlgo.startsWith(SshCapabilities::PubKeyEcdsaPrefix));
|
||||||
|
const EC_Group domain(SshCapabilities::oid(m_serverHostKeyAlgo));
|
||||||
|
const PointGFp point = OS2ECP(convertByteArray(reply.q), reply.q.count(),
|
||||||
|
domain.get_curve());
|
||||||
|
ECDSA_PublicKey * const ecdsaKey = new ECDSA_PublicKey(domain, point);
|
||||||
|
sigKey.reset(ecdsaKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
const byte * const botanH = convertByteArray(m_h);
|
||||||
|
const Botan::byte * const botanSig = convertByteArray(reply.signatureBlob);
|
||||||
|
PK_Verifier verifier(*sigKey, botanEmsaAlgoName(m_serverHostKeyAlgo));
|
||||||
|
if (!verifier.verify_message(botanH, m_h.size(), botanSig, reply.signatureBlob.size())) {
|
||||||
|
throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
|
||||||
|
"Invalid signature in key exchange reply packet.");
|
||||||
|
}
|
||||||
|
|
||||||
|
checkHostKey(reply.k_s);
|
||||||
|
|
||||||
|
m_sendFacility.sendNewKeysPacket();
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray SshKeyExchange::hashAlgoForKexAlgo() const
|
||||||
|
{
|
||||||
|
if (m_kexAlgoName == SshCapabilities::EcdhNistp256)
|
||||||
|
return SshCapabilities::HMacSha256;
|
||||||
|
if (m_kexAlgoName == SshCapabilities::EcdhNistp384)
|
||||||
|
return SshCapabilities::HMacSha384;
|
||||||
|
if (m_kexAlgoName == SshCapabilities::EcdhNistp521)
|
||||||
|
return SshCapabilities::HMacSha512;
|
||||||
|
return SshCapabilities::HMacSha1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshKeyExchange::determineHashingAlgorithm(const SshKeyExchangeInit &kexInit,
|
||||||
|
bool serverToClient)
|
||||||
|
{
|
||||||
|
QByteArray * const algo = serverToClient ? &m_s2cHMacAlgo : &m_c2sHMacAlgo;
|
||||||
|
const QList<QByteArray> &serverCapabilities = serverToClient
|
||||||
|
? kexInit.macAlgorithmsServerToClient.names
|
||||||
|
: kexInit.macAlgorithmsClientToServer.names;
|
||||||
|
*algo = SshCapabilities::findBestMatch(SshCapabilities::MacAlgorithms, serverCapabilities);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshKeyExchange::checkHostKey(const QByteArray &hostKey)
|
||||||
|
{
|
||||||
|
if (m_connParams.hostKeyCheckingMode == SshHostKeyCheckingNone) {
|
||||||
|
if (m_connParams.hostKeyDatabase)
|
||||||
|
m_connParams.hostKeyDatabase->insertHostKey(m_connParams.host, hostKey);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_connParams.hostKeyDatabase) {
|
||||||
|
throw SshClientException(SshInternalError,
|
||||||
|
SSH_TR("Host key database must exist "
|
||||||
|
"if host key checking is enabled."));
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (m_connParams.hostKeyDatabase->matchHostKey(m_connParams.host, hostKey)) {
|
||||||
|
case SshHostKeyDatabase::KeyLookupMatch:
|
||||||
|
return; // Nothing to do.
|
||||||
|
case SshHostKeyDatabase::KeyLookupMismatch:
|
||||||
|
if (m_connParams.hostKeyCheckingMode != SshHostKeyCheckingAllowMismatch)
|
||||||
|
throwHostKeyException();
|
||||||
|
break;
|
||||||
|
case SshHostKeyDatabase::KeyLookupNoMatch:
|
||||||
|
if (m_connParams.hostKeyCheckingMode == SshHostKeyCheckingStrict)
|
||||||
|
throwHostKeyException();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
m_connParams.hostKeyDatabase->insertHostKey(m_connParams.host, hostKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshKeyExchange::throwHostKeyException()
|
||||||
|
{
|
||||||
|
throw SshServerException(SSH_DISCONNECT_HOST_KEY_NOT_VERIFIABLE, "Host key changed",
|
||||||
|
SSH_TR("Host key of machine \"%1\" has changed.")
|
||||||
|
.arg(m_connParams.host));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Internal
|
||||||
|
} // namespace QSsh
|
||||||
93
client/3rd/QtSsh/src/ssh/sshkeyexchange_p.h
Normal file
93
client/3rd/QtSsh/src/ssh/sshkeyexchange_p.h
Normal file
|
|
@ -0,0 +1,93 @@
|
||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2016 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "sshconnection.h"
|
||||||
|
|
||||||
|
#include <QByteArray>
|
||||||
|
#include <QScopedPointer>
|
||||||
|
|
||||||
|
namespace Botan {
|
||||||
|
class DH_PrivateKey;
|
||||||
|
class ECDH_PrivateKey;
|
||||||
|
class HashFunction;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace QSsh {
|
||||||
|
namespace Internal {
|
||||||
|
|
||||||
|
struct SshKeyExchangeInit;
|
||||||
|
class SshSendFacility;
|
||||||
|
class SshIncomingPacket;
|
||||||
|
|
||||||
|
class SshKeyExchange
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SshKeyExchange(const SshConnectionParameters &connParams, SshSendFacility &sendFacility);
|
||||||
|
~SshKeyExchange();
|
||||||
|
|
||||||
|
void sendKexInitPacket(const QByteArray &serverId);
|
||||||
|
|
||||||
|
// Returns true <=> the server sends a guessed package.
|
||||||
|
bool sendDhInitPacket(const SshIncomingPacket &serverKexInit);
|
||||||
|
|
||||||
|
void sendNewKeysPacket(const SshIncomingPacket &dhReply,
|
||||||
|
const QByteArray &clientId);
|
||||||
|
|
||||||
|
QByteArray k() const { return m_k; }
|
||||||
|
QByteArray h() const { return m_h; }
|
||||||
|
Botan::HashFunction *hash() const { return m_hash.data(); }
|
||||||
|
QByteArray encryptionAlgo() const { return m_encryptionAlgo; }
|
||||||
|
QByteArray decryptionAlgo() const { return m_decryptionAlgo; }
|
||||||
|
QByteArray hMacAlgoClientToServer() const { return m_c2sHMacAlgo; }
|
||||||
|
QByteArray hMacAlgoServerToClient() const { return m_s2cHMacAlgo; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
QByteArray hashAlgoForKexAlgo() const;
|
||||||
|
void determineHashingAlgorithm(const SshKeyExchangeInit &kexInit, bool serverToClient);
|
||||||
|
void checkHostKey(const QByteArray &hostKey);
|
||||||
|
Q_NORETURN void throwHostKeyException();
|
||||||
|
|
||||||
|
QByteArray m_serverId;
|
||||||
|
QByteArray m_clientKexInitPayload;
|
||||||
|
QByteArray m_serverKexInitPayload;
|
||||||
|
QScopedPointer<Botan::DH_PrivateKey> m_dhKey;
|
||||||
|
QScopedPointer<Botan::ECDH_PrivateKey> m_ecdhKey;
|
||||||
|
QByteArray m_kexAlgoName;
|
||||||
|
QByteArray m_k;
|
||||||
|
QByteArray m_h;
|
||||||
|
QByteArray m_serverHostKeyAlgo;
|
||||||
|
QByteArray m_encryptionAlgo;
|
||||||
|
QByteArray m_decryptionAlgo;
|
||||||
|
QByteArray m_c2sHMacAlgo;
|
||||||
|
QByteArray m_s2cHMacAlgo;
|
||||||
|
QScopedPointer<Botan::HashFunction> m_hash;
|
||||||
|
const SshConnectionParameters m_connParams;
|
||||||
|
SshSendFacility &m_sendFacility;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Internal
|
||||||
|
} // namespace QSsh
|
||||||
226
client/3rd/QtSsh/src/ssh/sshkeygenerator.cpp
Normal file
226
client/3rd/QtSsh/src/ssh/sshkeygenerator.cpp
Normal file
|
|
@ -0,0 +1,226 @@
|
||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2016 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#include "sshkeygenerator.h"
|
||||||
|
|
||||||
|
#include "sshbotanconversions_p.h"
|
||||||
|
#include "sshcapabilities_p.h"
|
||||||
|
#include "ssh_global.h"
|
||||||
|
#include "sshinit_p.h"
|
||||||
|
#include "sshpacket_p.h"
|
||||||
|
|
||||||
|
#include <botan/botan.h>
|
||||||
|
|
||||||
|
#include <QDateTime>
|
||||||
|
#include <QInputDialog>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace QSsh {
|
||||||
|
|
||||||
|
using namespace Botan;
|
||||||
|
using namespace Internal;
|
||||||
|
|
||||||
|
SshKeyGenerator::SshKeyGenerator() : m_type(Rsa)
|
||||||
|
{
|
||||||
|
initSsh();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SshKeyGenerator::generateKeys(KeyType type, PrivateKeyFormat format, int keySize,
|
||||||
|
EncryptionMode encryptionMode)
|
||||||
|
{
|
||||||
|
m_type = type;
|
||||||
|
m_encryptionMode = encryptionMode;
|
||||||
|
|
||||||
|
try {
|
||||||
|
AutoSeeded_RNG rng;
|
||||||
|
KeyPtr key;
|
||||||
|
switch (m_type) {
|
||||||
|
case Rsa:
|
||||||
|
key = KeyPtr(new RSA_PrivateKey(rng, keySize));
|
||||||
|
break;
|
||||||
|
case Dsa:
|
||||||
|
key = KeyPtr(new DSA_PrivateKey(rng, DL_Group(rng, DL_Group::DSA_Kosherizer, keySize)));
|
||||||
|
break;
|
||||||
|
case Ecdsa: {
|
||||||
|
const QByteArray algo = SshCapabilities::ecdsaPubKeyAlgoForKeyWidth(keySize / 8);
|
||||||
|
key = KeyPtr(new ECDSA_PrivateKey(rng, EC_Group(SshCapabilities::oid(algo))));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
switch (format) {
|
||||||
|
case Pkcs8:
|
||||||
|
generatePkcs8KeyStrings(key, rng);
|
||||||
|
break;
|
||||||
|
case OpenSsl:
|
||||||
|
generateOpenSslKeyStrings(key);
|
||||||
|
break;
|
||||||
|
case Mixed:
|
||||||
|
default:
|
||||||
|
generatePkcs8KeyString(key, true, rng);
|
||||||
|
generateOpenSslPublicKeyString(key);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} catch (const std::exception &e) {
|
||||||
|
m_error = tr("Error generating key: %1").arg(QString::fromLatin1(e.what()));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshKeyGenerator::generatePkcs8KeyStrings(const KeyPtr &key, RandomNumberGenerator &rng)
|
||||||
|
{
|
||||||
|
generatePkcs8KeyString(key, false, rng);
|
||||||
|
generatePkcs8KeyString(key, true, rng);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshKeyGenerator::generatePkcs8KeyString(const KeyPtr &key, bool privateKey,
|
||||||
|
RandomNumberGenerator &rng)
|
||||||
|
{
|
||||||
|
Pipe pipe;
|
||||||
|
pipe.start_msg();
|
||||||
|
QByteArray *keyData;
|
||||||
|
if (privateKey) {
|
||||||
|
QString password;
|
||||||
|
if (m_encryptionMode == DoOfferEncryption)
|
||||||
|
password = getPassword();
|
||||||
|
if (!password.isEmpty())
|
||||||
|
pipe.write(PKCS8::PEM_encode(*key, rng, password.toLocal8Bit().data()));
|
||||||
|
else
|
||||||
|
pipe.write(PKCS8::PEM_encode(*key));
|
||||||
|
keyData = &m_privateKey;
|
||||||
|
} else {
|
||||||
|
pipe.write(X509::PEM_encode(*key));
|
||||||
|
keyData = &m_publicKey;
|
||||||
|
}
|
||||||
|
pipe.end_msg();
|
||||||
|
keyData->resize(static_cast<int>(pipe.remaining(pipe.message_count() - 1)));
|
||||||
|
pipe.read(convertByteArray(*keyData), keyData->size(),
|
||||||
|
pipe.message_count() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshKeyGenerator::generateOpenSslKeyStrings(const KeyPtr &key)
|
||||||
|
{
|
||||||
|
generateOpenSslPublicKeyString(key);
|
||||||
|
generateOpenSslPrivateKeyString(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshKeyGenerator::generateOpenSslPublicKeyString(const KeyPtr &key)
|
||||||
|
{
|
||||||
|
QList<BigInt> params;
|
||||||
|
QByteArray keyId;
|
||||||
|
QByteArray q;
|
||||||
|
switch (m_type) {
|
||||||
|
case Rsa: {
|
||||||
|
const QSharedPointer<RSA_PrivateKey> rsaKey = key.dynamicCast<RSA_PrivateKey>();
|
||||||
|
params << rsaKey->get_e() << rsaKey->get_n();
|
||||||
|
keyId = SshCapabilities::PubKeyRsa;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Dsa: {
|
||||||
|
const QSharedPointer<DSA_PrivateKey> dsaKey = key.dynamicCast<DSA_PrivateKey>();
|
||||||
|
params << dsaKey->group_p() << dsaKey->group_q() << dsaKey->group_g() << dsaKey->get_y();
|
||||||
|
keyId = SshCapabilities::PubKeyDss;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Ecdsa: {
|
||||||
|
const auto ecdsaKey = key.dynamicCast<ECDSA_PrivateKey>();
|
||||||
|
q = convertByteArray(EC2OSP(ecdsaKey->public_point(), PointGFp::UNCOMPRESSED));
|
||||||
|
keyId = SshCapabilities::ecdsaPubKeyAlgoForKeyWidth(
|
||||||
|
static_cast<int>(ecdsaKey->private_value().bytes()));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray publicKeyBlob = AbstractSshPacket::encodeString(keyId);
|
||||||
|
foreach (const BigInt &b, params)
|
||||||
|
publicKeyBlob += AbstractSshPacket::encodeMpInt(b);
|
||||||
|
if (!q.isEmpty()) {
|
||||||
|
publicKeyBlob += AbstractSshPacket::encodeString(keyId.mid(11)); // Without "ecdsa-sha2-" prefix.
|
||||||
|
publicKeyBlob += AbstractSshPacket::encodeString(q);
|
||||||
|
}
|
||||||
|
publicKeyBlob = publicKeyBlob.toBase64();
|
||||||
|
const QByteArray id = "QtCreator/"
|
||||||
|
+ QDateTime::currentDateTime().toString(Qt::ISODate).toUtf8();
|
||||||
|
m_publicKey = keyId + ' ' + publicKeyBlob + ' ' + id;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshKeyGenerator::generateOpenSslPrivateKeyString(const KeyPtr &key)
|
||||||
|
{
|
||||||
|
QList<BigInt> params;
|
||||||
|
const char *label = "";
|
||||||
|
switch (m_type) {
|
||||||
|
case Rsa: {
|
||||||
|
const QSharedPointer<RSA_PrivateKey> rsaKey
|
||||||
|
= key.dynamicCast<RSA_PrivateKey>();
|
||||||
|
params << rsaKey->get_n() << rsaKey->get_e() << rsaKey->get_d() << rsaKey->get_p()
|
||||||
|
<< rsaKey->get_q();
|
||||||
|
const BigInt dmp1 = rsaKey->get_d() % (rsaKey->get_p() - 1);
|
||||||
|
const BigInt dmq1 = rsaKey->get_d() % (rsaKey->get_q() - 1);
|
||||||
|
const BigInt iqmp = inverse_mod(rsaKey->get_q(), rsaKey->get_p());
|
||||||
|
params << dmp1 << dmq1 << iqmp;
|
||||||
|
label = "RSA PRIVATE KEY";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Dsa: {
|
||||||
|
const QSharedPointer<DSA_PrivateKey> dsaKey = key.dynamicCast<DSA_PrivateKey>();
|
||||||
|
params << dsaKey->group_p() << dsaKey->group_q() << dsaKey->group_g() << dsaKey->get_y()
|
||||||
|
<< dsaKey->get_x();
|
||||||
|
label = "DSA PRIVATE KEY";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Ecdsa:
|
||||||
|
params << key.dynamicCast<ECDSA_PrivateKey>()->private_value();
|
||||||
|
label = "EC PRIVATE KEY";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
DER_Encoder encoder;
|
||||||
|
encoder.start_cons(SEQUENCE).encode(size_t(0));
|
||||||
|
foreach (const BigInt &b, params)
|
||||||
|
encoder.encode(b);
|
||||||
|
encoder.end_cons();
|
||||||
|
m_privateKey = QByteArray(PEM_Code::encode (encoder.get_contents(), label).c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
QString SshKeyGenerator::getPassword() const
|
||||||
|
{
|
||||||
|
QInputDialog d;
|
||||||
|
d.setInputMode(QInputDialog::TextInput);
|
||||||
|
d.setTextEchoMode(QLineEdit::Password);
|
||||||
|
d.setWindowTitle(tr("Password for Private Key"));
|
||||||
|
d.setLabelText(tr("It is recommended that you secure your private key\n"
|
||||||
|
"with a password, which you can enter below."));
|
||||||
|
d.setOkButtonText(tr("Encrypt Key File"));
|
||||||
|
d.setCancelButtonText(tr("Do Not Encrypt Key File"));
|
||||||
|
int result = QDialog::Accepted;
|
||||||
|
QString password;
|
||||||
|
while (result == QDialog::Accepted && password.isEmpty()) {
|
||||||
|
result = d.exec();
|
||||||
|
password = d.textValue();
|
||||||
|
}
|
||||||
|
return result == QDialog::Accepted ? password : QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace QSsh
|
||||||
75
client/3rd/QtSsh/src/ssh/sshkeygenerator.h
Normal file
75
client/3rd/QtSsh/src/ssh/sshkeygenerator.h
Normal file
|
|
@ -0,0 +1,75 @@
|
||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2016 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "ssh_global.h"
|
||||||
|
|
||||||
|
#include <QCoreApplication>
|
||||||
|
#include <QSharedPointer>
|
||||||
|
|
||||||
|
namespace Botan {
|
||||||
|
class Private_Key;
|
||||||
|
class RandomNumberGenerator;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace QSsh {
|
||||||
|
|
||||||
|
class QSSH_EXPORT SshKeyGenerator
|
||||||
|
{
|
||||||
|
Q_DECLARE_TR_FUNCTIONS(SshKeyGenerator)
|
||||||
|
public:
|
||||||
|
enum KeyType { Rsa, Dsa, Ecdsa };
|
||||||
|
enum PrivateKeyFormat { Pkcs8, OpenSsl, Mixed };
|
||||||
|
enum EncryptionMode { DoOfferEncryption, DoNotOfferEncryption }; // Only relevant for Pkcs8 format.
|
||||||
|
|
||||||
|
SshKeyGenerator();
|
||||||
|
bool generateKeys(KeyType type, PrivateKeyFormat format, int keySize,
|
||||||
|
EncryptionMode encryptionMode = DoOfferEncryption);
|
||||||
|
|
||||||
|
QString error() const { return m_error; }
|
||||||
|
QByteArray privateKey() const { return m_privateKey; }
|
||||||
|
QByteArray publicKey() const { return m_publicKey; }
|
||||||
|
KeyType type() const { return m_type; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
typedef QSharedPointer<Botan::Private_Key> KeyPtr;
|
||||||
|
|
||||||
|
void generatePkcs8KeyStrings(const KeyPtr &key, Botan::RandomNumberGenerator &rng);
|
||||||
|
void generatePkcs8KeyString(const KeyPtr &key, bool privateKey,
|
||||||
|
Botan::RandomNumberGenerator &rng);
|
||||||
|
void generateOpenSslKeyStrings(const KeyPtr &key);
|
||||||
|
void generateOpenSslPrivateKeyString(const KeyPtr &key);
|
||||||
|
void generateOpenSslPublicKeyString(const KeyPtr &key);
|
||||||
|
QString getPassword() const;
|
||||||
|
|
||||||
|
QString m_error;
|
||||||
|
QByteArray m_publicKey;
|
||||||
|
QByteArray m_privateKey;
|
||||||
|
KeyType m_type;
|
||||||
|
EncryptionMode m_encryptionMode;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace QSsh
|
||||||
59
client/3rd/QtSsh/src/ssh/sshkeypasswordretriever.cpp
Normal file
59
client/3rd/QtSsh/src/ssh/sshkeypasswordretriever.cpp
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2016 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#include "sshkeypasswordretriever_p.h"
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QInputDialog>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
namespace QSsh {
|
||||||
|
namespace Internal {
|
||||||
|
|
||||||
|
std::string SshKeyPasswordRetriever::get_passphrase(const std::string &, const std::string &,
|
||||||
|
UI_Result &result) const
|
||||||
|
{
|
||||||
|
const bool hasGui = dynamic_cast<QApplication *>(QApplication::instance());
|
||||||
|
if (hasGui) {
|
||||||
|
bool ok;
|
||||||
|
const QString &password = QInputDialog::getText(0,
|
||||||
|
QCoreApplication::translate("QSsh::Ssh", "Password Required"),
|
||||||
|
QCoreApplication::translate("QSsh::Ssh", "Please enter the password for your private key."),
|
||||||
|
QLineEdit::Password, QString(), &ok);
|
||||||
|
result = ok ? OK : CANCEL_ACTION;
|
||||||
|
return std::string(password.toLocal8Bit().data());
|
||||||
|
} else {
|
||||||
|
result = OK;
|
||||||
|
std::string password;
|
||||||
|
std::cout << "Please enter the password for your private key (set echo off beforehand!): " << std::flush;
|
||||||
|
std::cin >> password;
|
||||||
|
return password;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Internal
|
||||||
|
} // namespace QSsh
|
||||||
43
client/3rd/QtSsh/src/ssh/sshkeypasswordretriever_p.h
Normal file
43
client/3rd/QtSsh/src/ssh/sshkeypasswordretriever_p.h
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2016 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <botan/botan.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace QSsh {
|
||||||
|
namespace Internal {
|
||||||
|
|
||||||
|
class SshKeyPasswordRetriever : public Botan::User_Interface
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
std::string get_passphrase(const std::string &what, const std::string &source,
|
||||||
|
UI_Result &result) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Internal
|
||||||
|
} // namespace QSsh
|
||||||
32
client/3rd/QtSsh/src/ssh/sshlogging.cpp
Normal file
32
client/3rd/QtSsh/src/ssh/sshlogging.cpp
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2016 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#include "sshlogging_p.h"
|
||||||
|
|
||||||
|
namespace QSsh {
|
||||||
|
namespace Internal {
|
||||||
|
Q_LOGGING_CATEGORY(sshLog, "qtc.ssh")
|
||||||
|
} // namespace Internal
|
||||||
|
} // namespace QSsh
|
||||||
34
client/3rd/QtSsh/src/ssh/sshlogging_p.h
Normal file
34
client/3rd/QtSsh/src/ssh/sshlogging_p.h
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2016 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QLoggingCategory>
|
||||||
|
|
||||||
|
namespace QSsh {
|
||||||
|
namespace Internal {
|
||||||
|
Q_DECLARE_LOGGING_CATEGORY(sshLog)
|
||||||
|
} // namespace Internal
|
||||||
|
} // namespace QSsh
|
||||||
381
client/3rd/QtSsh/src/ssh/sshoutgoingpacket.cpp
Normal file
381
client/3rd/QtSsh/src/ssh/sshoutgoingpacket.cpp
Normal file
|
|
@ -0,0 +1,381 @@
|
||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2016 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#include "sshoutgoingpacket_p.h"
|
||||||
|
|
||||||
|
#include "sshcapabilities_p.h"
|
||||||
|
#include "sshcryptofacility_p.h"
|
||||||
|
#include "sshlogging_p.h"
|
||||||
|
|
||||||
|
#include <QtEndian>
|
||||||
|
|
||||||
|
namespace QSsh {
|
||||||
|
namespace Internal {
|
||||||
|
|
||||||
|
SshOutgoingPacket::SshOutgoingPacket(const SshEncryptionFacility &encrypter,
|
||||||
|
const quint32 &seqNr) : m_encrypter(encrypter), m_seqNr(seqNr)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
quint32 SshOutgoingPacket::cipherBlockSize() const
|
||||||
|
{
|
||||||
|
return qMax(m_encrypter.cipherBlockSize(), 4U);
|
||||||
|
}
|
||||||
|
|
||||||
|
quint32 SshOutgoingPacket::macLength() const
|
||||||
|
{
|
||||||
|
return m_encrypter.macLength();
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray SshOutgoingPacket::generateKeyExchangeInitPacket()
|
||||||
|
{
|
||||||
|
const QByteArray &supportedkeyExchangeMethods
|
||||||
|
= encodeNameList(SshCapabilities::KeyExchangeMethods);
|
||||||
|
const QByteArray &supportedPublicKeyAlgorithms
|
||||||
|
= encodeNameList(SshCapabilities::PublicKeyAlgorithms);
|
||||||
|
const QByteArray &supportedEncryptionAlgorithms
|
||||||
|
= encodeNameList(SshCapabilities::EncryptionAlgorithms);
|
||||||
|
const QByteArray &supportedMacAlgorithms
|
||||||
|
= encodeNameList(SshCapabilities::MacAlgorithms);
|
||||||
|
const QByteArray &supportedCompressionAlgorithms
|
||||||
|
= encodeNameList(SshCapabilities::CompressionAlgorithms);
|
||||||
|
const QByteArray &supportedLanguages = encodeNameList(QList<QByteArray>());
|
||||||
|
|
||||||
|
init(SSH_MSG_KEXINIT);
|
||||||
|
m_data += m_encrypter.getRandomNumbers(16);
|
||||||
|
m_data.append(supportedkeyExchangeMethods);
|
||||||
|
m_data.append(supportedPublicKeyAlgorithms);
|
||||||
|
m_data.append(supportedEncryptionAlgorithms)
|
||||||
|
.append(supportedEncryptionAlgorithms);
|
||||||
|
m_data.append(supportedMacAlgorithms).append(supportedMacAlgorithms);
|
||||||
|
m_data.append(supportedCompressionAlgorithms)
|
||||||
|
.append(supportedCompressionAlgorithms);
|
||||||
|
m_data.append(supportedLanguages).append(supportedLanguages);
|
||||||
|
appendBool(false); // No guessed packet.
|
||||||
|
m_data.append(QByteArray(4, 0)); // Reserved.
|
||||||
|
QByteArray payload = m_data.mid(PayloadOffset);
|
||||||
|
finalize();
|
||||||
|
return payload;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshOutgoingPacket::generateKeyDhInitPacket(const Botan::BigInt &e)
|
||||||
|
{
|
||||||
|
init(SSH_MSG_KEXDH_INIT).appendMpInt(e).finalize();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshOutgoingPacket::generateKeyEcdhInitPacket(const QByteArray &clientQ)
|
||||||
|
{
|
||||||
|
init(SSH_MSG_KEX_ECDH_INIT).appendString(clientQ).finalize();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshOutgoingPacket::generateNewKeysPacket()
|
||||||
|
{
|
||||||
|
init(SSH_MSG_NEWKEYS).finalize();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshOutgoingPacket::generateUserAuthServiceRequestPacket()
|
||||||
|
{
|
||||||
|
generateServiceRequest("ssh-userauth");
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshOutgoingPacket::generateServiceRequest(const QByteArray &service)
|
||||||
|
{
|
||||||
|
init(SSH_MSG_SERVICE_REQUEST).appendString(service).finalize();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshOutgoingPacket::generateUserAuthByPasswordRequestPacket(const QByteArray &user,
|
||||||
|
const QByteArray &service, const QByteArray &pwd)
|
||||||
|
{
|
||||||
|
init(SSH_MSG_USERAUTH_REQUEST).appendString(user).appendString(service);
|
||||||
|
if (pwd.isEmpty())
|
||||||
|
appendString("none"); // RFC 4252, 5.2
|
||||||
|
else
|
||||||
|
appendString("password").appendBool(false).appendString(pwd);
|
||||||
|
finalize();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshOutgoingPacket::generateUserAuthByPublicKeyRequestPacket(const QByteArray &user,
|
||||||
|
const QByteArray &service)
|
||||||
|
{
|
||||||
|
init(SSH_MSG_USERAUTH_REQUEST).appendString(user).appendString(service)
|
||||||
|
.appendString("publickey").appendBool(true)
|
||||||
|
.appendString(m_encrypter.authenticationAlgorithmName())
|
||||||
|
.appendString(m_encrypter.authenticationPublicKey());
|
||||||
|
const QByteArray &dataToSign = m_data.mid(PayloadOffset);
|
||||||
|
appendString(m_encrypter.authenticationKeySignature(dataToSign));
|
||||||
|
finalize();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshOutgoingPacket::generateUserAuthByKeyboardInteractiveRequestPacket(const QByteArray &user,
|
||||||
|
const QByteArray &service)
|
||||||
|
{
|
||||||
|
// RFC 4256, 3.1
|
||||||
|
init(SSH_MSG_USERAUTH_REQUEST).appendString(user).appendString(service)
|
||||||
|
.appendString("keyboard-interactive")
|
||||||
|
.appendString(QByteArray()) // Language tag. Deprecated and should be empty
|
||||||
|
.appendString(QByteArray()) // Submethods.
|
||||||
|
.finalize();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshOutgoingPacket::generateUserAuthInfoResponsePacket(const QStringList &responses)
|
||||||
|
{
|
||||||
|
// RFC 4256, 3.4
|
||||||
|
init(SSH_MSG_USERAUTH_INFO_RESPONSE).appendInt(responses.count());
|
||||||
|
foreach (const QString &response, responses)
|
||||||
|
appendString(response.toUtf8());
|
||||||
|
finalize();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshOutgoingPacket::generateRequestFailurePacket()
|
||||||
|
{
|
||||||
|
init(SSH_MSG_REQUEST_FAILURE).finalize();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshOutgoingPacket::generateIgnorePacket()
|
||||||
|
{
|
||||||
|
init(SSH_MSG_IGNORE).finalize();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshOutgoingPacket::generateInvalidMessagePacket()
|
||||||
|
{
|
||||||
|
init(SSH_MSG_INVALID).finalize();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshOutgoingPacket::generateSessionPacket(quint32 channelId,
|
||||||
|
quint32 windowSize, quint32 maxPacketSize)
|
||||||
|
{
|
||||||
|
init(SSH_MSG_CHANNEL_OPEN).appendString("session").appendInt(channelId)
|
||||||
|
.appendInt(windowSize).appendInt(maxPacketSize).finalize();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshOutgoingPacket::generateDirectTcpIpPacket(quint32 channelId, quint32 windowSize,
|
||||||
|
quint32 maxPacketSize, const QByteArray &remoteHost, quint32 remotePort,
|
||||||
|
const QByteArray &localIpAddress, quint32 localPort)
|
||||||
|
{
|
||||||
|
init(SSH_MSG_CHANNEL_OPEN).appendString("direct-tcpip").appendInt(channelId)
|
||||||
|
.appendInt(windowSize).appendInt(maxPacketSize).appendString(remoteHost)
|
||||||
|
.appendInt(remotePort).appendString(localIpAddress).appendInt(localPort).finalize();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshOutgoingPacket::generateTcpIpForwardPacket(const QByteArray &bindAddress, quint32 bindPort)
|
||||||
|
{
|
||||||
|
init(SSH_MSG_GLOBAL_REQUEST).appendString("tcpip-forward").appendBool(true)
|
||||||
|
.appendString(bindAddress).appendInt(bindPort).finalize();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshOutgoingPacket::generateCancelTcpIpForwardPacket(const QByteArray &bindAddress,
|
||||||
|
quint32 bindPort)
|
||||||
|
{
|
||||||
|
init(SSH_MSG_GLOBAL_REQUEST).appendString("cancel-tcpip-forward").appendBool(true)
|
||||||
|
.appendString(bindAddress).appendInt(bindPort).finalize();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshOutgoingPacket::generateEnvPacket(quint32 remoteChannel,
|
||||||
|
const QByteArray &var, const QByteArray &value)
|
||||||
|
{
|
||||||
|
init(SSH_MSG_CHANNEL_REQUEST).appendInt(remoteChannel).appendString("env")
|
||||||
|
.appendBool(false).appendString(var).appendString(value).finalize();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshOutgoingPacket::generatePtyRequestPacket(quint32 remoteChannel,
|
||||||
|
const SshPseudoTerminal &terminal)
|
||||||
|
{
|
||||||
|
init(SSH_MSG_CHANNEL_REQUEST).appendInt(remoteChannel)
|
||||||
|
.appendString("pty-req").appendBool(false)
|
||||||
|
.appendString(terminal.termType).appendInt(terminal.columnCount)
|
||||||
|
.appendInt(terminal.rowCount).appendInt(0).appendInt(0);
|
||||||
|
QByteArray modeString;
|
||||||
|
for (SshPseudoTerminal::ModeMap::ConstIterator it = terminal.modes.constBegin();
|
||||||
|
it != terminal.modes.constEnd(); ++it) {
|
||||||
|
modeString += char(it.key());
|
||||||
|
modeString += encodeInt(it.value());
|
||||||
|
}
|
||||||
|
modeString += char(0); // TTY_OP_END
|
||||||
|
appendString(modeString).finalize();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshOutgoingPacket::generateExecPacket(quint32 remoteChannel,
|
||||||
|
const QByteArray &command)
|
||||||
|
{
|
||||||
|
init(SSH_MSG_CHANNEL_REQUEST).appendInt(remoteChannel).appendString("exec")
|
||||||
|
.appendBool(true).appendString(command).finalize();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshOutgoingPacket::generateShellPacket(quint32 remoteChannel)
|
||||||
|
{
|
||||||
|
init(SSH_MSG_CHANNEL_REQUEST).appendInt(remoteChannel).appendString("shell")
|
||||||
|
.appendBool(true).finalize();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshOutgoingPacket::generateSftpPacket(quint32 remoteChannel)
|
||||||
|
{
|
||||||
|
init(SSH_MSG_CHANNEL_REQUEST).appendInt(remoteChannel)
|
||||||
|
.appendString("subsystem").appendBool(true).appendString("sftp")
|
||||||
|
.finalize();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshOutgoingPacket::generateWindowAdjustPacket(quint32 remoteChannel,
|
||||||
|
quint32 bytesToAdd)
|
||||||
|
{
|
||||||
|
init(SSH_MSG_CHANNEL_WINDOW_ADJUST).appendInt(remoteChannel)
|
||||||
|
.appendInt(bytesToAdd).finalize();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshOutgoingPacket::generateChannelDataPacket(quint32 remoteChannel,
|
||||||
|
const QByteArray &data)
|
||||||
|
{
|
||||||
|
init(SSH_MSG_CHANNEL_DATA).appendInt(remoteChannel).appendString(data)
|
||||||
|
.finalize();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshOutgoingPacket::generateChannelSignalPacket(quint32 remoteChannel,
|
||||||
|
const QByteArray &signalName)
|
||||||
|
{
|
||||||
|
init(SSH_MSG_CHANNEL_REQUEST).appendInt(remoteChannel)
|
||||||
|
.appendString("signal").appendBool(false).appendString(signalName)
|
||||||
|
.finalize();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshOutgoingPacket::generateChannelEofPacket(quint32 remoteChannel)
|
||||||
|
{
|
||||||
|
init(SSH_MSG_CHANNEL_EOF).appendInt(remoteChannel).finalize();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshOutgoingPacket::generateChannelClosePacket(quint32 remoteChannel)
|
||||||
|
{
|
||||||
|
init(SSH_MSG_CHANNEL_CLOSE).appendInt(remoteChannel).finalize();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshOutgoingPacket::generateChannelOpenConfirmationPacket(quint32 remoteChannel,
|
||||||
|
quint32 localChannel,
|
||||||
|
quint32 localWindowSize,
|
||||||
|
quint32 maxPacketSize)
|
||||||
|
{
|
||||||
|
init(SSH_MSG_CHANNEL_OPEN_CONFIRMATION).appendInt(remoteChannel).appendInt(localChannel)
|
||||||
|
.appendInt(localWindowSize).appendInt(maxPacketSize).finalize();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshOutgoingPacket::generateChannelOpenFailurePacket(quint32 remoteChannel, quint32 reason,
|
||||||
|
const QByteArray &reasonString)
|
||||||
|
{
|
||||||
|
init(SSH_MSG_CHANNEL_OPEN_FAILURE).appendInt(remoteChannel).appendInt(reason)
|
||||||
|
.appendString(reasonString).appendString(QByteArray()).finalize();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshOutgoingPacket::generateDisconnectPacket(SshErrorCode reason,
|
||||||
|
const QByteArray &reasonString)
|
||||||
|
{
|
||||||
|
init(SSH_MSG_DISCONNECT).appendInt(reason).appendString(reasonString)
|
||||||
|
.appendString(QByteArray()).finalize();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshOutgoingPacket::generateMsgUnimplementedPacket(quint32 serverSeqNr)
|
||||||
|
{
|
||||||
|
init(SSH_MSG_UNIMPLEMENTED).appendInt(serverSeqNr).finalize();
|
||||||
|
}
|
||||||
|
|
||||||
|
SshOutgoingPacket &SshOutgoingPacket::appendInt(quint32 val)
|
||||||
|
{
|
||||||
|
m_data.append(encodeInt(val));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
SshOutgoingPacket &SshOutgoingPacket::appendMpInt(const Botan::BigInt &number)
|
||||||
|
{
|
||||||
|
m_data.append(encodeMpInt(number));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
SshOutgoingPacket &SshOutgoingPacket::appendBool(bool b)
|
||||||
|
{
|
||||||
|
m_data += static_cast<char>(b);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
SshOutgoingPacket &SshOutgoingPacket::appendString(const QByteArray &string)
|
||||||
|
{
|
||||||
|
m_data.append(encodeString(string));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
SshOutgoingPacket &SshOutgoingPacket::init(SshPacketType type)
|
||||||
|
{
|
||||||
|
m_data.resize(TypeOffset + 1);
|
||||||
|
m_data[TypeOffset] = type;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
SshOutgoingPacket &SshOutgoingPacket::setPadding()
|
||||||
|
{
|
||||||
|
m_data += m_encrypter.getRandomNumbers(MinPaddingLength);
|
||||||
|
int padLength = MinPaddingLength;
|
||||||
|
const int divisor = sizeDivisor();
|
||||||
|
const int mod = m_data.size() % divisor;
|
||||||
|
padLength += divisor - mod;
|
||||||
|
m_data += m_encrypter.getRandomNumbers(padLength - MinPaddingLength);
|
||||||
|
m_data[PaddingLengthOffset] = padLength;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
SshOutgoingPacket &SshOutgoingPacket::encrypt()
|
||||||
|
{
|
||||||
|
const QByteArray &mac
|
||||||
|
= generateMac(m_encrypter, m_seqNr);
|
||||||
|
m_encrypter.encrypt(m_data);
|
||||||
|
m_data += mac;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshOutgoingPacket::finalize()
|
||||||
|
{
|
||||||
|
setPadding();
|
||||||
|
setLengthField(m_data);
|
||||||
|
m_length = m_data.size() - 4;
|
||||||
|
qCDebug(sshLog, "Encrypting packet of type %u", m_data.at(TypeOffset));
|
||||||
|
encrypt();
|
||||||
|
qCDebug(sshLog, "Sending packet of size %d", rawData().count());
|
||||||
|
Q_ASSERT(isComplete());
|
||||||
|
}
|
||||||
|
|
||||||
|
int SshOutgoingPacket::sizeDivisor() const
|
||||||
|
{
|
||||||
|
return qMax(cipherBlockSize(), 8U);
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray SshOutgoingPacket::encodeNameList(const QList<QByteArray> &list)
|
||||||
|
{
|
||||||
|
QByteArray data;
|
||||||
|
data.resize(4);
|
||||||
|
for (int i = 0; i < list.count(); ++i) {
|
||||||
|
if (i > 0)
|
||||||
|
data.append(',');
|
||||||
|
data.append(list.at(i));
|
||||||
|
}
|
||||||
|
AbstractSshPacket::setLengthField(data);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Internal
|
||||||
|
} // namespace QSsh
|
||||||
113
client/3rd/QtSsh/src/ssh/sshoutgoingpacket_p.h
Normal file
113
client/3rd/QtSsh/src/ssh/sshoutgoingpacket_p.h
Normal file
|
|
@ -0,0 +1,113 @@
|
||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2016 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "sshpacket_p.h"
|
||||||
|
|
||||||
|
#include "sshpseudoterminal.h"
|
||||||
|
|
||||||
|
#include <QStringList>
|
||||||
|
|
||||||
|
namespace QSsh {
|
||||||
|
namespace Internal {
|
||||||
|
|
||||||
|
class SshEncryptionFacility;
|
||||||
|
|
||||||
|
class SshOutgoingPacket : public AbstractSshPacket
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SshOutgoingPacket(const SshEncryptionFacility &encrypter,
|
||||||
|
const quint32 &seqNr);
|
||||||
|
|
||||||
|
QByteArray generateKeyExchangeInitPacket(); // Returns payload.
|
||||||
|
void generateKeyDhInitPacket(const Botan::BigInt &e);
|
||||||
|
void generateKeyEcdhInitPacket(const QByteArray &clientQ);
|
||||||
|
void generateNewKeysPacket();
|
||||||
|
void generateDisconnectPacket(SshErrorCode reason,
|
||||||
|
const QByteArray &reasonString);
|
||||||
|
void generateMsgUnimplementedPacket(quint32 serverSeqNr);
|
||||||
|
void generateUserAuthServiceRequestPacket();
|
||||||
|
void generateUserAuthByPasswordRequestPacket(const QByteArray &user,
|
||||||
|
const QByteArray &service, const QByteArray &pwd);
|
||||||
|
void generateUserAuthByPublicKeyRequestPacket(const QByteArray &user,
|
||||||
|
const QByteArray &service);
|
||||||
|
void generateUserAuthByKeyboardInteractiveRequestPacket(const QByteArray &user,
|
||||||
|
const QByteArray &service);
|
||||||
|
void generateUserAuthInfoResponsePacket(const QStringList &responses);
|
||||||
|
void generateRequestFailurePacket();
|
||||||
|
void generateIgnorePacket();
|
||||||
|
void generateInvalidMessagePacket();
|
||||||
|
void generateSessionPacket(quint32 channelId, quint32 windowSize,
|
||||||
|
quint32 maxPacketSize);
|
||||||
|
void generateDirectTcpIpPacket(quint32 channelId, quint32 windowSize,
|
||||||
|
quint32 maxPacketSize, const QByteArray &remoteHost, quint32 remotePort,
|
||||||
|
const QByteArray &localIpAddress, quint32 localPort);
|
||||||
|
void generateTcpIpForwardPacket(const QByteArray &bindAddress, quint32 bindPort);
|
||||||
|
void generateCancelTcpIpForwardPacket(const QByteArray &bindAddress, quint32 bindPort);
|
||||||
|
void generateEnvPacket(quint32 remoteChannel, const QByteArray &var,
|
||||||
|
const QByteArray &value);
|
||||||
|
void generatePtyRequestPacket(quint32 remoteChannel,
|
||||||
|
const SshPseudoTerminal &terminal);
|
||||||
|
void generateExecPacket(quint32 remoteChannel, const QByteArray &command);
|
||||||
|
void generateShellPacket(quint32 remoteChannel);
|
||||||
|
void generateSftpPacket(quint32 remoteChannel);
|
||||||
|
void generateWindowAdjustPacket(quint32 remoteChannel, quint32 bytesToAdd);
|
||||||
|
void generateChannelDataPacket(quint32 remoteChannel,
|
||||||
|
const QByteArray &data);
|
||||||
|
void generateChannelSignalPacket(quint32 remoteChannel,
|
||||||
|
const QByteArray &signalName);
|
||||||
|
void generateChannelEofPacket(quint32 remoteChannel);
|
||||||
|
void generateChannelClosePacket(quint32 remoteChannel);
|
||||||
|
void generateChannelOpenConfirmationPacket(quint32 remoteChannel, quint32 localChannel,
|
||||||
|
quint32 localWindowSize, quint32 maxPackeSize);
|
||||||
|
void generateChannelOpenFailurePacket(quint32 remoteChannel, quint32 reason,
|
||||||
|
const QByteArray &reasonString);
|
||||||
|
|
||||||
|
private:
|
||||||
|
virtual quint32 cipherBlockSize() const;
|
||||||
|
virtual quint32 macLength() const;
|
||||||
|
|
||||||
|
static QByteArray encodeNameList(const QList<QByteArray> &list);
|
||||||
|
|
||||||
|
void generateServiceRequest(const QByteArray &service);
|
||||||
|
|
||||||
|
SshOutgoingPacket &init(SshPacketType type);
|
||||||
|
SshOutgoingPacket &setPadding();
|
||||||
|
SshOutgoingPacket &encrypt();
|
||||||
|
void finalize();
|
||||||
|
|
||||||
|
SshOutgoingPacket &appendInt(quint32 val);
|
||||||
|
SshOutgoingPacket &appendString(const QByteArray &string);
|
||||||
|
SshOutgoingPacket &appendMpInt(const Botan::BigInt &number);
|
||||||
|
SshOutgoingPacket &appendBool(bool b);
|
||||||
|
int sizeDivisor() const;
|
||||||
|
|
||||||
|
const SshEncryptionFacility &m_encrypter;
|
||||||
|
const quint32 &m_seqNr;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Internal
|
||||||
|
} // namespace QSsh
|
||||||
153
client/3rd/QtSsh/src/ssh/sshpacket.cpp
Normal file
153
client/3rd/QtSsh/src/ssh/sshpacket.cpp
Normal file
|
|
@ -0,0 +1,153 @@
|
||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2016 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#include "sshpacket_p.h"
|
||||||
|
|
||||||
|
#include "sshcapabilities_p.h"
|
||||||
|
#include "sshcryptofacility_p.h"
|
||||||
|
#include "sshexception_p.h"
|
||||||
|
#include "sshlogging_p.h"
|
||||||
|
#include "sshpacketparser_p.h"
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
#include <cctype>
|
||||||
|
|
||||||
|
namespace QSsh {
|
||||||
|
namespace Internal {
|
||||||
|
|
||||||
|
const quint32 AbstractSshPacket::PaddingLengthOffset = 4;
|
||||||
|
const quint32 AbstractSshPacket::PayloadOffset = PaddingLengthOffset + 1;
|
||||||
|
const quint32 AbstractSshPacket::TypeOffset = PayloadOffset;
|
||||||
|
const quint32 AbstractSshPacket::MinPaddingLength = 4;
|
||||||
|
|
||||||
|
static void printByteArray(const QByteArray &data)
|
||||||
|
{
|
||||||
|
qCDebug(sshLog, "%s", data.toHex().constData());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
AbstractSshPacket::AbstractSshPacket() : m_length(0) { }
|
||||||
|
AbstractSshPacket::~AbstractSshPacket() {}
|
||||||
|
|
||||||
|
bool AbstractSshPacket::isComplete() const
|
||||||
|
{
|
||||||
|
if (currentDataSize() < minPacketSize())
|
||||||
|
return false;
|
||||||
|
return 4 + length() + macLength() == currentDataSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AbstractSshPacket::clear()
|
||||||
|
{
|
||||||
|
m_data.clear();
|
||||||
|
m_length = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
SshPacketType AbstractSshPacket::type() const
|
||||||
|
{
|
||||||
|
Q_ASSERT(isComplete());
|
||||||
|
return static_cast<SshPacketType>(m_data.at(TypeOffset));
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray AbstractSshPacket::payLoad() const
|
||||||
|
{
|
||||||
|
return QByteArray(m_data.constData() + PayloadOffset,
|
||||||
|
length() - paddingLength() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AbstractSshPacket::printRawBytes() const
|
||||||
|
{
|
||||||
|
printByteArray(m_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray AbstractSshPacket::encodeString(const QByteArray &string)
|
||||||
|
{
|
||||||
|
QByteArray data;
|
||||||
|
data.resize(4);
|
||||||
|
data += string;
|
||||||
|
setLengthField(data);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray AbstractSshPacket::encodeMpInt(const Botan::BigInt &number)
|
||||||
|
{
|
||||||
|
if (number.is_zero())
|
||||||
|
return QByteArray(4, 0);
|
||||||
|
|
||||||
|
int stringLength = static_cast<int>(number.bytes());
|
||||||
|
const bool positiveAndMsbSet = number.sign() == Botan::BigInt::Positive
|
||||||
|
&& (number.byte_at(stringLength - 1) & 0x80);
|
||||||
|
if (positiveAndMsbSet)
|
||||||
|
++stringLength;
|
||||||
|
QByteArray data;
|
||||||
|
data.resize(4 + stringLength);
|
||||||
|
int pos = 4;
|
||||||
|
if (positiveAndMsbSet)
|
||||||
|
data[pos++] = '\0';
|
||||||
|
number.binary_encode(reinterpret_cast<Botan::byte *>(data.data()) + pos);
|
||||||
|
setLengthField(data);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
int AbstractSshPacket::paddingLength() const
|
||||||
|
{
|
||||||
|
return m_data[PaddingLengthOffset];
|
||||||
|
}
|
||||||
|
|
||||||
|
quint32 AbstractSshPacket::length() const
|
||||||
|
{
|
||||||
|
//Q_ASSERT(currentDataSize() >= minPacketSize());
|
||||||
|
if (m_length == 0)
|
||||||
|
calculateLength();
|
||||||
|
return m_length;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AbstractSshPacket::calculateLength() const
|
||||||
|
{
|
||||||
|
m_length = SshPacketParser::asUint32(m_data, static_cast<quint32>(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray AbstractSshPacket::generateMac(const SshAbstractCryptoFacility &crypt,
|
||||||
|
quint32 seqNr) const
|
||||||
|
{
|
||||||
|
const quint32 seqNrBe = qToBigEndian(seqNr);
|
||||||
|
QByteArray data(reinterpret_cast<const char *>(&seqNrBe), sizeof seqNrBe);
|
||||||
|
data += QByteArray(m_data.constData(), length() + 4);
|
||||||
|
return crypt.generateMac(data, data.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
quint32 AbstractSshPacket::minPacketSize() const
|
||||||
|
{
|
||||||
|
return qMax<quint32>(cipherBlockSize(), 16) + macLength();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AbstractSshPacket::setLengthField(QByteArray &data)
|
||||||
|
{
|
||||||
|
const quint32 length = qToBigEndian(data.size() - 4);
|
||||||
|
data.replace(0, 4, reinterpret_cast<const char *>(&length), 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Internal
|
||||||
|
} // namespace QSsh
|
||||||
147
client/3rd/QtSsh/src/ssh/sshpacket_p.h
Normal file
147
client/3rd/QtSsh/src/ssh/sshpacket_p.h
Normal file
|
|
@ -0,0 +1,147 @@
|
||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2016 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "sshexception_p.h"
|
||||||
|
|
||||||
|
#include <QtEndian>
|
||||||
|
#include <QByteArray>
|
||||||
|
#include <QList>
|
||||||
|
|
||||||
|
namespace Botan { class BigInt; }
|
||||||
|
|
||||||
|
namespace QSsh {
|
||||||
|
namespace Internal {
|
||||||
|
|
||||||
|
enum SshPacketType {
|
||||||
|
SSH_MSG_DISCONNECT = 1,
|
||||||
|
SSH_MSG_IGNORE = 2,
|
||||||
|
SSH_MSG_UNIMPLEMENTED = 3,
|
||||||
|
SSH_MSG_DEBUG = 4,
|
||||||
|
SSH_MSG_SERVICE_REQUEST = 5,
|
||||||
|
SSH_MSG_SERVICE_ACCEPT = 6,
|
||||||
|
|
||||||
|
SSH_MSG_KEXINIT = 20,
|
||||||
|
SSH_MSG_NEWKEYS = 21,
|
||||||
|
SSH_MSG_KEXDH_INIT = 30,
|
||||||
|
SSH_MSG_KEX_ECDH_INIT = 30,
|
||||||
|
SSH_MSG_KEXDH_REPLY = 31,
|
||||||
|
SSH_MSG_KEX_ECDH_REPLY = 31,
|
||||||
|
|
||||||
|
SSH_MSG_USERAUTH_REQUEST = 50,
|
||||||
|
SSH_MSG_USERAUTH_FAILURE = 51,
|
||||||
|
SSH_MSG_USERAUTH_SUCCESS = 52,
|
||||||
|
SSH_MSG_USERAUTH_BANNER = 53,
|
||||||
|
SSH_MSG_USERAUTH_PK_OK = 60,
|
||||||
|
SSH_MSG_USERAUTH_PASSWD_CHANGEREQ = 60,
|
||||||
|
SSH_MSG_USERAUTH_INFO_REQUEST = 60,
|
||||||
|
SSH_MSG_USERAUTH_INFO_RESPONSE = 61,
|
||||||
|
|
||||||
|
SSH_MSG_GLOBAL_REQUEST = 80,
|
||||||
|
SSH_MSG_REQUEST_SUCCESS = 81,
|
||||||
|
SSH_MSG_REQUEST_FAILURE = 82,
|
||||||
|
|
||||||
|
// TODO: We currently take no precautions against sending these messages
|
||||||
|
// during a key re-exchange, which is not allowed.
|
||||||
|
SSH_MSG_CHANNEL_OPEN = 90,
|
||||||
|
SSH_MSG_CHANNEL_OPEN_CONFIRMATION = 91,
|
||||||
|
SSH_MSG_CHANNEL_OPEN_FAILURE = 92,
|
||||||
|
SSH_MSG_CHANNEL_WINDOW_ADJUST = 93,
|
||||||
|
SSH_MSG_CHANNEL_DATA = 94,
|
||||||
|
SSH_MSG_CHANNEL_EXTENDED_DATA = 95,
|
||||||
|
SSH_MSG_CHANNEL_EOF = 96,
|
||||||
|
SSH_MSG_CHANNEL_CLOSE = 97,
|
||||||
|
SSH_MSG_CHANNEL_REQUEST = 98,
|
||||||
|
SSH_MSG_CHANNEL_SUCCESS = 99,
|
||||||
|
SSH_MSG_CHANNEL_FAILURE = 100,
|
||||||
|
|
||||||
|
// Not completely safe, since the server may actually understand this
|
||||||
|
// message type as an extension. Switch to a different value in that case
|
||||||
|
// (between 128 and 191).
|
||||||
|
SSH_MSG_INVALID = 128
|
||||||
|
};
|
||||||
|
|
||||||
|
enum SshOpenFailureType {
|
||||||
|
SSH_OPEN_ADMINISTRATIVELY_PROHIBITED = 1,
|
||||||
|
SSH_OPEN_CONNECT_FAILED = 2,
|
||||||
|
SSH_OPEN_UNKNOWN_CHANNEL_TYPE = 3,
|
||||||
|
SSH_OPEN_RESOURCE_SHORTAGE = 4
|
||||||
|
};
|
||||||
|
|
||||||
|
enum SshExtendedDataType { SSH_EXTENDED_DATA_STDERR = 1 };
|
||||||
|
|
||||||
|
class SshAbstractCryptoFacility;
|
||||||
|
|
||||||
|
class AbstractSshPacket
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~AbstractSshPacket();
|
||||||
|
|
||||||
|
void clear();
|
||||||
|
bool isComplete() const;
|
||||||
|
SshPacketType type() const;
|
||||||
|
|
||||||
|
static QByteArray encodeString(const QByteArray &string);
|
||||||
|
static QByteArray encodeMpInt(const Botan::BigInt &number);
|
||||||
|
template<typename T> static QByteArray encodeInt(T value)
|
||||||
|
{
|
||||||
|
const T valMsb = qToBigEndian(value);
|
||||||
|
return QByteArray(reinterpret_cast<const char *>(&valMsb), sizeof valMsb);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void setLengthField(QByteArray &data);
|
||||||
|
|
||||||
|
void printRawBytes() const; // For Debugging.
|
||||||
|
|
||||||
|
const QByteArray &rawData() const { return m_data; }
|
||||||
|
|
||||||
|
QByteArray payLoad() const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
AbstractSshPacket();
|
||||||
|
|
||||||
|
virtual quint32 cipherBlockSize() const = 0;
|
||||||
|
virtual quint32 macLength() const = 0;
|
||||||
|
virtual void calculateLength() const;
|
||||||
|
|
||||||
|
quint32 length() const;
|
||||||
|
int paddingLength() const;
|
||||||
|
quint32 minPacketSize() const;
|
||||||
|
quint32 currentDataSize() const { return m_data.size(); }
|
||||||
|
QByteArray generateMac(const SshAbstractCryptoFacility &crypt,
|
||||||
|
quint32 seqNr) const;
|
||||||
|
|
||||||
|
static const quint32 PaddingLengthOffset;
|
||||||
|
static const quint32 PayloadOffset;
|
||||||
|
static const quint32 TypeOffset;
|
||||||
|
static const quint32 MinPaddingLength;
|
||||||
|
|
||||||
|
mutable QByteArray m_data;
|
||||||
|
mutable quint32 m_length;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Internal
|
||||||
|
} // namespace QSsh
|
||||||
149
client/3rd/QtSsh/src/ssh/sshpacketparser.cpp
Normal file
149
client/3rd/QtSsh/src/ssh/sshpacketparser.cpp
Normal file
|
|
@ -0,0 +1,149 @@
|
||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2016 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#include "sshpacketparser_p.h"
|
||||||
|
|
||||||
|
#include <cctype>
|
||||||
|
|
||||||
|
namespace QSsh {
|
||||||
|
namespace Internal {
|
||||||
|
|
||||||
|
namespace { quint32 size(const QByteArray &data) { return data.size(); } }
|
||||||
|
|
||||||
|
QString SshPacketParser::asUserString(const QByteArray &rawString)
|
||||||
|
{
|
||||||
|
QByteArray filteredString;
|
||||||
|
filteredString.resize(rawString.size());
|
||||||
|
for (int i = 0; i < rawString.size(); ++i) {
|
||||||
|
const char c = rawString.at(i);
|
||||||
|
filteredString[i]
|
||||||
|
= std::isprint(c) || c == '\n' || c == '\r' || c == '\t' ? c : '?';
|
||||||
|
}
|
||||||
|
return QString::fromUtf8(filteredString);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SshPacketParser::asBool(const QByteArray &data, quint32 offset)
|
||||||
|
{
|
||||||
|
if (size(data) <= offset)
|
||||||
|
throw SshPacketParseException();
|
||||||
|
return data.at(offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SshPacketParser::asBool(const QByteArray &data, quint32 *offset)
|
||||||
|
{
|
||||||
|
bool b = asBool(data, *offset);
|
||||||
|
++(*offset);
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
quint32 SshPacketParser::asUint32(const QByteArray &data, quint32 offset)
|
||||||
|
{
|
||||||
|
if (size(data) < offset + 4)
|
||||||
|
throw SshPacketParseException();
|
||||||
|
const quint32 value = ((data.at(offset) & 0xff) << 24)
|
||||||
|
+ ((data.at(offset + 1) & 0xff) << 16)
|
||||||
|
+ ((data.at(offset + 2) & 0xff) << 8) + (data.at(offset + 3) & 0xff);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
quint32 SshPacketParser::asUint32(const QByteArray &data, quint32 *offset)
|
||||||
|
{
|
||||||
|
const quint32 v = asUint32(data, *offset);
|
||||||
|
*offset += 4;
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
quint64 SshPacketParser::asUint64(const QByteArray &data, quint32 offset)
|
||||||
|
{
|
||||||
|
if (size(data) < offset + 8)
|
||||||
|
throw SshPacketParseException();
|
||||||
|
const quint64 value = (static_cast<quint64>(data.at(offset) & 0xff) << 56)
|
||||||
|
+ (static_cast<quint64>(data.at(offset + 1) & 0xff) << 48)
|
||||||
|
+ (static_cast<quint64>(data.at(offset + 2) & 0xff) << 40)
|
||||||
|
+ (static_cast<quint64>(data.at(offset + 3) & 0xff) << 32)
|
||||||
|
+ ((data.at(offset + 4) & 0xff) << 24)
|
||||||
|
+ ((data.at(offset + 5) & 0xff) << 16)
|
||||||
|
+ ((data.at(offset + 6) & 0xff) << 8)
|
||||||
|
+ (data.at(offset + 7) & 0xff);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
quint64 SshPacketParser::asUint64(const QByteArray &data, quint32 *offset)
|
||||||
|
{
|
||||||
|
const quint64 val = asUint64(data, *offset);
|
||||||
|
*offset += 8;
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray SshPacketParser::asString(const QByteArray &data, quint32 *offset)
|
||||||
|
{
|
||||||
|
const quint32 length = asUint32(data, offset);
|
||||||
|
if (size(data) < *offset + length)
|
||||||
|
throw SshPacketParseException();
|
||||||
|
const QByteArray &string = data.mid(*offset, length);
|
||||||
|
*offset += length;
|
||||||
|
return string;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString SshPacketParser::asUserString(const QByteArray &data, quint32 *offset)
|
||||||
|
{
|
||||||
|
return asUserString(asString(data, offset));
|
||||||
|
}
|
||||||
|
|
||||||
|
SshNameList SshPacketParser::asNameList(const QByteArray &data, quint32 *offset)
|
||||||
|
{
|
||||||
|
const quint32 length = asUint32(data, offset);
|
||||||
|
const int listEndPos = *offset + length;
|
||||||
|
if (data.size() < listEndPos)
|
||||||
|
throw SshPacketParseException();
|
||||||
|
SshNameList names(length + 4);
|
||||||
|
int nextNameOffset = *offset;
|
||||||
|
int nextCommaOffset = data.indexOf(',', nextNameOffset);
|
||||||
|
while (nextNameOffset > 0 && nextNameOffset < listEndPos) {
|
||||||
|
const int stringEndPos = nextCommaOffset == -1
|
||||||
|
|| nextCommaOffset > listEndPos ? listEndPos : nextCommaOffset;
|
||||||
|
names.names << QByteArray(data.constData() + nextNameOffset,
|
||||||
|
stringEndPos - nextNameOffset);
|
||||||
|
nextNameOffset = nextCommaOffset + 1;
|
||||||
|
nextCommaOffset = data.indexOf(',', nextNameOffset);
|
||||||
|
}
|
||||||
|
*offset += length;
|
||||||
|
return names;
|
||||||
|
}
|
||||||
|
|
||||||
|
Botan::BigInt SshPacketParser::asBigInt(const QByteArray &data, quint32 *offset)
|
||||||
|
{
|
||||||
|
const quint32 length = asUint32(data, offset);
|
||||||
|
if (length == 0)
|
||||||
|
return Botan::BigInt();
|
||||||
|
const Botan::byte *numberStart
|
||||||
|
= reinterpret_cast<const Botan::byte *>(data.constData() + *offset);
|
||||||
|
*offset += length;
|
||||||
|
return Botan::BigInt::decode(numberStart, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Internal
|
||||||
|
} // namespace QSsh
|
||||||
74
client/3rd/QtSsh/src/ssh/sshpacketparser_p.h
Normal file
74
client/3rd/QtSsh/src/ssh/sshpacketparser_p.h
Normal file
|
|
@ -0,0 +1,74 @@
|
||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2016 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <botan/botan.h>
|
||||||
|
|
||||||
|
#include <QByteArray>
|
||||||
|
#include <QList>
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
namespace QSsh {
|
||||||
|
namespace Internal {
|
||||||
|
|
||||||
|
struct SshNameList
|
||||||
|
{
|
||||||
|
SshNameList() : originalLength(0) {}
|
||||||
|
SshNameList(quint32 originalLength) : originalLength(originalLength) {}
|
||||||
|
quint32 originalLength;
|
||||||
|
QList<QByteArray> names;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SshPacketParseException { };
|
||||||
|
|
||||||
|
// This class's functions try to read a byte array at a certain offset
|
||||||
|
// as the respective chunk of data as specified in the SSH RFCs.
|
||||||
|
// If they succeed, they update the offset, so they can easily
|
||||||
|
// be called in succession by client code.
|
||||||
|
// For convenience, some have also versions that don't update the offset,
|
||||||
|
// so they can be called with rvalues if the new value is not needed.
|
||||||
|
// If they fail, they throw an SshPacketParseException.
|
||||||
|
class SshPacketParser
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static bool asBool(const QByteArray &data, quint32 offset);
|
||||||
|
static bool asBool(const QByteArray &data, quint32 *offset);
|
||||||
|
static quint16 asUint16(const QByteArray &data, quint32 offset);
|
||||||
|
static quint16 asUint16(const QByteArray &data, quint32 *offset);
|
||||||
|
static quint64 asUint64(const QByteArray &data, quint32 offset);
|
||||||
|
static quint64 asUint64(const QByteArray &data, quint32 *offset);
|
||||||
|
static quint32 asUint32(const QByteArray &data, quint32 offset);
|
||||||
|
static quint32 asUint32(const QByteArray &data, quint32 *offset);
|
||||||
|
static QByteArray asString(const QByteArray &data, quint32 *offset);
|
||||||
|
static QString asUserString(const QByteArray &data, quint32 *offset);
|
||||||
|
static SshNameList asNameList(const QByteArray &data, quint32 *offset);
|
||||||
|
static Botan::BigInt asBigInt(const QByteArray &data, quint32 *offset);
|
||||||
|
|
||||||
|
static QString asUserString(const QByteArray &rawString);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Internal
|
||||||
|
} // namespace QSsh
|
||||||
110
client/3rd/QtSsh/src/ssh/sshpseudoterminal.h
Normal file
110
client/3rd/QtSsh/src/ssh/sshpseudoterminal.h
Normal file
|
|
@ -0,0 +1,110 @@
|
||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2016 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "ssh_global.h"
|
||||||
|
|
||||||
|
#include <QByteArray>
|
||||||
|
#include <QHash>
|
||||||
|
|
||||||
|
namespace QSsh {
|
||||||
|
|
||||||
|
class QSSH_EXPORT SshPseudoTerminal
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit SshPseudoTerminal(const QByteArray &termType = "vt100",
|
||||||
|
int rowCount = 24, int columnCount = 80)
|
||||||
|
: termType(termType), rowCount(rowCount), columnCount(columnCount) {}
|
||||||
|
|
||||||
|
QByteArray termType;
|
||||||
|
int rowCount;
|
||||||
|
int columnCount;
|
||||||
|
|
||||||
|
enum Mode {
|
||||||
|
VINTR = 1, // Interrupt character.
|
||||||
|
VQUIT = 2, // The quit character (sends SIGQUIT signal on POSIX systems).
|
||||||
|
VERASE = 3, // Erase the character to left of the cursor.
|
||||||
|
VKILL = 4, // Kill the current input line.
|
||||||
|
VEOF = 5, // End-of-file character (sends EOF from the terminal).
|
||||||
|
VEOL = 6, // End-of-line character in addition to carriage return and/or linefeed.
|
||||||
|
VEOL2 = 7, // Additional end-of-line character.
|
||||||
|
VSTART = 8, // Continues paused output (normally control-Q).
|
||||||
|
VSTOP = 9, // Pauses output (normally control-S).
|
||||||
|
VSUSP = 10, // Suspends the current program.
|
||||||
|
VDSUSP = 11, // Another suspend character.
|
||||||
|
VREPRINT = 12, // Reprints the current input line.
|
||||||
|
VWERASE = 13, // Erases a word left of cursor.
|
||||||
|
VLNEXT = 14, // Enter the next character typed literally, even if it is a special character.
|
||||||
|
VFLUSH = 15, // Character to flush output.
|
||||||
|
VSWTCH = 16, // Switch to a different shell layer.
|
||||||
|
VSTATUS = 17, // Prints system status line (load, command, pid, etc).
|
||||||
|
VDISCARD = 18, // Toggles the flushing of terminal output.
|
||||||
|
|
||||||
|
IGNPAR = 30, // The ignore parity flag. The parameter SHOULD be 0 if this flag is FALSE, and 1 if it is TRUE.
|
||||||
|
PARMRK = 31, // Mark parity and framing errors.
|
||||||
|
INPCK = 32, // Enable checking of parity errors.
|
||||||
|
ISTRIP = 33, // Strip 8th bit off characters.
|
||||||
|
INLCR = 34, // Map NL into CR on input.
|
||||||
|
IGNCR = 35, // Ignore CR on input.
|
||||||
|
ICRNL = 36, // Map CR to NL on input.
|
||||||
|
IUCLC = 37, // Translate uppercase characters to lowercase.
|
||||||
|
IXON = 38, // Enable output flow control.
|
||||||
|
IXANY = 39, // Any char will restart after stop.
|
||||||
|
IXOFF = 40, // Enable input flow control.
|
||||||
|
IMAXBEL = 41, // Ring bell on input queue full.
|
||||||
|
ISIG = 50, // Enable signals INTR, QUIT, [D]SUSP.
|
||||||
|
ICANON = 51, // Canonicalize input lines.
|
||||||
|
XCASE = 52, // Enable input and output of uppercase characters by preceding their lowercase equivalents with "\".
|
||||||
|
ECHO = 53, // Enable echoing.
|
||||||
|
ECHOE = 54, // Visually erase chars.
|
||||||
|
ECHOK = 55, // Kill character discards current line.
|
||||||
|
ECHONL = 56, // Echo NL even if ECHO is off.
|
||||||
|
NOFLSH = 57, // Don't flush after interrupt.
|
||||||
|
TOSTOP = 58, // Stop background jobs from output.
|
||||||
|
IEXTEN = 59, // Enable extensions.
|
||||||
|
ECHOCTL = 60, // Echo control characters as ^(Char).
|
||||||
|
ECHOKE = 61, // Visual erase for line kill.
|
||||||
|
PENDIN = 62, // Retype pending input.
|
||||||
|
OPOST = 70, // Enable output processing.
|
||||||
|
OLCUC = 71, // Convert lowercase to uppercase.
|
||||||
|
ONLCR = 72, // Map NL to CR-NL.
|
||||||
|
OCRNL = 73, // Translate carriage return to newline (output).
|
||||||
|
ONOCR = 74, // Translate newline to carriage return-newline (output).
|
||||||
|
ONLRET = 75, // Newline performs a carriage return (output).
|
||||||
|
CS7 = 90, // 7 bit mode.
|
||||||
|
CS8 = 91, // 8 bit mode.
|
||||||
|
PARENB = 92, // Parity enable.
|
||||||
|
PARODD = 93, // Odd parity, else even.
|
||||||
|
|
||||||
|
TTY_OP_ISPEED = 128, // Specifies the input baud rate in bits per second.
|
||||||
|
TTY_OP_OSPEED = 129 // Specifies the output baud rate in bits per second.
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef QHash<Mode, quint32> ModeMap;
|
||||||
|
ModeMap modes;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace QSsh
|
||||||
380
client/3rd/QtSsh/src/ssh/sshremoteprocess.cpp
Normal file
380
client/3rd/QtSsh/src/ssh/sshremoteprocess.cpp
Normal file
|
|
@ -0,0 +1,380 @@
|
||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2016 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#include "sshremoteprocess.h"
|
||||||
|
#include "sshremoteprocess_p.h"
|
||||||
|
|
||||||
|
#include "ssh_global.h"
|
||||||
|
#include "sshincomingpacket_p.h"
|
||||||
|
#include "sshlogging_p.h"
|
||||||
|
#include "sshsendfacility_p.h"
|
||||||
|
|
||||||
|
#include <botan/botan.h>
|
||||||
|
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
#include <cstdlib>
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\class QSsh::SshRemoteProcess
|
||||||
|
|
||||||
|
\brief The SshRemoteProcess class implements an SSH channel for running a
|
||||||
|
remote process.
|
||||||
|
|
||||||
|
Objects are created via SshConnection::createRemoteProcess.
|
||||||
|
The process is started via the start() member function.
|
||||||
|
If the process needs a pseudo terminal, you can request one
|
||||||
|
via requestTerminal() before calling start().
|
||||||
|
Note that this class does not support QIODevice's waitFor*() functions, i.e. it has
|
||||||
|
no synchronous mode.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace QSsh {
|
||||||
|
|
||||||
|
const struct {
|
||||||
|
SshRemoteProcess::Signal signalEnum;
|
||||||
|
const char * const signalString;
|
||||||
|
} signalMap[] = {
|
||||||
|
{SshRemoteProcess::AbrtSignal, "ABRT"}, {SshRemoteProcess::AlrmSignal, "ALRM"},
|
||||||
|
{SshRemoteProcess::FpeSignal, "FPE"}, {SshRemoteProcess::HupSignal, "HUP"},
|
||||||
|
{SshRemoteProcess::IllSignal, "ILL"}, {SshRemoteProcess::IntSignal, "INT"},
|
||||||
|
{SshRemoteProcess::KillSignal, "KILL"}, {SshRemoteProcess::PipeSignal, "PIPE"},
|
||||||
|
{SshRemoteProcess::QuitSignal, "QUIT"}, {SshRemoteProcess::SegvSignal, "SEGV"},
|
||||||
|
{SshRemoteProcess::TermSignal, "TERM"}, {SshRemoteProcess::Usr1Signal, "USR1"},
|
||||||
|
{SshRemoteProcess::Usr2Signal, "USR2"}
|
||||||
|
};
|
||||||
|
|
||||||
|
SshRemoteProcess::SshRemoteProcess(const QByteArray &command, quint32 channelId,
|
||||||
|
Internal::SshSendFacility &sendFacility)
|
||||||
|
: d(new Internal::SshRemoteProcessPrivate(command, channelId, sendFacility, this))
|
||||||
|
{
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
SshRemoteProcess::SshRemoteProcess(quint32 channelId, Internal::SshSendFacility &sendFacility)
|
||||||
|
: d(new Internal::SshRemoteProcessPrivate(channelId, sendFacility, this))
|
||||||
|
{
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
SshRemoteProcess::~SshRemoteProcess()
|
||||||
|
{
|
||||||
|
QSSH_ASSERT(d->channelState() != Internal::AbstractSshChannel::SessionEstablished);
|
||||||
|
close();
|
||||||
|
delete d;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SshRemoteProcess::atEnd() const
|
||||||
|
{
|
||||||
|
return QIODevice::atEnd() && d->data().isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
qint64 SshRemoteProcess::bytesAvailable() const
|
||||||
|
{
|
||||||
|
return QIODevice::bytesAvailable() + d->data().count();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SshRemoteProcess::canReadLine() const
|
||||||
|
{
|
||||||
|
return QIODevice::canReadLine() || d->data().contains('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray SshRemoteProcess::readAllStandardOutput()
|
||||||
|
{
|
||||||
|
return readAllFromChannel(QProcess::StandardOutput);
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray SshRemoteProcess::readAllStandardError()
|
||||||
|
{
|
||||||
|
return readAllFromChannel(QProcess::StandardError);
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray SshRemoteProcess::readAllFromChannel(QProcess::ProcessChannel channel)
|
||||||
|
{
|
||||||
|
const QProcess::ProcessChannel currentReadChannel = readChannel();
|
||||||
|
setReadChannel(channel);
|
||||||
|
const QByteArray &data = readAll();
|
||||||
|
setReadChannel(currentReadChannel);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshRemoteProcess::close()
|
||||||
|
{
|
||||||
|
d->closeChannel();
|
||||||
|
QIODevice::close();
|
||||||
|
}
|
||||||
|
|
||||||
|
qint64 SshRemoteProcess::readData(char *data, qint64 maxlen)
|
||||||
|
{
|
||||||
|
const qint64 bytesRead = qMin(qint64(d->data().count()), maxlen);
|
||||||
|
memcpy(data, d->data().constData(), bytesRead);
|
||||||
|
d->data().remove(0, bytesRead);
|
||||||
|
return bytesRead;
|
||||||
|
}
|
||||||
|
|
||||||
|
qint64 SshRemoteProcess::writeData(const char *data, qint64 len)
|
||||||
|
{
|
||||||
|
if (isRunning()) {
|
||||||
|
d->sendData(QByteArray(data, len));
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
QProcess::ProcessChannel SshRemoteProcess::readChannel() const
|
||||||
|
{
|
||||||
|
return d->m_readChannel;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshRemoteProcess::setReadChannel(QProcess::ProcessChannel channel)
|
||||||
|
{
|
||||||
|
d->m_readChannel = channel;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshRemoteProcess::init()
|
||||||
|
{
|
||||||
|
connect(d, &Internal::SshRemoteProcessPrivate::started,
|
||||||
|
this, &SshRemoteProcess::started, Qt::QueuedConnection);
|
||||||
|
connect(d, &Internal::SshRemoteProcessPrivate::readyReadStandardOutput,
|
||||||
|
this, &SshRemoteProcess::readyReadStandardOutput, Qt::QueuedConnection);
|
||||||
|
connect(d, &Internal::SshRemoteProcessPrivate::readyRead,
|
||||||
|
this, &SshRemoteProcess::readyRead, Qt::QueuedConnection);
|
||||||
|
connect(d, &Internal::SshRemoteProcessPrivate::readyReadStandardError,
|
||||||
|
this, &SshRemoteProcess::readyReadStandardError, Qt::QueuedConnection);
|
||||||
|
connect(d, &Internal::SshRemoteProcessPrivate::closed,
|
||||||
|
this, &SshRemoteProcess::closed, Qt::QueuedConnection);
|
||||||
|
connect(d, &Internal::SshRemoteProcessPrivate::eof,
|
||||||
|
this, &SshRemoteProcess::readChannelFinished, Qt::QueuedConnection);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshRemoteProcess::addToEnvironment(const QByteArray &var, const QByteArray &value)
|
||||||
|
{
|
||||||
|
if (d->channelState() == Internal::SshRemoteProcessPrivate::Inactive)
|
||||||
|
d->m_env << qMakePair(var, value); // Cached locally and sent on start()
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshRemoteProcess::clearEnvironment()
|
||||||
|
{
|
||||||
|
d->m_env.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshRemoteProcess::requestTerminal(const SshPseudoTerminal &terminal)
|
||||||
|
{
|
||||||
|
QSSH_ASSERT_AND_RETURN(d->channelState() == Internal::SshRemoteProcessPrivate::Inactive);
|
||||||
|
d->m_useTerminal = true;
|
||||||
|
d->m_terminal = terminal;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshRemoteProcess::start()
|
||||||
|
{
|
||||||
|
if (d->channelState() == Internal::SshRemoteProcessPrivate::Inactive) {
|
||||||
|
qCDebug(Internal::sshLog, "process start requested, channel id = %u", d->localChannelId());
|
||||||
|
QIODevice::open(QIODevice::ReadWrite);
|
||||||
|
d->requestSessionStart();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshRemoteProcess::sendSignal(Signal signal)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
if (isRunning()) {
|
||||||
|
const char *signalString = 0;
|
||||||
|
for (size_t i = 0; i < sizeof signalMap/sizeof *signalMap && !signalString; ++i) {
|
||||||
|
if (signalMap[i].signalEnum == signal)
|
||||||
|
signalString = signalMap[i].signalString;
|
||||||
|
}
|
||||||
|
QSSH_ASSERT_AND_RETURN(signalString);
|
||||||
|
d->m_sendFacility.sendChannelSignalPacket(d->remoteChannel(), signalString);
|
||||||
|
}
|
||||||
|
} catch (const std::exception &e) {
|
||||||
|
setErrorString(QString::fromLatin1(e.what()));
|
||||||
|
d->closeChannel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SshRemoteProcess::isRunning() const
|
||||||
|
{
|
||||||
|
return d->m_procState == Internal::SshRemoteProcessPrivate::Running;
|
||||||
|
}
|
||||||
|
|
||||||
|
int SshRemoteProcess::exitCode() const { return d->m_exitCode; }
|
||||||
|
|
||||||
|
SshRemoteProcess::Signal SshRemoteProcess::exitSignal() const
|
||||||
|
{
|
||||||
|
return static_cast<SshRemoteProcess::Signal>(d->m_signal);
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Internal {
|
||||||
|
|
||||||
|
SshRemoteProcessPrivate::SshRemoteProcessPrivate(const QByteArray &command,
|
||||||
|
quint32 channelId, SshSendFacility &sendFacility, SshRemoteProcess *proc)
|
||||||
|
: AbstractSshChannel(channelId, sendFacility),
|
||||||
|
m_command(command),
|
||||||
|
m_isShell(false),
|
||||||
|
m_useTerminal(false),
|
||||||
|
m_proc(proc)
|
||||||
|
{
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
SshRemoteProcessPrivate::SshRemoteProcessPrivate(quint32 channelId, SshSendFacility &sendFacility,
|
||||||
|
SshRemoteProcess *proc)
|
||||||
|
: AbstractSshChannel(channelId, sendFacility),
|
||||||
|
m_isShell(true),
|
||||||
|
m_useTerminal(true),
|
||||||
|
m_proc(proc)
|
||||||
|
{
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshRemoteProcessPrivate::init()
|
||||||
|
{
|
||||||
|
m_procState = NotYetStarted;
|
||||||
|
m_wasRunning = false;
|
||||||
|
m_exitCode = 0;
|
||||||
|
m_readChannel = QProcess::StandardOutput;
|
||||||
|
m_signal = SshRemoteProcess::NoSignal;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshRemoteProcessPrivate::setProcState(ProcessState newState)
|
||||||
|
{
|
||||||
|
qCDebug(sshLog, "channel: old state = %d,new state = %d", m_procState, newState);
|
||||||
|
m_procState = newState;
|
||||||
|
if (newState == StartFailed) {
|
||||||
|
emit closed(SshRemoteProcess::FailedToStart);
|
||||||
|
} else if (newState == Running) {
|
||||||
|
m_wasRunning = true;
|
||||||
|
emit started();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray &SshRemoteProcessPrivate::data()
|
||||||
|
{
|
||||||
|
return m_readChannel == QProcess::StandardOutput ? m_stdout : m_stderr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshRemoteProcessPrivate::closeHook()
|
||||||
|
{
|
||||||
|
if (m_wasRunning) {
|
||||||
|
if (m_signal != SshRemoteProcess::NoSignal)
|
||||||
|
emit closed(SshRemoteProcess::CrashExit);
|
||||||
|
else
|
||||||
|
emit closed(SshRemoteProcess::NormalExit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshRemoteProcessPrivate::handleOpenSuccessInternal()
|
||||||
|
{
|
||||||
|
foreach (const EnvVar &envVar, m_env) {
|
||||||
|
m_sendFacility.sendEnvPacket(remoteChannel(), envVar.first,
|
||||||
|
envVar.second);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_useTerminal)
|
||||||
|
m_sendFacility.sendPtyRequestPacket(remoteChannel(), m_terminal);
|
||||||
|
|
||||||
|
if (m_isShell)
|
||||||
|
m_sendFacility.sendShellPacket(remoteChannel());
|
||||||
|
else
|
||||||
|
m_sendFacility.sendExecPacket(remoteChannel(), m_command);
|
||||||
|
setProcState(ExecRequested);
|
||||||
|
m_timeoutTimer.start(ReplyTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshRemoteProcessPrivate::handleOpenFailureInternal(const QString &reason)
|
||||||
|
{
|
||||||
|
setProcState(StartFailed);
|
||||||
|
m_proc->setErrorString(reason);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshRemoteProcessPrivate::handleChannelSuccess()
|
||||||
|
{
|
||||||
|
if (m_procState != ExecRequested) {
|
||||||
|
throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
|
||||||
|
"Unexpected SSH_MSG_CHANNEL_SUCCESS message.");
|
||||||
|
}
|
||||||
|
m_timeoutTimer.stop();
|
||||||
|
setProcState(Running);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshRemoteProcessPrivate::handleChannelFailure()
|
||||||
|
{
|
||||||
|
if (m_procState != ExecRequested) {
|
||||||
|
throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
|
||||||
|
"Unexpected SSH_MSG_CHANNEL_FAILURE message.");
|
||||||
|
}
|
||||||
|
m_timeoutTimer.stop();
|
||||||
|
setProcState(StartFailed);
|
||||||
|
closeChannel();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshRemoteProcessPrivate::handleChannelDataInternal(const QByteArray &data)
|
||||||
|
{
|
||||||
|
m_stdout += data;
|
||||||
|
emit readyReadStandardOutput();
|
||||||
|
if (m_readChannel == QProcess::StandardOutput)
|
||||||
|
emit readyRead();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshRemoteProcessPrivate::handleChannelExtendedDataInternal(quint32 type,
|
||||||
|
const QByteArray &data)
|
||||||
|
{
|
||||||
|
if (type != SSH_EXTENDED_DATA_STDERR) {
|
||||||
|
qCWarning(sshLog, "Unknown extended data type %u", type);
|
||||||
|
} else {
|
||||||
|
m_stderr += data;
|
||||||
|
emit readyReadStandardError();
|
||||||
|
if (m_readChannel == QProcess::StandardError)
|
||||||
|
emit readyRead();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshRemoteProcessPrivate::handleExitStatus(const SshChannelExitStatus &exitStatus)
|
||||||
|
{
|
||||||
|
qCDebug(sshLog, "Process exiting with exit code %d", exitStatus.exitStatus);
|
||||||
|
m_exitCode = exitStatus.exitStatus;
|
||||||
|
m_procState = Exited;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshRemoteProcessPrivate::handleExitSignal(const SshChannelExitSignal &signal)
|
||||||
|
{
|
||||||
|
qCDebug(sshLog, "Exit due to signal %s", signal.signal.data());
|
||||||
|
|
||||||
|
for (size_t i = 0; i < sizeof signalMap/sizeof *signalMap; ++i) {
|
||||||
|
if (signalMap[i].signalString == signal.signal) {
|
||||||
|
m_signal = signalMap[i].signalEnum;
|
||||||
|
m_procState = Exited;
|
||||||
|
m_proc->setErrorString(tr("Process killed by signal"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw SshServerException(SSH_DISCONNECT_PROTOCOL_ERROR, "Invalid signal",
|
||||||
|
tr("Server sent invalid signal \"%1\"").arg(QString::fromUtf8(signal.signal)));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Internal
|
||||||
|
} // namespace QSsh
|
||||||
121
client/3rd/QtSsh/src/ssh/sshremoteprocess.h
Normal file
121
client/3rd/QtSsh/src/ssh/sshremoteprocess.h
Normal file
|
|
@ -0,0 +1,121 @@
|
||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2016 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "ssh_global.h"
|
||||||
|
|
||||||
|
#include <QProcess>
|
||||||
|
#include <QSharedPointer>
|
||||||
|
|
||||||
|
QT_BEGIN_NAMESPACE
|
||||||
|
class QByteArray;
|
||||||
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
|
namespace QSsh {
|
||||||
|
class SshPseudoTerminal;
|
||||||
|
namespace Internal {
|
||||||
|
class SshChannelManager;
|
||||||
|
class SshRemoteProcessPrivate;
|
||||||
|
class SshSendFacility;
|
||||||
|
} // namespace Internal
|
||||||
|
|
||||||
|
// TODO: ProcessChannel
|
||||||
|
class QSSH_EXPORT SshRemoteProcess : public QIODevice
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
friend class Internal::SshChannelManager;
|
||||||
|
friend class Internal::SshRemoteProcessPrivate;
|
||||||
|
|
||||||
|
public:
|
||||||
|
typedef QSharedPointer<SshRemoteProcess> Ptr;
|
||||||
|
enum ExitStatus { FailedToStart, CrashExit, NormalExit };
|
||||||
|
enum Signal {
|
||||||
|
AbrtSignal, AlrmSignal, FpeSignal, HupSignal, IllSignal, IntSignal, KillSignal, PipeSignal,
|
||||||
|
QuitSignal, SegvSignal, TermSignal, Usr1Signal, Usr2Signal, NoSignal
|
||||||
|
};
|
||||||
|
|
||||||
|
~SshRemoteProcess();
|
||||||
|
|
||||||
|
// QIODevice stuff
|
||||||
|
bool atEnd() const;
|
||||||
|
qint64 bytesAvailable() const;
|
||||||
|
bool canReadLine() const;
|
||||||
|
void close();
|
||||||
|
bool isSequential() const { return true; }
|
||||||
|
|
||||||
|
QProcess::ProcessChannel readChannel() const;
|
||||||
|
void setReadChannel(QProcess::ProcessChannel channel);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Note that this is of limited value in practice, because servers are
|
||||||
|
* usually configured to ignore such requests for security reasons.
|
||||||
|
*/
|
||||||
|
void addToEnvironment(const QByteArray &var, const QByteArray &value);
|
||||||
|
void clearEnvironment();
|
||||||
|
|
||||||
|
void requestTerminal(const SshPseudoTerminal &terminal);
|
||||||
|
void start();
|
||||||
|
|
||||||
|
bool isRunning() const;
|
||||||
|
int exitCode() const;
|
||||||
|
Signal exitSignal() const;
|
||||||
|
|
||||||
|
QByteArray readAllStandardOutput();
|
||||||
|
QByteArray readAllStandardError();
|
||||||
|
|
||||||
|
// Note: This is ignored by the OpenSSH server.
|
||||||
|
void sendSignal(Signal signal);
|
||||||
|
void kill() { sendSignal(KillSignal); }
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void started();
|
||||||
|
|
||||||
|
void readyReadStandardOutput();
|
||||||
|
void readyReadStandardError();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Parameter is of type ExitStatus, but we use int because of
|
||||||
|
* signal/slot awkwardness (full namespace required).
|
||||||
|
*/
|
||||||
|
void closed(int exitStatus);
|
||||||
|
|
||||||
|
private:
|
||||||
|
SshRemoteProcess(const QByteArray &command, quint32 channelId,
|
||||||
|
Internal::SshSendFacility &sendFacility);
|
||||||
|
SshRemoteProcess(quint32 channelId, Internal::SshSendFacility &sendFacility);
|
||||||
|
|
||||||
|
// QIODevice stuff
|
||||||
|
qint64 readData(char *data, qint64 maxlen);
|
||||||
|
qint64 writeData(const char *data, qint64 len);
|
||||||
|
|
||||||
|
void init();
|
||||||
|
QByteArray readAllFromChannel(QProcess::ProcessChannel channel);
|
||||||
|
|
||||||
|
Internal::SshRemoteProcessPrivate *d;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace QSsh
|
||||||
103
client/3rd/QtSsh/src/ssh/sshremoteprocess_p.h
Normal file
103
client/3rd/QtSsh/src/ssh/sshremoteprocess_p.h
Normal file
|
|
@ -0,0 +1,103 @@
|
||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2016 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "sshpseudoterminal.h"
|
||||||
|
|
||||||
|
#include "sshchannel_p.h"
|
||||||
|
|
||||||
|
#include <QList>
|
||||||
|
#include <QPair>
|
||||||
|
#include <QProcess>
|
||||||
|
|
||||||
|
namespace QSsh {
|
||||||
|
class SshRemoteProcess;
|
||||||
|
|
||||||
|
namespace Internal {
|
||||||
|
class SshSendFacility;
|
||||||
|
|
||||||
|
class SshRemoteProcessPrivate : public AbstractSshChannel
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
friend class QSsh::SshRemoteProcess;
|
||||||
|
public:
|
||||||
|
enum ProcessState {
|
||||||
|
NotYetStarted, ExecRequested, StartFailed, Running, Exited
|
||||||
|
};
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void started();
|
||||||
|
void readyRead();
|
||||||
|
void readyReadStandardOutput();
|
||||||
|
void readyReadStandardError();
|
||||||
|
void closed(int exitStatus);
|
||||||
|
|
||||||
|
private:
|
||||||
|
SshRemoteProcessPrivate(const QByteArray &command, quint32 channelId,
|
||||||
|
SshSendFacility &sendFacility, SshRemoteProcess *proc);
|
||||||
|
SshRemoteProcessPrivate(quint32 channelId, SshSendFacility &sendFacility,
|
||||||
|
SshRemoteProcess *proc);
|
||||||
|
|
||||||
|
virtual void handleChannelSuccess();
|
||||||
|
virtual void handleChannelFailure();
|
||||||
|
|
||||||
|
virtual void handleOpenSuccessInternal();
|
||||||
|
virtual void handleOpenFailureInternal(const QString &reason);
|
||||||
|
virtual void handleChannelDataInternal(const QByteArray &data);
|
||||||
|
virtual void handleChannelExtendedDataInternal(quint32 type,
|
||||||
|
const QByteArray &data);
|
||||||
|
virtual void handleExitStatus(const SshChannelExitStatus &exitStatus);
|
||||||
|
virtual void handleExitSignal(const SshChannelExitSignal &signal);
|
||||||
|
|
||||||
|
virtual void closeHook();
|
||||||
|
|
||||||
|
void init();
|
||||||
|
void setProcState(ProcessState newState);
|
||||||
|
QByteArray &data();
|
||||||
|
|
||||||
|
QProcess::ProcessChannel m_readChannel;
|
||||||
|
|
||||||
|
ProcessState m_procState;
|
||||||
|
bool m_wasRunning;
|
||||||
|
int m_signal;
|
||||||
|
int m_exitCode;
|
||||||
|
|
||||||
|
const QByteArray m_command;
|
||||||
|
const bool m_isShell;
|
||||||
|
|
||||||
|
typedef QPair<QByteArray, QByteArray> EnvVar;
|
||||||
|
QList<EnvVar> m_env;
|
||||||
|
bool m_useTerminal;
|
||||||
|
SshPseudoTerminal m_terminal;
|
||||||
|
|
||||||
|
QByteArray m_stdout;
|
||||||
|
QByteArray m_stderr;
|
||||||
|
|
||||||
|
SshRemoteProcess *m_proc;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Internal
|
||||||
|
} // namespace QSsh
|
||||||
286
client/3rd/QtSsh/src/ssh/sshremoteprocessrunner.cpp
Normal file
286
client/3rd/QtSsh/src/ssh/sshremoteprocessrunner.cpp
Normal file
|
|
@ -0,0 +1,286 @@
|
||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2016 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#include "sshremoteprocessrunner.h"
|
||||||
|
|
||||||
|
#include "sshconnectionmanager.h"
|
||||||
|
#include "sshpseudoterminal.h"
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\class QSsh::SshRemoteProcessRunner
|
||||||
|
|
||||||
|
\brief The SshRemoteProcessRunner class is a convenience class for
|
||||||
|
running a remote process over an SSH connection.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace QSsh {
|
||||||
|
namespace Internal {
|
||||||
|
namespace {
|
||||||
|
enum State { Inactive, Connecting, Connected, ProcessRunning };
|
||||||
|
} // anonymous namespace
|
||||||
|
|
||||||
|
class SshRemoteProcessRunnerPrivate
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SshRemoteProcessRunnerPrivate() : m_state(Inactive) {}
|
||||||
|
|
||||||
|
SshRemoteProcess::Ptr m_process;
|
||||||
|
SshConnection *m_connection;
|
||||||
|
bool m_runInTerminal;
|
||||||
|
SshPseudoTerminal m_terminal;
|
||||||
|
QByteArray m_command;
|
||||||
|
QSsh::SshError m_lastConnectionError;
|
||||||
|
QString m_lastConnectionErrorString;
|
||||||
|
SshRemoteProcess::ExitStatus m_exitStatus;
|
||||||
|
SshRemoteProcess::Signal m_exitSignal;
|
||||||
|
QByteArray m_stdout;
|
||||||
|
QByteArray m_stderr;
|
||||||
|
int m_exitCode;
|
||||||
|
QString m_processErrorString;
|
||||||
|
State m_state;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Internal
|
||||||
|
|
||||||
|
using namespace Internal;
|
||||||
|
|
||||||
|
SshRemoteProcessRunner::SshRemoteProcessRunner(QObject *parent)
|
||||||
|
: QObject(parent), d(new SshRemoteProcessRunnerPrivate)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
SshRemoteProcessRunner::~SshRemoteProcessRunner()
|
||||||
|
{
|
||||||
|
disconnect();
|
||||||
|
setState(Inactive);
|
||||||
|
delete d;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshRemoteProcessRunner::run(const QByteArray &command,
|
||||||
|
const SshConnectionParameters &sshParams)
|
||||||
|
{
|
||||||
|
QSSH_ASSERT_AND_RETURN(d->m_state == Inactive);
|
||||||
|
|
||||||
|
d->m_runInTerminal = false;
|
||||||
|
runInternal(command, sshParams);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshRemoteProcessRunner::runInTerminal(const QByteArray &command,
|
||||||
|
const SshPseudoTerminal &terminal, const SshConnectionParameters &sshParams)
|
||||||
|
{
|
||||||
|
d->m_terminal = terminal;
|
||||||
|
d->m_runInTerminal = true;
|
||||||
|
runInternal(command, sshParams);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshRemoteProcessRunner::runInternal(const QByteArray &command,
|
||||||
|
const SshConnectionParameters &sshParams)
|
||||||
|
{
|
||||||
|
setState(Connecting);
|
||||||
|
|
||||||
|
d->m_lastConnectionError = SshNoError;
|
||||||
|
d->m_lastConnectionErrorString.clear();
|
||||||
|
d->m_processErrorString.clear();
|
||||||
|
d->m_exitSignal = SshRemoteProcess::NoSignal;
|
||||||
|
d->m_exitCode = -1;
|
||||||
|
d->m_command = command;
|
||||||
|
d->m_connection = QSsh::acquireConnection(sshParams);
|
||||||
|
connect(d->m_connection, &SshConnection::error,
|
||||||
|
this, &SshRemoteProcessRunner::handleConnectionError);
|
||||||
|
connect(d->m_connection, &SshConnection::disconnected,
|
||||||
|
this, &SshRemoteProcessRunner::handleDisconnected);
|
||||||
|
if (d->m_connection->state() == SshConnection::Connected) {
|
||||||
|
handleConnected();
|
||||||
|
} else {
|
||||||
|
connect(d->m_connection, &SshConnection::connected, this, &SshRemoteProcessRunner::handleConnected);
|
||||||
|
if (d->m_connection->state() == SshConnection::Unconnected)
|
||||||
|
d->m_connection->connectToHost();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshRemoteProcessRunner::handleConnected()
|
||||||
|
{
|
||||||
|
QSSH_ASSERT_AND_RETURN(d->m_state == Connecting);
|
||||||
|
setState(Connected);
|
||||||
|
|
||||||
|
d->m_process = d->m_connection->createRemoteProcess(d->m_command);
|
||||||
|
connect(d->m_process.data(), &SshRemoteProcess::started,
|
||||||
|
this, &SshRemoteProcessRunner::handleProcessStarted);
|
||||||
|
connect(d->m_process.data(), &SshRemoteProcess::closed,
|
||||||
|
this, &SshRemoteProcessRunner::handleProcessFinished);
|
||||||
|
connect(d->m_process.data(), &SshRemoteProcess::readyReadStandardOutput,
|
||||||
|
this, &SshRemoteProcessRunner::handleStdout);
|
||||||
|
connect(d->m_process.data(), &SshRemoteProcess::readyReadStandardError,
|
||||||
|
this, &SshRemoteProcessRunner::handleStderr);
|
||||||
|
if (d->m_runInTerminal)
|
||||||
|
d->m_process->requestTerminal(d->m_terminal);
|
||||||
|
d->m_process->start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshRemoteProcessRunner::handleConnectionError(QSsh::SshError error)
|
||||||
|
{
|
||||||
|
d->m_lastConnectionError = error;
|
||||||
|
d->m_lastConnectionErrorString = d->m_connection->errorString();
|
||||||
|
handleDisconnected();
|
||||||
|
emit connectionError();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshRemoteProcessRunner::handleDisconnected()
|
||||||
|
{
|
||||||
|
QSSH_ASSERT_AND_RETURN(d->m_state == Connecting || d->m_state == Connected
|
||||||
|
|| d->m_state == ProcessRunning);
|
||||||
|
setState(Inactive);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshRemoteProcessRunner::handleProcessStarted()
|
||||||
|
{
|
||||||
|
QSSH_ASSERT_AND_RETURN(d->m_state == Connected);
|
||||||
|
|
||||||
|
setState(ProcessRunning);
|
||||||
|
emit processStarted();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshRemoteProcessRunner::handleProcessFinished(int exitStatus)
|
||||||
|
{
|
||||||
|
d->m_exitStatus = static_cast<SshRemoteProcess::ExitStatus>(exitStatus);
|
||||||
|
switch (d->m_exitStatus) {
|
||||||
|
case SshRemoteProcess::FailedToStart:
|
||||||
|
QSSH_ASSERT_AND_RETURN(d->m_state == Connected);
|
||||||
|
break;
|
||||||
|
case SshRemoteProcess::CrashExit:
|
||||||
|
QSSH_ASSERT_AND_RETURN(d->m_state == ProcessRunning);
|
||||||
|
d->m_exitSignal = d->m_process->exitSignal();
|
||||||
|
break;
|
||||||
|
case SshRemoteProcess::NormalExit:
|
||||||
|
QSSH_ASSERT_AND_RETURN(d->m_state == ProcessRunning);
|
||||||
|
d->m_exitCode = d->m_process->exitCode();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Q_ASSERT_X(false, Q_FUNC_INFO, "Impossible exit status.");
|
||||||
|
}
|
||||||
|
d->m_processErrorString = d->m_process->errorString();
|
||||||
|
setState(Inactive);
|
||||||
|
emit processClosed(exitStatus);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshRemoteProcessRunner::handleStdout()
|
||||||
|
{
|
||||||
|
d->m_stdout += d->m_process->readAllStandardOutput();
|
||||||
|
emit readyReadStandardOutput();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshRemoteProcessRunner::handleStderr()
|
||||||
|
{
|
||||||
|
d->m_stderr += d->m_process->readAllStandardError();
|
||||||
|
emit readyReadStandardError();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshRemoteProcessRunner::setState(int newState)
|
||||||
|
{
|
||||||
|
if (d->m_state == newState)
|
||||||
|
return;
|
||||||
|
|
||||||
|
d->m_state = static_cast<State>(newState);
|
||||||
|
if (d->m_state == Inactive) {
|
||||||
|
if (d->m_process) {
|
||||||
|
disconnect(d->m_process.data(), 0, this, 0);
|
||||||
|
d->m_process->close();
|
||||||
|
d->m_process.clear();
|
||||||
|
}
|
||||||
|
if (d->m_connection) {
|
||||||
|
disconnect(d->m_connection, 0, this, 0);
|
||||||
|
QSsh::releaseConnection(d->m_connection);
|
||||||
|
d->m_connection = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray SshRemoteProcessRunner::command() const { return d->m_command; }
|
||||||
|
SshError SshRemoteProcessRunner::lastConnectionError() const { return d->m_lastConnectionError; }
|
||||||
|
QString SshRemoteProcessRunner::lastConnectionErrorString() const {
|
||||||
|
return d->m_lastConnectionErrorString;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SshRemoteProcessRunner::isProcessRunning() const
|
||||||
|
{
|
||||||
|
return d->m_process && d->m_process->isRunning();
|
||||||
|
}
|
||||||
|
|
||||||
|
SshRemoteProcess::ExitStatus SshRemoteProcessRunner::processExitStatus() const
|
||||||
|
{
|
||||||
|
QSSH_ASSERT(!isProcessRunning());
|
||||||
|
return d->m_exitStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
SshRemoteProcess::Signal SshRemoteProcessRunner::processExitSignal() const
|
||||||
|
{
|
||||||
|
QSSH_ASSERT(processExitStatus() == SshRemoteProcess::CrashExit);
|
||||||
|
return d->m_exitSignal;
|
||||||
|
}
|
||||||
|
|
||||||
|
int SshRemoteProcessRunner::processExitCode() const
|
||||||
|
{
|
||||||
|
QSSH_ASSERT(processExitStatus() == SshRemoteProcess::NormalExit);
|
||||||
|
return d->m_exitCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString SshRemoteProcessRunner::processErrorString() const
|
||||||
|
{
|
||||||
|
return d->m_processErrorString;
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray SshRemoteProcessRunner::readAllStandardOutput()
|
||||||
|
{
|
||||||
|
const QByteArray data = d->m_stdout;
|
||||||
|
d->m_stdout.clear();
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray SshRemoteProcessRunner::readAllStandardError()
|
||||||
|
{
|
||||||
|
const QByteArray data = d->m_stderr;
|
||||||
|
d->m_stderr.clear();
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshRemoteProcessRunner::writeDataToProcess(const QByteArray &data)
|
||||||
|
{
|
||||||
|
QSSH_ASSERT(isProcessRunning());
|
||||||
|
d->m_process->write(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshRemoteProcessRunner::sendSignalToProcess(SshRemoteProcess::Signal signal)
|
||||||
|
{
|
||||||
|
QSSH_ASSERT(isProcessRunning());
|
||||||
|
d->m_process->sendSignal(signal);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshRemoteProcessRunner::cancel()
|
||||||
|
{
|
||||||
|
setState(Inactive);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace QSsh
|
||||||
82
client/3rd/QtSsh/src/ssh/sshremoteprocessrunner.h
Normal file
82
client/3rd/QtSsh/src/ssh/sshremoteprocessrunner.h
Normal file
|
|
@ -0,0 +1,82 @@
|
||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2016 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "sshconnection.h"
|
||||||
|
#include "sshremoteprocess.h"
|
||||||
|
|
||||||
|
namespace QSsh {
|
||||||
|
namespace Internal { class SshRemoteProcessRunnerPrivate; }
|
||||||
|
|
||||||
|
class QSSH_EXPORT SshRemoteProcessRunner : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
SshRemoteProcessRunner(QObject *parent = 0);
|
||||||
|
~SshRemoteProcessRunner();
|
||||||
|
|
||||||
|
void run(const QByteArray &command, const SshConnectionParameters &sshParams);
|
||||||
|
void runInTerminal(const QByteArray &command, const SshPseudoTerminal &terminal,
|
||||||
|
const SshConnectionParameters &sshParams);
|
||||||
|
QByteArray command() const;
|
||||||
|
|
||||||
|
QSsh::SshError lastConnectionError() const;
|
||||||
|
QString lastConnectionErrorString() const;
|
||||||
|
|
||||||
|
bool isProcessRunning() const;
|
||||||
|
void writeDataToProcess(const QByteArray &data);
|
||||||
|
void sendSignalToProcess(SshRemoteProcess::Signal signal); // No effect with OpenSSH server.
|
||||||
|
void cancel(); // Does not stop remote process, just frees SSH-related process resources.
|
||||||
|
SshRemoteProcess::ExitStatus processExitStatus() const;
|
||||||
|
SshRemoteProcess::Signal processExitSignal() const;
|
||||||
|
int processExitCode() const;
|
||||||
|
QString processErrorString() const;
|
||||||
|
QByteArray readAllStandardOutput();
|
||||||
|
QByteArray readAllStandardError();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void connectionError();
|
||||||
|
void processStarted();
|
||||||
|
void readyReadStandardOutput();
|
||||||
|
void readyReadStandardError();
|
||||||
|
void processClosed(int exitStatus); // values are of type SshRemoteProcess::ExitStatus
|
||||||
|
|
||||||
|
private:
|
||||||
|
void handleConnected();
|
||||||
|
void handleConnectionError(QSsh::SshError error);
|
||||||
|
void handleDisconnected();
|
||||||
|
void handleProcessStarted();
|
||||||
|
void handleProcessFinished(int exitStatus);
|
||||||
|
void handleStdout();
|
||||||
|
void handleStderr();
|
||||||
|
void runInternal(const QByteArray &command, const QSsh::SshConnectionParameters &sshParams);
|
||||||
|
void setState(int newState);
|
||||||
|
|
||||||
|
Internal::SshRemoteProcessRunnerPrivate * const d;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace QSsh
|
||||||
269
client/3rd/QtSsh/src/ssh/sshsendfacility.cpp
Normal file
269
client/3rd/QtSsh/src/ssh/sshsendfacility.cpp
Normal file
|
|
@ -0,0 +1,269 @@
|
||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2016 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#include "sshsendfacility_p.h"
|
||||||
|
|
||||||
|
#include "sshkeyexchange_p.h"
|
||||||
|
#include "sshlogging_p.h"
|
||||||
|
#include "sshoutgoingpacket_p.h"
|
||||||
|
|
||||||
|
#include <QTcpSocket>
|
||||||
|
|
||||||
|
namespace QSsh {
|
||||||
|
namespace Internal {
|
||||||
|
|
||||||
|
SshSendFacility::SshSendFacility(QTcpSocket *socket)
|
||||||
|
: m_clientSeqNr(0), m_socket(socket),
|
||||||
|
m_outgoingPacket(m_encrypter, m_clientSeqNr)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshSendFacility::sendPacket()
|
||||||
|
{
|
||||||
|
qCDebug(sshLog, "Sending packet, client seq nr is %u", m_clientSeqNr);
|
||||||
|
if (m_socket->isValid()
|
||||||
|
&& m_socket->state() == QAbstractSocket::ConnectedState) {
|
||||||
|
m_socket->write(m_outgoingPacket.rawData());
|
||||||
|
++m_clientSeqNr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshSendFacility::reset()
|
||||||
|
{
|
||||||
|
m_clientSeqNr = 0;
|
||||||
|
m_encrypter.clearKeys();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshSendFacility::recreateKeys(const SshKeyExchange &keyExchange)
|
||||||
|
{
|
||||||
|
m_encrypter.recreateKeys(keyExchange);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshSendFacility::createAuthenticationKey(const QByteArray &privKeyFileContents)
|
||||||
|
{
|
||||||
|
m_encrypter.createAuthenticationKey(privKeyFileContents);
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray SshSendFacility::sendKeyExchangeInitPacket()
|
||||||
|
{
|
||||||
|
const QByteArray &payLoad = m_outgoingPacket.generateKeyExchangeInitPacket();
|
||||||
|
sendPacket();
|
||||||
|
return payLoad;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshSendFacility::sendKeyDhInitPacket(const Botan::BigInt &e)
|
||||||
|
{
|
||||||
|
m_outgoingPacket.generateKeyDhInitPacket(e);
|
||||||
|
sendPacket();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshSendFacility::sendKeyEcdhInitPacket(const QByteArray &clientQ)
|
||||||
|
{
|
||||||
|
m_outgoingPacket.generateKeyEcdhInitPacket(clientQ);
|
||||||
|
sendPacket();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshSendFacility::sendNewKeysPacket()
|
||||||
|
{
|
||||||
|
m_outgoingPacket.generateNewKeysPacket();
|
||||||
|
sendPacket();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshSendFacility::sendDisconnectPacket(SshErrorCode reason,
|
||||||
|
const QByteArray &reasonString)
|
||||||
|
{
|
||||||
|
m_outgoingPacket.generateDisconnectPacket(reason, reasonString);
|
||||||
|
sendPacket();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshSendFacility::sendMsgUnimplementedPacket(quint32 serverSeqNr)
|
||||||
|
{
|
||||||
|
m_outgoingPacket.generateMsgUnimplementedPacket(serverSeqNr);
|
||||||
|
sendPacket();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshSendFacility::sendUserAuthServiceRequestPacket()
|
||||||
|
{
|
||||||
|
m_outgoingPacket.generateUserAuthServiceRequestPacket();
|
||||||
|
sendPacket();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshSendFacility::sendUserAuthByPasswordRequestPacket(const QByteArray &user,
|
||||||
|
const QByteArray &service, const QByteArray &pwd)
|
||||||
|
{
|
||||||
|
m_outgoingPacket.generateUserAuthByPasswordRequestPacket(user, service, pwd);
|
||||||
|
sendPacket();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshSendFacility::sendUserAuthByPublicKeyRequestPacket(const QByteArray &user,
|
||||||
|
const QByteArray &service)
|
||||||
|
{
|
||||||
|
m_outgoingPacket.generateUserAuthByPublicKeyRequestPacket(user, service);
|
||||||
|
sendPacket();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshSendFacility::sendUserAuthByKeyboardInteractiveRequestPacket(const QByteArray &user,
|
||||||
|
const QByteArray &service)
|
||||||
|
{
|
||||||
|
m_outgoingPacket.generateUserAuthByKeyboardInteractiveRequestPacket(user, service);
|
||||||
|
sendPacket();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshSendFacility::sendUserAuthInfoResponsePacket(const QStringList &responses)
|
||||||
|
{
|
||||||
|
m_outgoingPacket.generateUserAuthInfoResponsePacket(responses);
|
||||||
|
sendPacket();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshSendFacility::sendRequestFailurePacket()
|
||||||
|
{
|
||||||
|
m_outgoingPacket.generateRequestFailurePacket();
|
||||||
|
sendPacket();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshSendFacility::sendIgnorePacket()
|
||||||
|
{
|
||||||
|
m_outgoingPacket.generateIgnorePacket();
|
||||||
|
sendPacket();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshSendFacility::sendInvalidPacket()
|
||||||
|
{
|
||||||
|
m_outgoingPacket.generateInvalidMessagePacket();
|
||||||
|
sendPacket();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshSendFacility::sendSessionPacket(quint32 channelId, quint32 windowSize,
|
||||||
|
quint32 maxPacketSize)
|
||||||
|
{
|
||||||
|
m_outgoingPacket.generateSessionPacket(channelId, windowSize,
|
||||||
|
maxPacketSize);
|
||||||
|
sendPacket();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshSendFacility::sendDirectTcpIpPacket(quint32 channelId, quint32 windowSize,
|
||||||
|
quint32 maxPacketSize, const QByteArray &remoteHost, quint32 remotePort,
|
||||||
|
const QByteArray &localIpAddress, quint32 localPort)
|
||||||
|
{
|
||||||
|
m_outgoingPacket.generateDirectTcpIpPacket(channelId, windowSize, maxPacketSize, remoteHost,
|
||||||
|
remotePort, localIpAddress, localPort);
|
||||||
|
sendPacket();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshSendFacility::sendTcpIpForwardPacket(const QByteArray &bindAddress, quint32 bindPort)
|
||||||
|
{
|
||||||
|
m_outgoingPacket.generateTcpIpForwardPacket(bindAddress, bindPort);
|
||||||
|
sendPacket();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshSendFacility::sendCancelTcpIpForwardPacket(const QByteArray &bindAddress, quint32 bindPort)
|
||||||
|
{
|
||||||
|
m_outgoingPacket.generateCancelTcpIpForwardPacket(bindAddress, bindPort);
|
||||||
|
sendPacket();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshSendFacility::sendPtyRequestPacket(quint32 remoteChannel,
|
||||||
|
const SshPseudoTerminal &terminal)
|
||||||
|
{
|
||||||
|
m_outgoingPacket.generatePtyRequestPacket(remoteChannel, terminal);
|
||||||
|
sendPacket();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshSendFacility::sendEnvPacket(quint32 remoteChannel,
|
||||||
|
const QByteArray &var, const QByteArray &value)
|
||||||
|
{
|
||||||
|
m_outgoingPacket.generateEnvPacket(remoteChannel, var, value);
|
||||||
|
sendPacket();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshSendFacility::sendExecPacket(quint32 remoteChannel,
|
||||||
|
const QByteArray &command)
|
||||||
|
{
|
||||||
|
m_outgoingPacket.generateExecPacket(remoteChannel, command);
|
||||||
|
sendPacket();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshSendFacility::sendShellPacket(quint32 remoteChannel)
|
||||||
|
{
|
||||||
|
m_outgoingPacket.generateShellPacket(remoteChannel);
|
||||||
|
sendPacket();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshSendFacility::sendSftpPacket(quint32 remoteChannel)
|
||||||
|
{
|
||||||
|
m_outgoingPacket.generateSftpPacket(remoteChannel);
|
||||||
|
sendPacket();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshSendFacility::sendWindowAdjustPacket(quint32 remoteChannel,
|
||||||
|
quint32 bytesToAdd)
|
||||||
|
{
|
||||||
|
m_outgoingPacket.generateWindowAdjustPacket(remoteChannel, bytesToAdd);
|
||||||
|
sendPacket();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshSendFacility::sendChannelDataPacket(quint32 remoteChannel,
|
||||||
|
const QByteArray &data)
|
||||||
|
{
|
||||||
|
m_outgoingPacket.generateChannelDataPacket(remoteChannel, data);
|
||||||
|
sendPacket();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshSendFacility::sendChannelSignalPacket(quint32 remoteChannel,
|
||||||
|
const QByteArray &signalName)
|
||||||
|
{
|
||||||
|
m_outgoingPacket.generateChannelSignalPacket(remoteChannel, signalName);
|
||||||
|
sendPacket();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshSendFacility::sendChannelEofPacket(quint32 remoteChannel)
|
||||||
|
{
|
||||||
|
m_outgoingPacket.generateChannelEofPacket(remoteChannel);
|
||||||
|
sendPacket();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshSendFacility::sendChannelClosePacket(quint32 remoteChannel)
|
||||||
|
{
|
||||||
|
m_outgoingPacket.generateChannelClosePacket(remoteChannel);
|
||||||
|
sendPacket();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshSendFacility::sendChannelOpenConfirmationPacket(quint32 remoteChannel, quint32 localChannel,
|
||||||
|
quint32 localWindowSize, quint32 maxPacketSize)
|
||||||
|
{
|
||||||
|
m_outgoingPacket.generateChannelOpenConfirmationPacket(remoteChannel, localChannel,
|
||||||
|
localWindowSize, maxPacketSize);
|
||||||
|
sendPacket();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshSendFacility::sendChannelOpenFailurePacket(quint32 remoteChannel, quint32 reason,
|
||||||
|
const QByteArray &reasonString)
|
||||||
|
{
|
||||||
|
m_outgoingPacket.generateChannelOpenFailurePacket(remoteChannel, reason, reasonString);
|
||||||
|
sendPacket();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Internal
|
||||||
|
} // namespace QSsh
|
||||||
106
client/3rd/QtSsh/src/ssh/sshsendfacility_p.h
Normal file
106
client/3rd/QtSsh/src/ssh/sshsendfacility_p.h
Normal file
|
|
@ -0,0 +1,106 @@
|
||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2016 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "sshcryptofacility_p.h"
|
||||||
|
#include "sshoutgoingpacket_p.h"
|
||||||
|
|
||||||
|
#include <QStringList>
|
||||||
|
|
||||||
|
QT_BEGIN_NAMESPACE
|
||||||
|
class QTcpSocket;
|
||||||
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
|
|
||||||
|
namespace QSsh {
|
||||||
|
class SshPseudoTerminal;
|
||||||
|
|
||||||
|
namespace Internal {
|
||||||
|
class SshKeyExchange;
|
||||||
|
|
||||||
|
class SshSendFacility
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SshSendFacility(QTcpSocket *socket);
|
||||||
|
void reset();
|
||||||
|
void recreateKeys(const SshKeyExchange &keyExchange);
|
||||||
|
void createAuthenticationKey(const QByteArray &privKeyFileContents);
|
||||||
|
|
||||||
|
QByteArray sendKeyExchangeInitPacket();
|
||||||
|
void sendKeyDhInitPacket(const Botan::BigInt &e);
|
||||||
|
void sendKeyEcdhInitPacket(const QByteArray &clientQ);
|
||||||
|
void sendNewKeysPacket();
|
||||||
|
void sendDisconnectPacket(SshErrorCode reason,
|
||||||
|
const QByteArray &reasonString);
|
||||||
|
void sendMsgUnimplementedPacket(quint32 serverSeqNr);
|
||||||
|
void sendUserAuthServiceRequestPacket();
|
||||||
|
void sendUserAuthByPasswordRequestPacket(const QByteArray &user,
|
||||||
|
const QByteArray &service, const QByteArray &pwd);
|
||||||
|
void sendUserAuthByPublicKeyRequestPacket(const QByteArray &user,
|
||||||
|
const QByteArray &service);
|
||||||
|
void sendUserAuthByKeyboardInteractiveRequestPacket(const QByteArray &user,
|
||||||
|
const QByteArray &service);
|
||||||
|
void sendUserAuthInfoResponsePacket(const QStringList &responses);
|
||||||
|
void sendRequestFailurePacket();
|
||||||
|
void sendIgnorePacket();
|
||||||
|
void sendInvalidPacket();
|
||||||
|
void sendSessionPacket(quint32 channelId, quint32 windowSize,
|
||||||
|
quint32 maxPacketSize);
|
||||||
|
void sendDirectTcpIpPacket(quint32 channelId, quint32 windowSize, quint32 maxPacketSize,
|
||||||
|
const QByteArray &remoteHost, quint32 remotePort, const QByteArray &localIpAddress,
|
||||||
|
quint32 localPort);
|
||||||
|
void sendTcpIpForwardPacket(const QByteArray &bindAddress, quint32 bindPort);
|
||||||
|
void sendCancelTcpIpForwardPacket(const QByteArray &bindAddress, quint32 bindPort);
|
||||||
|
void sendPtyRequestPacket(quint32 remoteChannel,
|
||||||
|
const SshPseudoTerminal &terminal);
|
||||||
|
void sendEnvPacket(quint32 remoteChannel, const QByteArray &var,
|
||||||
|
const QByteArray &value);
|
||||||
|
void sendExecPacket(quint32 remoteChannel, const QByteArray &command);
|
||||||
|
void sendShellPacket(quint32 remoteChannel);
|
||||||
|
void sendSftpPacket(quint32 remoteChannel);
|
||||||
|
void sendWindowAdjustPacket(quint32 remoteChannel, quint32 bytesToAdd);
|
||||||
|
void sendChannelDataPacket(quint32 remoteChannel, const QByteArray &data);
|
||||||
|
void sendChannelSignalPacket(quint32 remoteChannel,
|
||||||
|
const QByteArray &signalName);
|
||||||
|
void sendChannelEofPacket(quint32 remoteChannel);
|
||||||
|
void sendChannelClosePacket(quint32 remoteChannel);
|
||||||
|
void sendChannelOpenConfirmationPacket(quint32 remoteChannel, quint32 localChannel,
|
||||||
|
quint32 localWindowSize, quint32 maxPackeSize);
|
||||||
|
void sendChannelOpenFailurePacket(quint32 remoteChannel, quint32 reason,
|
||||||
|
const QByteArray &reasonString);
|
||||||
|
quint32 nextClientSeqNr() const { return m_clientSeqNr; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
void sendPacket();
|
||||||
|
|
||||||
|
quint32 m_clientSeqNr;
|
||||||
|
SshEncryptionFacility m_encrypter;
|
||||||
|
QTcpSocket *m_socket;
|
||||||
|
SshOutgoingPacket m_outgoingPacket;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Internal
|
||||||
|
} // namespace QSsh
|
||||||
136
client/3rd/QtSsh/src/ssh/sshtcpipforwardserver.cpp
Normal file
136
client/3rd/QtSsh/src/ssh/sshtcpipforwardserver.cpp
Normal file
|
|
@ -0,0 +1,136 @@
|
||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2016 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#include "sshtcpipforwardserver.h"
|
||||||
|
#include "sshtcpipforwardserver_p.h"
|
||||||
|
|
||||||
|
#include "sshlogging_p.h"
|
||||||
|
#include "sshsendfacility_p.h"
|
||||||
|
|
||||||
|
namespace QSsh {
|
||||||
|
namespace Internal {
|
||||||
|
|
||||||
|
SshTcpIpForwardServerPrivate::SshTcpIpForwardServerPrivate(const QString &bindAddress,
|
||||||
|
quint16 bindPort, SshSendFacility &sendFacility)
|
||||||
|
: m_sendFacility(sendFacility),
|
||||||
|
m_bindAddress(bindAddress),
|
||||||
|
m_bindPort(bindPort),
|
||||||
|
m_state(SshTcpIpForwardServer::Inactive)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Internal
|
||||||
|
|
||||||
|
using namespace Internal;
|
||||||
|
|
||||||
|
SshTcpIpForwardServer::SshTcpIpForwardServer(const QString &bindAddress, quint16 bindPort,
|
||||||
|
SshSendFacility &sendFacility)
|
||||||
|
: d(new SshTcpIpForwardServerPrivate(bindAddress, bindPort, sendFacility))
|
||||||
|
{
|
||||||
|
connect(&d->m_timeoutTimer, &QTimer::timeout, this, &SshTcpIpForwardServer::setClosed);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshTcpIpForwardServer::setListening(quint16 port)
|
||||||
|
{
|
||||||
|
QSSH_ASSERT_AND_RETURN(d->m_state != Listening);
|
||||||
|
d->m_bindPort = port;
|
||||||
|
d->m_state = Listening;
|
||||||
|
emit stateChanged(Listening);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshTcpIpForwardServer::setClosed()
|
||||||
|
{
|
||||||
|
QSSH_ASSERT_AND_RETURN(d->m_state != Inactive);
|
||||||
|
d->m_state = Inactive;
|
||||||
|
emit stateChanged(Inactive);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshTcpIpForwardServer::setNewConnection(const SshForwardedTcpIpTunnel::Ptr &connection)
|
||||||
|
{
|
||||||
|
d->m_pendingConnections.append(connection);
|
||||||
|
emit newConnection();
|
||||||
|
}
|
||||||
|
|
||||||
|
SshTcpIpForwardServer::~SshTcpIpForwardServer()
|
||||||
|
{
|
||||||
|
delete d;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshTcpIpForwardServer::initialize()
|
||||||
|
{
|
||||||
|
if (d->m_state == Inactive || d->m_state == Closing) {
|
||||||
|
try {
|
||||||
|
d->m_state = Initializing;
|
||||||
|
emit stateChanged(Initializing);
|
||||||
|
d->m_sendFacility.sendTcpIpForwardPacket(d->m_bindAddress.toUtf8(), d->m_bindPort);
|
||||||
|
d->m_timeoutTimer.start(d->ReplyTimeout);
|
||||||
|
} catch (const std::exception &e) {
|
||||||
|
qCWarning(sshLog, "Botan error: %s", e.what());
|
||||||
|
d->m_timeoutTimer.stop();
|
||||||
|
setClosed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshTcpIpForwardServer::close()
|
||||||
|
{
|
||||||
|
d->m_timeoutTimer.stop();
|
||||||
|
|
||||||
|
if (d->m_state == Initializing || d->m_state == Listening) {
|
||||||
|
try {
|
||||||
|
d->m_state = Closing;
|
||||||
|
emit stateChanged(Closing);
|
||||||
|
d->m_sendFacility.sendCancelTcpIpForwardPacket(d->m_bindAddress.toUtf8(),
|
||||||
|
d->m_bindPort);
|
||||||
|
d->m_timeoutTimer.start(d->ReplyTimeout);
|
||||||
|
} catch (const std::exception &e) {
|
||||||
|
qCWarning(sshLog, "Botan error: %s", e.what());
|
||||||
|
d->m_timeoutTimer.stop();
|
||||||
|
setClosed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString &SshTcpIpForwardServer::bindAddress() const
|
||||||
|
{
|
||||||
|
return d->m_bindAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
quint16 SshTcpIpForwardServer::port() const
|
||||||
|
{
|
||||||
|
return d->m_bindPort;
|
||||||
|
}
|
||||||
|
|
||||||
|
SshTcpIpForwardServer::State SshTcpIpForwardServer::state() const
|
||||||
|
{
|
||||||
|
return d->m_state;
|
||||||
|
}
|
||||||
|
|
||||||
|
SshForwardedTcpIpTunnel::Ptr SshTcpIpForwardServer::nextPendingConnection()
|
||||||
|
{
|
||||||
|
return d->m_pendingConnections.takeFirst();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace QSsh
|
||||||
81
client/3rd/QtSsh/src/ssh/sshtcpipforwardserver.h
Normal file
81
client/3rd/QtSsh/src/ssh/sshtcpipforwardserver.h
Normal file
|
|
@ -0,0 +1,81 @@
|
||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2016 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "ssh_global.h"
|
||||||
|
#include "sshforwardedtcpiptunnel.h"
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
namespace QSsh {
|
||||||
|
|
||||||
|
namespace Internal {
|
||||||
|
class SshChannelManager;
|
||||||
|
class SshTcpIpForwardServerPrivate;
|
||||||
|
class SshSendFacility;
|
||||||
|
class SshConnectionPrivate;
|
||||||
|
} // namespace Internal
|
||||||
|
|
||||||
|
class QSSH_EXPORT SshTcpIpForwardServer : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
friend class Internal::SshChannelManager;
|
||||||
|
friend class Internal::SshConnectionPrivate;
|
||||||
|
|
||||||
|
public:
|
||||||
|
enum State {
|
||||||
|
Inactive,
|
||||||
|
Initializing,
|
||||||
|
Listening,
|
||||||
|
Closing
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef QSharedPointer<SshTcpIpForwardServer> Ptr;
|
||||||
|
~SshTcpIpForwardServer();
|
||||||
|
|
||||||
|
const QString &bindAddress() const;
|
||||||
|
quint16 port() const;
|
||||||
|
State state() const;
|
||||||
|
void initialize();
|
||||||
|
void close();
|
||||||
|
|
||||||
|
SshForwardedTcpIpTunnel::Ptr nextPendingConnection();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void error(const QString &reason);
|
||||||
|
void newConnection();
|
||||||
|
void stateChanged(State state);
|
||||||
|
|
||||||
|
private:
|
||||||
|
SshTcpIpForwardServer(const QString &bindAddress, quint16 bindPort,
|
||||||
|
Internal::SshSendFacility &sendFacility);
|
||||||
|
void setListening(quint16 port);
|
||||||
|
void setClosed();
|
||||||
|
void setNewConnection(const SshForwardedTcpIpTunnel::Ptr &connection);
|
||||||
|
|
||||||
|
Internal::SshTcpIpForwardServerPrivate * const d;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace QSsh
|
||||||
54
client/3rd/QtSsh/src/ssh/sshtcpipforwardserver_p.h
Normal file
54
client/3rd/QtSsh/src/ssh/sshtcpipforwardserver_p.h
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2016 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "sshtcpipforwardserver.h"
|
||||||
|
#include <QList>
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
|
namespace QSsh {
|
||||||
|
namespace Internal {
|
||||||
|
|
||||||
|
class SshTcpIpForwardServerPrivate
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static const int ReplyTimeout = 10000; // milli seconds
|
||||||
|
|
||||||
|
SshTcpIpForwardServerPrivate(const QString &bindAddress, quint16 bindPort,
|
||||||
|
SshSendFacility &sendFacility);
|
||||||
|
|
||||||
|
SshSendFacility &m_sendFacility;
|
||||||
|
QTimer m_timeoutTimer;
|
||||||
|
|
||||||
|
const QString m_bindAddress;
|
||||||
|
quint16 m_bindPort;
|
||||||
|
SshTcpIpForwardServer::State m_state;
|
||||||
|
|
||||||
|
QList<SshForwardedTcpIpTunnel::Ptr> m_pendingConnections;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Internal
|
||||||
|
} // namespace QSsh
|
||||||
123
client/3rd/QtSsh/src/ssh/sshtcpiptunnel.cpp
Normal file
123
client/3rd/QtSsh/src/ssh/sshtcpiptunnel.cpp
Normal file
|
|
@ -0,0 +1,123 @@
|
||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2016 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#include "sshsendfacility_p.h"
|
||||||
|
#include "sshtcpiptunnel_p.h"
|
||||||
|
|
||||||
|
#include "sshincomingpacket_p.h"
|
||||||
|
#include "sshexception_p.h"
|
||||||
|
#include "sshlogging_p.h"
|
||||||
|
|
||||||
|
namespace QSsh {
|
||||||
|
|
||||||
|
namespace Internal {
|
||||||
|
SshTcpIpTunnelPrivate::SshTcpIpTunnelPrivate(quint32 channelId, SshSendFacility &sendFacility)
|
||||||
|
: AbstractSshChannel(channelId, sendFacility)
|
||||||
|
{
|
||||||
|
connect(this, &AbstractSshChannel::eof, this, &SshTcpIpTunnelPrivate::handleEof);
|
||||||
|
}
|
||||||
|
|
||||||
|
SshTcpIpTunnelPrivate::~SshTcpIpTunnelPrivate()
|
||||||
|
{
|
||||||
|
closeChannel();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void SshTcpIpTunnelPrivate::handleChannelSuccess()
|
||||||
|
{
|
||||||
|
throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
|
||||||
|
"Unexpected SSH_MSG_CHANNEL_SUCCESS message.");
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshTcpIpTunnelPrivate::handleChannelFailure()
|
||||||
|
{
|
||||||
|
throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
|
||||||
|
"Unexpected SSH_MSG_CHANNEL_FAILURE message.");
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshTcpIpTunnelPrivate::handleOpenFailureInternal(const QString &reason)
|
||||||
|
{
|
||||||
|
emit error(reason);
|
||||||
|
closeChannel();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshTcpIpTunnelPrivate::handleChannelDataInternal(const QByteArray &data)
|
||||||
|
{
|
||||||
|
m_data += data;
|
||||||
|
emit readyRead();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshTcpIpTunnelPrivate::handleChannelExtendedDataInternal(quint32 type,
|
||||||
|
const QByteArray &data)
|
||||||
|
{
|
||||||
|
qCWarning(sshLog, "%s: Unexpected extended channel data. Type is %u, content is '%s'.",
|
||||||
|
Q_FUNC_INFO, type, data.constData());
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshTcpIpTunnelPrivate::handleExitStatus(const SshChannelExitStatus &exitStatus)
|
||||||
|
{
|
||||||
|
qCWarning(sshLog, "%s: Unexpected exit status %d.", Q_FUNC_INFO, exitStatus.exitStatus);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshTcpIpTunnelPrivate::handleExitSignal(const SshChannelExitSignal &signal)
|
||||||
|
{
|
||||||
|
qCWarning(sshLog, "%s: Unexpected exit signal %s.", Q_FUNC_INFO, signal.signal.constData());
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshTcpIpTunnelPrivate::closeHook()
|
||||||
|
{
|
||||||
|
emit closed();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshTcpIpTunnelPrivate::handleEof()
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* For some reason, the OpenSSH server only sends EOF when the remote port goes away,
|
||||||
|
* but does not close the channel, even though it becomes useless in that case.
|
||||||
|
* So we close it ourselves.
|
||||||
|
*/
|
||||||
|
closeChannel();
|
||||||
|
}
|
||||||
|
|
||||||
|
qint64 SshTcpIpTunnelPrivate::readData(char *data, qint64 maxlen)
|
||||||
|
{
|
||||||
|
const qint64 bytesRead = qMin(qint64(m_data.count()), maxlen);
|
||||||
|
memcpy(data, m_data.constData(), bytesRead);
|
||||||
|
m_data.remove(0, bytesRead);
|
||||||
|
return bytesRead;
|
||||||
|
}
|
||||||
|
|
||||||
|
qint64 SshTcpIpTunnelPrivate::writeData(const char *data, qint64 len)
|
||||||
|
{
|
||||||
|
QSSH_ASSERT_AND_RETURN_VALUE(channelState() == AbstractSshChannel::SessionEstablished, 0);
|
||||||
|
|
||||||
|
sendData(QByteArray(data, len));
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Internal
|
||||||
|
|
||||||
|
} // namespace QSsh
|
||||||
82
client/3rd/QtSsh/src/ssh/sshtcpiptunnel_p.h
Normal file
82
client/3rd/QtSsh/src/ssh/sshtcpiptunnel_p.h
Normal file
|
|
@ -0,0 +1,82 @@
|
||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2016 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "sshchannel_p.h"
|
||||||
|
#include <QIODevice>
|
||||||
|
#include <QByteArray>
|
||||||
|
|
||||||
|
namespace QSsh {
|
||||||
|
namespace Internal {
|
||||||
|
|
||||||
|
class SshTcpIpTunnelPrivate : public AbstractSshChannel
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
SshTcpIpTunnelPrivate(quint32 channelId, SshSendFacility &sendFacility);
|
||||||
|
~SshTcpIpTunnelPrivate();
|
||||||
|
|
||||||
|
template<class SshTcpIpTunnel>
|
||||||
|
void init(SshTcpIpTunnel *q)
|
||||||
|
{
|
||||||
|
connect(this, &SshTcpIpTunnelPrivate::closed,
|
||||||
|
q, &SshTcpIpTunnel::close, Qt::QueuedConnection);
|
||||||
|
connect(this, &SshTcpIpTunnelPrivate::readyRead,
|
||||||
|
q, &SshTcpIpTunnel::readyRead, Qt::QueuedConnection);
|
||||||
|
connect(this, &SshTcpIpTunnelPrivate::error, q, [q](const QString &reason) {
|
||||||
|
q->setErrorString(reason);
|
||||||
|
emit q->error(reason);
|
||||||
|
}, Qt::QueuedConnection);
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleChannelSuccess() override;
|
||||||
|
void handleChannelFailure() override;
|
||||||
|
|
||||||
|
qint64 readData(char *data, qint64 maxlen);
|
||||||
|
qint64 writeData(const char *data, qint64 len);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void readyRead();
|
||||||
|
void error(const QString &reason);
|
||||||
|
void closed();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void handleOpenFailureInternal(const QString &reason) override;
|
||||||
|
void handleChannelDataInternal(const QByteArray &data) override;
|
||||||
|
void handleChannelExtendedDataInternal(quint32 type, const QByteArray &data) override;
|
||||||
|
void handleExitStatus(const SshChannelExitStatus &exitStatus) override;
|
||||||
|
void handleExitSignal(const SshChannelExitSignal &signal) override;
|
||||||
|
void closeHook() override;
|
||||||
|
|
||||||
|
QByteArray m_data;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void handleEof();
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Internal
|
||||||
|
} // namespace QSsh
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue