summaryrefslogtreecommitdiff
path: root/src/protocol/fqterm_ssh_socket.cpp
diff options
context:
space:
mode:
authoriroul <iroul@iroul-VirtualBox.(none)>2014-04-04 07:35:14 -0700
committeriroul <iroul@iroul-VirtualBox.(none)>2014-04-04 07:35:14 -0700
commitafd34f2893a06a3aecf17e8e83b1df6ed2ae91a2 (patch)
tree851102abc55d91a1b76e63e9e89f9a7733da95b5 /src/protocol/fqterm_ssh_socket.cpp
parentc4b028ad53f7b362a864de24828d7cc39ff67b0a (diff)
downloadfqterm-afd34f2893a06a3aecf17e8e83b1df6ed2ae91a2.tar.xz
move to my github.
Diffstat (limited to 'src/protocol/fqterm_ssh_socket.cpp')
-rw-r--r--src/protocol/fqterm_ssh_socket.cpp352
1 files changed, 352 insertions, 0 deletions
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 <QString>
+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"