From afd34f2893a06a3aecf17e8e83b1df6ed2ae91a2 Mon Sep 17 00:00:00 2001 From: iroul Date: Fri, 4 Apr 2014 07:35:14 -0700 Subject: move to my github. --- src/protocol/fqterm_ssh_socket.cpp | 352 +++++++++++++++++++++++++++++++++++++ 1 file changed, 352 insertions(+) create mode 100644 src/protocol/fqterm_ssh_socket.cpp (limited to 'src/protocol/fqterm_ssh_socket.cpp') diff --git a/src/protocol/fqterm_ssh_socket.cpp b/src/protocol/fqterm_ssh_socket.cpp new file mode 100644 index 0000000..4c1e77a --- /dev/null +++ b/src/protocol/fqterm_ssh_socket.cpp @@ -0,0 +1,352 @@ +/*************************************************************************** + * fqterm, a terminal emulator for both BBS and *nix. * + * Copyright (C) 2008 fqterm development group. * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * + ***************************************************************************/ + +#include "fqterm_ssh_socket.h" +#include "fqterm_ssh_packet.h" +#include "fqterm_ssh1_packet.h" +#include "fqterm_ssh2_packet.h" +#include "fqterm_ssh_buffer.h" +#include "fqterm_ssh_rsa.h" +#include "fqterm_ssh_kex.h" +#include "fqterm_ssh2_kex.h" +#include "fqterm_ssh_const.h" +#include "fqterm_ssh_auth.h" +#include "fqterm_ssh_channel.h" +#include "fqterm_trace.h" +#include +namespace FQTerm { + +#define V1STR "SSH-1.5-FQTermSSH\n" +#define V2STR "SSH-2.0-FQTermSSH\n" +#define SSH_V1_C "SSH-1.5-FQTermSSH" +#define SSH_V2_C "SSH-2.0-FQTermSSH" + +FQTermSSHSocket::FQTermSSHSocket(int col, int row, const QString& termtype, const char *sshuser, const char *sshpasswd) + : termtype_(termtype) { + col_ = col; + row_ = row; + init_user_ = sshuser; + init_passwd_ = sshpasswd; + + private_socket_ = new FQTermSocketPrivate(); + + input_buffer_ = NULL; + output_buffer_ = NULL; + socket_buffer_ = NULL; + packet_receiver_ = NULL; + packet_sender_ = NULL; + key_exchanger_ = NULL; + authentication_ = NULL; + ssh_channel_ = NULL; + ssh_version_ = 1; + is_channel_ok_ = false; + auth_ok_emitted_ = false; + + FQ_VERIFY(connect(private_socket_, SIGNAL(hostFound()), this, SIGNAL(hostFound()))); + FQ_VERIFY(connect(private_socket_, SIGNAL(connected()), this, SIGNAL(connected()))); + FQ_VERIFY(connect(private_socket_, SIGNAL(connectionClosed()), this, SIGNAL(connectionClosed()))); + FQ_VERIFY(connect(private_socket_, SIGNAL(delayedCloseFinished()), this, SIGNAL(delayedCloseFinished()))); + FQ_VERIFY(connect(private_socket_, SIGNAL(error(QAbstractSocket::SocketError )), this, SIGNAL(error(QAbstractSocket::SocketError )))); + FQ_VERIFY(connect(private_socket_, SIGNAL(socketState(int)), this, SIGNAL(socketState(int)))); + + FQ_VERIFY(connect(private_socket_, SIGNAL(readyRead()), this, SLOT(socketReadyRead()))); +} + +FQTermSSHSocket::~FQTermSSHSocket() { + delete private_socket_; + delete input_buffer_; + delete output_buffer_; + delete socket_buffer_; + delete packet_receiver_; + delete packet_sender_; + delete key_exchanger_; + delete authentication_; + delete ssh_channel_; +} + +void FQTermSSHSocket::init(int ssh_version) { + // Actually we could reuse these buffers, sender/receivers, and etc. + // but in that case reset methods should be added to all these classes. + // Guys lazy as me won't do that. + + delete input_buffer_; + delete output_buffer_; + delete socket_buffer_; + delete packet_receiver_; + delete packet_sender_; + delete key_exchanger_; + delete authentication_; + delete ssh_channel_; + + is_channel_ok_ = false; + auth_ok_emitted_ = false; + + if (ssh_version == 1) { + input_buffer_ = new FQTermSSHBuffer(1024); + output_buffer_ = new FQTermSSHBuffer(1024); + socket_buffer_ = new FQTermSSHBuffer(1024); + packet_receiver_ = new FQTermSSH1PacketReceiver; + packet_sender_ = new FQTermSSH1PacketSender; + key_exchanger_ = new FQTermSSH1Kex(SSH_V1_C, server_name_.toAscii().constData()); + authentication_ = new FQTermSSH1PasswdAuth(init_user_, init_passwd_); + ssh_channel_ = new FQTermSSH1Channel; + + FQ_VERIFY(connect(packet_receiver_, SIGNAL(packetAvaliable(int)), this, SLOT(handlePacket(int)))); + FQ_VERIFY(connect(packet_receiver_, SIGNAL(packetError(QString)), this, SLOT(handleError(QString)))); + + FQ_VERIFY(connect(packet_sender_, SIGNAL(dataToWrite()), this, SLOT(writeData()))); + + FQ_VERIFY(connect(key_exchanger_, SIGNAL(kexOK()), this, SLOT(kexOK()))); + FQ_VERIFY(connect(key_exchanger_, SIGNAL(kexError(QString)), this, SLOT(handleError(QString)))); + FQ_VERIFY(connect(key_exchanger_, SIGNAL(reKex()), packet_receiver_, SLOT(resetEncryption()))); + FQ_VERIFY(connect(key_exchanger_, SIGNAL(reKex()), packet_sender_, SLOT(resetEncryption()))); + FQ_VERIFY(connect(key_exchanger_, SIGNAL(startEncryption(const u_char*)), packet_receiver_, SLOT(startEncryption(const u_char*)))); + FQ_VERIFY(connect(key_exchanger_, SIGNAL(startEncryption(const u_char*)), packet_sender_, SLOT(startEncryption(const u_char*)))); + + FQ_VERIFY(connect(authentication_, SIGNAL(requestUserPwd(QString *, QString *, bool *)), this, SIGNAL(requestUserPwd(QString *, QString *, bool *)))); + FQ_VERIFY(connect(authentication_, SIGNAL(authOK()), this, SLOT(authOK()))); + FQ_VERIFY(connect(authentication_, SIGNAL(authError(QString)), this, SLOT(handleError(QString)))); + + FQ_VERIFY(connect(ssh_channel_, SIGNAL(channelOK()), this, SLOT(channelOK()))); + FQ_VERIFY(connect(ssh_channel_, SIGNAL(channelReadyRead(const char *, int)), this, SLOT(channelReadyRead(const char *, int)))); + + key_exchanger_->initKex(packet_receiver_, packet_sender_); + } else { + input_buffer_ = new FQTermSSHBuffer(1024); + output_buffer_ = new FQTermSSHBuffer(1024); + socket_buffer_ = new FQTermSSHBuffer(1024); + packet_receiver_ = new FQTermSSH2PacketReceiver; + packet_sender_ = new FQTermSSH2PacketSender; + key_exchanger_ = new FQTermSSH2Kex(SSH_V2_C, server_name_.toAscii().constData()); + authentication_ = new FQTermSSH2PasswdAuth(init_user_, init_passwd_); + ssh_channel_ = new FQTermSSH2Channel; + + FQ_VERIFY(connect(packet_receiver_, SIGNAL(packetAvaliable(int)), this, SLOT(handlePacket(int)))); + FQ_VERIFY(connect(packet_receiver_, SIGNAL(packetError(QString)), this, SLOT(handleError(QString)))); + + FQ_VERIFY(connect(packet_sender_, SIGNAL(dataToWrite()), this, SLOT(writeData()))); + + FQ_VERIFY(connect(key_exchanger_, SIGNAL(kexOK()), this, SLOT(kexOK()))); + FQ_VERIFY(connect(key_exchanger_, SIGNAL(kexError(QString)), this, SLOT(handleError(QString)))); + FQ_VERIFY(connect(key_exchanger_, SIGNAL(reKex()), packet_receiver_, SLOT(resetEncryption()))); + FQ_VERIFY(connect(key_exchanger_, SIGNAL(reKex()), packet_sender_, SLOT(resetEncryption()))); + + FQ_VERIFY(connect(key_exchanger_, SIGNAL(startEncryption(const u_char*)), packet_receiver_, SLOT(startEncryption(const u_char*)))); + FQ_VERIFY(connect(key_exchanger_, SIGNAL(startEncryption(const u_char*)), packet_sender_, SLOT(startEncryption(const u_char*)))); + + FQ_VERIFY(connect(authentication_, SIGNAL(requestUserPwd(QString *, QString *, bool *)), this, SIGNAL(requestUserPwd(QString *, QString *, bool *)))); + FQ_VERIFY(connect(authentication_, SIGNAL(authOK()), this, SLOT(authOK()))); + FQ_VERIFY(connect(authentication_, SIGNAL(authError(QString)), this, SLOT(handleError(QString)))); + + FQ_VERIFY(connect(ssh_channel_, SIGNAL(channelOK()), this, SLOT(channelOK()))); + FQ_VERIFY(connect(ssh_channel_, SIGNAL(channelReadyRead(const char *, int)), this, SLOT(channelReadyRead(const char *, int)))); + FQ_VERIFY(connect(ssh_channel_, SIGNAL(channelError(QString)), this, SLOT(handleError(QString)))); + + key_exchanger_->initKex(packet_receiver_, packet_sender_); + } +} + +void FQTermSSHSocket::kexOK() { + FQ_TRACE("sshsocket", 3) << "Key exchange completed!"; + authentication_->initAuth(packet_receiver_, packet_sender_); +} + +void FQTermSSHSocket::authOK() { + FQ_TRACE("sshsocket", 3) << "Auth completed!"; + ssh_channel_->initChannel(packet_receiver_, packet_sender_, col_, row_, termtype_); +} + +void FQTermSSHSocket::channelOK() { + FQ_TRACE("sshsocket", 3) << "Channel established!"; + is_channel_ok_ = true; + //auth_ok_emitted_ = false; +} + +void FQTermSSHSocket::channelReadyRead(const char *data, int len) { + input_buffer_->putRawData(data, len); + emit readyRead(); +} + +unsigned long FQTermSSHSocket::socketWriteBlock(const char *data, unsigned long len) { + QByteArray to_write(data, len); + return private_socket_->writeBlock(to_write); +} + +void FQTermSSHSocket::socketReadyRead() { + if (!auth_ok_emitted_ && is_channel_ok_) { + auth_ok_emitted_ = true; + emit sshAuthOK(); + } + unsigned long size; + + switch (ssh_socket_state_) { + case BeforeSession: + { + QByteArray str; + int version; + size = private_socket_->bytesAvailable(); + str = private_socket_->readBlock(size); + + server_name_ = QString(str).trimmed(); // remove the newline + version = chooseVersion(str.data()); + + FQ_TRACE("sshsocket", 3) << "SSH server: " << server_name_; + FQ_TRACE("sshsocket", 3) << "SSH version chosen: " << version; + + if (version == 1) { + init(1); + ssh_version_ = 1; + socketWriteBlock(V1STR, strlen(V1STR)); + } else if (version == 2) { + init(2); + ssh_version_ = 2; + socketWriteBlock(V2STR, strlen(V2STR)); + } else { + handleError(tr("Unknown SSH version. " + "Check if you set the right server and port.")); + return ; + } + ssh_socket_state_ = SockSession; + private_socket_->flush(); + break; + } + case SockSession: + parsePacket(); + } +} + +void FQTermSSHSocket::parsePacket() { + unsigned long size; + QByteArray data; + size = private_socket_->bytesAvailable(); + data = private_socket_->readBlock(size); + + socket_buffer_->putRawData(data.data(), size); + packet_receiver_->parseData(socket_buffer_); +} + +int FQTermSSHSocket::chooseVersion(const QString &ver) { + QString verion = ver.mid(ver.indexOf("-") + 1); + verion = verion.left(verion.indexOf("-")); + + if (verion == "1.99" || verion == "1.3" || verion == "1.5") { + return 1; + } else if (verion == "2.0") { + return 2; + } else { + return - 1; + } +} + +void FQTermSSHSocket::connectToHost(const QString &host_name, quint16 port) { + ssh_socket_state_ = BeforeSession; + private_socket_->connectToHost(host_name, port); +} + +void FQTermSSHSocket::writeData() { + socketWriteBlock((const char*)packet_sender_->output_buffer_->data(), + packet_sender_->output_buffer_->len()); + private_socket_->flush(); +} + +void FQTermSSHSocket::handlePacket(int type) { + if (ssh_version_ == 1) { + switch (type) { + case SSH1_MSG_DISCONNECT: + char *reason; + reason = (char*)packet_receiver_->getString(); + FQ_TRACE("sshsocket", 1) << "Disconnect because: " << reason; + delete [] reason; + break; + default: + return ; + } + } else { + // ssh_version_ == 2; + switch (type) { + case SSH2_MSG_DISCONNECT: + char *reason; + packet_receiver_->consume(4); + reason = (char*)packet_receiver_->getString(); + FQ_TRACE("sshsocket", 1) << "Disconnect because: " << reason; + delete[] reason; + break; + default: + return ; + } + } +} + +unsigned long FQTermSSHSocket::bytesAvailable() { + return input_buffer_->len(); +} + +QByteArray FQTermSSHSocket::readBlock(unsigned long size) { + QByteArray data(size, 0); + input_buffer_->getRawData(data.data(), size); + return data; +} + +long FQTermSSHSocket::writeBlock(const QByteArray &data) { + if (!is_channel_ok_) return 0; + + unsigned long size = data.size(); + output_buffer_->putRawData(data.data(), size); + return size; +} + +void FQTermSSHSocket::flush() { + if (!is_channel_ok_) return; + + int size = output_buffer_->len(); + + ssh_channel_->sendData((const char *)output_buffer_->data(), size); + + output_buffer_->consume(size); +} + +void FQTermSSHSocket::close() { + private_socket_->close(); +} + +void FQTermSSHSocket::handleError(QString reason) { + close(); + + emit errorMessage(reason); + emit connectionClosed(); +} + +void FQTermSSHSocket::setProxy(int nProxyType, bool bAuth, + const QString &strProxyHost, + quint16 uProxyPort, + const QString &strProxyUsr, + const QString &strProxyPwd) { + private_socket_->setProxy(nProxyType, bAuth, strProxyHost, + uProxyPort, strProxyUsr, strProxyPwd); +} + +bool FQTermSSHSocket::setTermSize(int col, int row) { + if (ssh_channel_ && is_channel_ok_) + ssh_channel_->changeTermSize(col, row); + return true; +} +} // namespace FQTerm + +#include "fqterm_ssh_socket.moc" -- cgit v1.2.3