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
|
||||
macOSPackage/
|
||||
|
||||
# C++ objects and libs
|
||||
*.slo
|
||||
*.lo
|
||||
*.o
|
||||
*.a
|
||||
*.la
|
||||
*.lai
|
||||
*.so
|
||||
*.dll
|
||||
*.dylib
|
||||
AmneziaVPN.dmg
|
||||
AmneziaVPN.exe
|
||||
|
||||
# Qt-es
|
||||
/.qmake.cache
|
||||
|
|
@ -47,4 +38,5 @@ CMakeLists.txt.user*
|
|||
*.*~
|
||||
|
||||
# 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