summaryrefslogtreecommitdiff
path: root/src/protocol
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
parentc4b028ad53f7b362a864de24828d7cc39ff67b0a (diff)
downloadfqterm-afd34f2893a06a3aecf17e8e83b1df6ed2ae91a2.tar.xz
move to my github.
Diffstat (limited to 'src/protocol')
-rw-r--r--src/protocol/CMakeLists.txt65
-rw-r--r--src/protocol/fqterm_socket.cpp672
-rw-r--r--src/protocol/fqterm_socket.h196
-rw-r--r--src/protocol/fqterm_ssh_socket.cpp352
-rw-r--r--src/protocol/fqterm_ssh_socket.h131
-rw-r--r--src/protocol/internal/crc32.cpp84
-rw-r--r--src/protocol/internal/crc32.h37
-rw-r--r--src/protocol/internal/fqterm_serialization.h54
-rw-r--r--src/protocol/internal/fqterm_ssh1_packet.cpp148
-rw-r--r--src/protocol/internal/fqterm_ssh1_packet.h40
-rw-r--r--src/protocol/internal/fqterm_ssh2_kex.cpp394
-rw-r--r--src/protocol/internal/fqterm_ssh2_kex.h105
-rw-r--r--src/protocol/internal/fqterm_ssh2_packet.cpp267
-rw-r--r--src/protocol/internal/fqterm_ssh2_packet.h51
-rw-r--r--src/protocol/internal/fqterm_ssh_auth.cpp223
-rw-r--r--src/protocol/internal/fqterm_ssh_auth.h112
-rw-r--r--src/protocol/internal/fqterm_ssh_buffer.cpp302
-rw-r--r--src/protocol/internal/fqterm_ssh_buffer.h83
-rw-r--r--src/protocol/internal/fqterm_ssh_channel.cpp439
-rw-r--r--src/protocol/internal/fqterm_ssh_channel.h130
-rw-r--r--src/protocol/internal/fqterm_ssh_cipher.h58
-rw-r--r--src/protocol/internal/fqterm_ssh_const.h146
-rw-r--r--src/protocol/internal/fqterm_ssh_des.cpp180
-rw-r--r--src/protocol/internal/fqterm_ssh_des.h74
-rw-r--r--src/protocol/internal/fqterm_ssh_hash.h47
-rw-r--r--src/protocol/internal/fqterm_ssh_kex.cpp210
-rw-r--r--src/protocol/internal/fqterm_ssh_kex.h100
-rw-r--r--src/protocol/internal/fqterm_ssh_mac.cpp53
-rw-r--r--src/protocol/internal/fqterm_ssh_mac.h54
-rw-r--r--src/protocol/internal/fqterm_ssh_md5.cpp36
-rw-r--r--src/protocol/internal/fqterm_ssh_md5.h50
-rw-r--r--src/protocol/internal/fqterm_ssh_packet.cpp242
-rw-r--r--src/protocol/internal/fqterm_ssh_packet.h148
-rw-r--r--src/protocol/internal/fqterm_ssh_pubkey.h37
-rw-r--r--src/protocol/internal/fqterm_ssh_rsa.cpp67
-rw-r--r--src/protocol/internal/fqterm_ssh_rsa.h41
-rw-r--r--src/protocol/internal/fqterm_ssh_types.h43
37 files changed, 5471 insertions, 0 deletions
diff --git a/src/protocol/CMakeLists.txt b/src/protocol/CMakeLists.txt
new file mode 100644
index 0000000..c515590
--- /dev/null
+++ b/src/protocol/CMakeLists.txt
@@ -0,0 +1,65 @@
+set(export_SRCS
+ fqterm_socket.cpp
+ fqterm_socket.h
+ fqterm_ssh_socket.h
+ fqterm_ssh_socket.cpp
+)
+
+set(internal_SRCS
+ internal/crc32.h
+ internal/fqterm_serialization.h
+ internal/fqterm_ssh_auth.h
+ internal/fqterm_ssh_buffer.h
+ internal/fqterm_ssh_cipher.h
+ internal/fqterm_ssh_const.h
+ internal/fqterm_ssh_des.h
+ internal/fqterm_ssh_hash.h
+ internal/fqterm_ssh_kex.h
+ internal/fqterm_ssh2_kex.h
+ internal/fqterm_ssh_mac.h
+ internal/fqterm_ssh_md5.h
+ internal/fqterm_ssh_packet.h
+ internal/fqterm_ssh1_packet.h
+ internal/fqterm_ssh2_packet.h
+ internal/fqterm_ssh_rsa.h
+ internal/fqterm_ssh_channel.h
+ internal/fqterm_ssh_types.h
+ internal/fqterm_ssh_pubkey.h
+ internal/crc32.cpp
+ internal/fqterm_ssh_auth.cpp
+ internal/fqterm_ssh_buffer.cpp
+ internal/fqterm_ssh_des.cpp
+ internal/fqterm_ssh_kex.cpp
+ internal/fqterm_ssh2_kex.cpp
+ internal/fqterm_ssh_mac.cpp
+ internal/fqterm_ssh_md5.cpp
+ internal/fqterm_ssh_packet.cpp
+ internal/fqterm_ssh1_packet.cpp
+ internal/fqterm_ssh2_packet.cpp
+ internal/fqterm_ssh_rsa.cpp
+ internal/fqterm_ssh_channel.cpp
+)
+
+qt4_automoc(
+ ${export_SRCS}
+ ${internal_SRCS}
+)
+
+include_directories(
+ ${QT_INCLUDE_DIR}
+ ${QT_QTCORE_INCLUDE_DIR}
+ ${QT_QTNETWORK_INCLUDE_DIR}
+ ${OPENSSL_INCLUDE_DIR}
+ ${CMAKE_CURRENT_SOURCE_DIR}/internal
+ ${CMAKE_CURRENT_SOURCE_DIR}/../common
+ ${CMAKE_CURRENT_BINARY_DIR}
+)
+
+add_library(fqterm_protocol
+ ${export_SRCS}
+ ${internal_SRCS}
+)
+
+add_dependencies(fqterm_protocol
+ fqterm_common
+)
diff --git a/src/protocol/fqterm_socket.cpp b/src/protocol/fqterm_socket.cpp
new file mode 100644
index 0000000..131044a
--- /dev/null
+++ b/src/protocol/fqterm_socket.cpp
@@ -0,0 +1,672 @@
+/***************************************************************************
+ * 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 <stdlib.h>
+#include <ctype.h>
+
+#include <QByteArray>
+#include <QTcpSocket>
+#include <QProcess>
+
+#include "fqterm.h"
+#include "fqterm_socket.h"
+#include "fqterm_trace.h"
+
+#if !defined(Q_OS_BSD4) && !defined(_OS_FREEBSD_) \
+ && !defined(Q_OS_MACX) && !defined(Q_OS_DARWIN)
+#include <malloc.h>
+#endif
+
+namespace FQTerm {
+
+/* hack from wget/http.c */
+#define BASE64_LENGTH(len) (4 * (((len) + 2) / 3))
+static void base64_encode(const char *s, char *store, int length);
+static char *basic_authentication_encode(const char *user, const char *passwd,
+ const char *header);
+static int parse_http_status_line(const char *line, const char
+ **reason_phrase_ptr);
+
+const char wingate_enter = 'J' &0x1f;
+
+//==============================================================================
+//FQTermSocketPrivate
+
+//==============================================================================
+FQTermSocketPrivate::FQTermSocketPrivate(QObject *parent)
+ : QObject(parent) {
+ m_socket = new QTcpSocket(this);
+
+ // proxy related
+ proxy_type = NOPROXY;
+ proxy_state = 0;
+ bauth = false;
+
+ FQ_VERIFY(connect(m_socket, SIGNAL(connected()), this, SLOT(socketConnected())));
+ FQ_VERIFY(connect(m_socket, SIGNAL(hostFound()), this, SIGNAL(hostFound())));
+ FQ_VERIFY(connect(m_socket, SIGNAL(disconnected()), this, SIGNAL(connectionClosed())));
+
+ FQ_VERIFY(connect(m_socket, SIGNAL(readyRead()), this, SLOT(socketReadyRead())));
+ FQ_VERIFY(connect(m_socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SIGNAL(error(QAbstractSocket::SocketError))));
+}
+
+FQTermSocketPrivate::~FQTermSocketPrivate() {
+ delete m_socket;
+}
+
+void FQTermSocketPrivate::setProxy(int nProxyType, bool bAuth, const QString &strProxyHost,
+ quint16 uProxyPort, const QString &strProxyUsr,
+ const QString &strProxyPwd) {
+ proxy_type = nProxyType;
+ if (proxy_type == NOPROXY) {
+ return ;
+ }
+ bauth = bAuth;
+ proxy_host = strProxyHost;
+ proxy_port = uProxyPort;
+ proxy_usr = strProxyUsr;
+ proxy_pwd = strProxyPwd;
+}
+
+void FQTermSocketPrivate::socketConnected() {
+ QByteArray strPort;
+ QByteArray command(9, 0);
+
+ char *proxyauth;
+ char *request;
+ int len = 0;
+
+ switch (proxy_type) {
+ case NOPROXY:
+ // no proxy
+ emit connected();
+ return ;
+ case WINGATE:
+ // Wingate Proxy
+ strPort.setNum(port);
+ writeBlock(host.toLocal8Bit());
+ writeBlock(" ");
+ writeBlock(strPort);
+ writeBlock(&wingate_enter); // CTRL+J
+ emit socketState(TSHOSTCONNECTED);
+ return ;
+ case SOCKS4:
+ // Socks4 Proxy
+ command.resize(9);
+ command[0] = '\x04';
+ command[1] = '\x01';
+ memcpy(command.data() + 2, &addr_host.sin_port, 2);
+ memcpy(command.data() + 4, &addr_host.sin_addr, 4);
+ writeBlock(command);
+ proxy_state = 1;
+ emit socketState(TSPROXYCONNECTED);
+ return ;
+ case SOCKS5:
+ // Socks5 Proxy
+ if (bauth) {
+ command.resize(4);
+ command[0] = '\x05';
+ command[1] = '\x02';
+ command[2] = '\x02';
+ command[3] = '\x00';
+ writeBlock(command);
+ }
+ else {
+ command.resize(3);
+ command[0] = '\x05';
+ command[1] = '\x01';
+ command[2] = '\x00';
+ writeBlock(command);
+ }
+ proxy_state = 1;
+ emit socketState(TSPROXYCONNECTED);
+ return ;
+ case HTTP:
+ proxyauth = NULL;
+ if (bauth) {
+ proxyauth = basic_authentication_encode(proxy_usr.toLocal8Bit(),
+ proxy_pwd.toLocal8Bit(), "Proxy-Authorization");
+ }
+
+ len = proxyauth != NULL ? strlen(proxyauth): 0;
+
+ request = new char[host.length() + len + 81];
+
+ sprintf(request, "CONNECT %s:%u HTTP/1.0\r\n""%s\r\n", host.toLatin1().constData(), port,
+ proxyauth != NULL ? proxyauth : "");
+
+
+ writeBlock(request);
+ delete [] request;
+ free(proxyauth);
+ proxy_state = 1;
+ emit socketState(TSPROXYCONNECTED);
+ return ;
+ default:
+ emit socketState(TSHOSTCONNECTED);
+ return ;
+ }
+}
+
+void FQTermSocketPrivate::socketReadyRead() {
+ if (proxy_type == NOPROXY || proxy_state == 0) {
+ emit readyRead();
+ }
+
+ int nbytes, nread;
+ QByteArray from_socket;
+ // get the data size
+ nbytes = bytesAvailable();
+ if (nbytes <= 0) {
+ return ;
+ }
+
+ //resize input buffer
+ from_socket.resize(0);
+
+ //read data from socket to from_socket
+ from_socket = readBlock(nbytes);
+ nread = from_socket.size();
+ //do some checks
+ if (nread <= 0) {
+ FQ_TRACE("socket", 0) << "Failed to read socket: " << nread << " <= 0";
+ return ;
+ }
+ if (nread > nbytes) {
+ FQ_TRACE("socket", 0) << "Overflow when reading socket: "
+ << " nread = " << nread
+ << " > nbytes = " << nbytes;
+ return ;
+ }
+
+ // Socks4 Proxy Reply
+ if (proxy_type == SOCKS4 && proxy_state == 1) {
+ if (nread != 8) {
+ emit socketState(TSPROXYERROR);
+ return ;
+ }
+
+ if (from_socket.at(1) == 90) {
+ emit connected();
+ } else {
+ emit socketState(TSPROXYFAIL);
+ }
+ proxy_state = 0;
+ return ;
+ }
+
+ // HTTP Proxy Reply
+ if (proxy_type == HTTP && proxy_state != 0) {
+ const char *error;
+ int statcode = parse_http_status_line(from_socket.data(), &error);
+ if (statcode >= 200 && statcode < 300) {
+ emit connected();
+ } else {
+ emit socketState(TSPROXYFAIL);
+ }
+ proxy_state = 0;
+ return ;
+ }
+
+ // Socks5 Proxy Reply
+ if (proxy_type == SOCKS5 && proxy_state != 0) {
+ socks5_reply(from_socket, nread);
+ return ;
+ }
+}
+
+void FQTermSocketPrivate::flush() {
+ m_socket->flush();
+}
+
+/////////////////////////////////////////////////////
+//originally version from qhostaddress.cpp, qt4.3.2, --dp
+static bool isIp4(const QString& address)
+{
+ QStringList ipv4 = address.split(QLatin1String("."));
+ if (ipv4.count() != 4)
+ return false;
+
+ for (int i = 0; i < 4; ++i) {
+ bool ok = false;
+ uint byteValue = ipv4.at(i).toUInt(&ok);
+ if (!ok || byteValue > 255)
+ return false;
+
+ }
+
+ return true;
+}
+/////////////////////////////////////////////////////////
+
+void FQTermSocketPrivate::connectToHost(const QString &hostname, quint16 portnumber) {
+ host = hostname;
+ port = portnumber;
+ addr_host.sin_port = htons(portnumber);
+
+ if (proxy_type == NOPROXY) {
+ m_socket->connectToHost(host, port);
+ } else {
+ //get ip addr from name
+ struct hostent *hostent;
+#if defined(_OS_WIN32_) || defined(Q_OS_WIN32)
+ WSADATA wsd;
+ if (WSAStartup(0x202, &wsd) != 0) {
+ emit socketState(TSEINIWINSOCK);
+ WSACleanup();
+ return ;
+ }
+#endif
+ // QHostAddress ha(host);
+ //ipv4 address check -- dp
+ if(isIp4(host)) {
+ hostent = gethostbyaddr( host.toLocal8Bit(), host.length(), AF_INET );
+ FQ_TRACE("ssh", 1) << "by addr";
+ } else {
+ hostent = gethostbyname(host.toLocal8Bit());
+ FQ_TRACE("ssh", 1) << "by name";
+ }
+
+ if (hostent == NULL) {
+ emit socketState(TSEGETHOSTBYNAME);
+ return ;
+ }
+ //now only ipv4 support
+ memcpy(&addr_host.sin_addr, hostent->h_addr, 4);
+ m_socket->connectToHost(proxy_host, proxy_port);
+ }
+
+}
+
+void FQTermSocketPrivate::close() {
+ m_socket->close();
+}
+
+QByteArray FQTermSocketPrivate::readBlock(unsigned long maxlen) {
+ return m_socket->read(maxlen);
+}
+
+long FQTermSocketPrivate::writeBlock(const QByteArray &data) {
+ return m_socket->write(data);
+}
+
+unsigned long FQTermSocketPrivate::bytesAvailable() {
+ return m_socket->bytesAvailable();
+}
+
+/*------------------------------------------------------------------------
+ * connect command for socks5
+ *------------------------------------------------------------------------
+ */
+void FQTermSocketPrivate::socks5_connect() {
+ QByteArray command(10, 0);
+ command[0] = '\x05';
+ command[1] = '\x01';
+ command[2] = '\x00';
+ command[3] = '\x01';
+ memcpy(command.data() + 4, &addr_host.sin_addr, 4);
+ memcpy(command.data() + 8, &addr_host.sin_port, 2);
+ writeBlock(command);
+}
+
+/*------------------------------------------------------------------------
+ * authentation command for socks5
+ *------------------------------------------------------------------------
+ */
+void FQTermSocketPrivate::socks5_auth() {
+ int ulen = proxy_usr.length();
+ int plen = proxy_pwd.length();
+ QByteArray command(3+ulen + plen, 0);
+ sprintf((char*)command.data(), " %s %s", proxy_usr.toLocal8Bit().data(),
+ proxy_pwd.toLocal8Bit().data());
+ command[0] = '\x01';
+ command[1] = ulen;
+ command[2+ulen] = plen;
+ writeBlock(command);
+}
+
+/*------------------------------------------------------------------------
+ * reply from socks5
+ *------------------------------------------------------------------------
+ */
+void FQTermSocketPrivate::socks5_reply(const QByteArray &from_socket, int nread) {
+ if (proxy_state == 1) { // Socks5 Proxy Replay 1
+ if (nread != 2) {
+ proxy_state = 0;
+ emit socketState(TSPROXYERROR);
+ return ;
+ }
+ switch (from_socket.at(1)) {
+ case '\x00':
+ // no authentation needed
+ socks5_connect();
+ proxy_state = 3;
+ emit socketState(TSCONNECTVIAPROXY);
+ return ;
+ case '\x02':
+ //need user/password
+ socks5_auth();
+ proxy_state = 2;
+ emit socketState(TSPROXYAUTH);
+ return ;
+ case '\xff':
+ proxy_state = 0;
+ emit socketState(TSPROXYFAIL);
+ return ;
+ default:
+ proxy_state = 0;
+ emit socketState(TSPROXYFAIL);
+ return ;
+ }
+ } else if (proxy_state == 2) { //Socks5 Proxy Replay 2
+ if (nread != 2) {
+ proxy_state = 0;
+ emit socketState(TSPROXYERROR);
+ return ;
+ }
+
+
+ if (from_socket.at(1) != '\00') {
+ proxy_state = 0;
+ emit socketState(TSPROXYFAIL);
+ return ;
+ }
+ socks5_connect();
+ proxy_state = 3;
+ emit socketState(TSCONNECTVIAPROXY);
+ return ;
+ } else if (proxy_state == 3) { //Socks5 Proxy Replay 3
+ proxy_state = 0;
+ if (nread != 10) {
+ emit socketState(TSPROXYERROR);
+ return ;
+ }
+ if (from_socket.at(1) != '\00') {
+ emit socketState(TSPROXYFAIL);
+ return ;
+ }
+ emit connected();
+ return ;
+ } else {
+ proxy_state = 0;
+ return ;
+ }
+}
+
+/* hack from wget/http.c */
+/* How many bytes it will take to store LEN bytes in base64. */
+#define BASE64_LENGTH(len) (4 * (((len) + 2) / 3))
+
+/* Encode the string S of length LENGTH to base64 format and place it
+ to STORE. STORE will be 0-terminated, and must point to a writable
+ buffer of at least 1+BASE64_LENGTH(length) bytes. */
+static void base64_encode(const char *s, char *store, int length) {
+ /* Conversion table. */
+ static char tbl[64] = {
+ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
+ 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',
+ 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's',
+ 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', '+', '/'
+ };
+ int i;
+ unsigned char *p = (unsigned char*)store;
+
+ /* Transform the 3x8 bits to 4x6 bits, as required by base64. */
+ for (i = 0; i < length; i += 3) {
+ *p++ = tbl[s[0] >> 2];
+ *p++ = tbl[((s[0] &3) << 4) + (s[1] >> 4)];
+ *p++ = tbl[((s[1] &0xf) << 2) + (s[2] >> 6)];
+ *p++ = tbl[s[2] &0x3f];
+ s += 3;
+ }
+ /* Pad the result if necessary... */
+ if (i == length + 1) {
+ *(p - 1) = '=';
+ } else if (i == length + 2) {
+ *(p - 1) = *(p - 2) = '=';
+ }
+ /* ...and zero-terminate it. */
+ *p = '\0';
+}
+
+/* Create the authentication header contents for the `Basic' scheme.
+ This is done by encoding the string `USER:PASS' in base64 and
+ prepending `HEADER: Basic ' to it. */
+static char *basic_authentication_encode(const char *user, const char *passwd,
+ const char *header) {
+ char *t1, *t2, *res;
+ int len1 = strlen(user) + 1+strlen(passwd);
+ int len2 = BASE64_LENGTH(len1);
+
+ t1 = (char*)alloca(len1 + 1);
+ sprintf(t1, "%s:%s", user, passwd);
+ t2 = (char*)alloca(1+len2);
+ base64_encode(t1, t2, len1);
+ res = (char*)malloc(len2 + 11+strlen(header));
+ sprintf(res, "%s: Basic %s\r\n", header, t2);
+
+ return res;
+}
+
+/* Parse the HTTP status line, which is of format:
+
+ HTTP-Version SP Status-Code SP Reason-Phrase
+
+ The function returns the status-code, or -1 if the status line is
+ malformed. The pointer to reason-phrase is returned in RP. */
+static int parse_http_status_line(const char *line, const char
+ **reason_phrase_ptr) {
+ /* (the variables must not be named `major' and `minor', because
+ that breaks compilation with SunOS4 cc.) */
+ int mjr, mnr, statcode;
+ const char *p;
+
+ *reason_phrase_ptr = NULL;
+
+ /* The standard format of HTTP-Version is: `HTTP/X.Y', where X is
+ major version, and Y is minor version. */
+ if (strncmp(line, "HTTP/", 5) != 0) {
+ return -1;
+ }
+ line += 5;
+
+ /* Calculate major HTTP version. */
+ p = line;
+ for (mjr = 0; QChar(*line).isDigit(); line++) {
+ mjr = 10 * mjr + (*line - '0');
+ }
+ if (*line != '.' || p == line) {
+ return -1;
+ }
+ ++line;
+
+ /* Calculate minor HTTP version. */
+ p = line;
+ for (mnr = 0; QChar(*line).isDigit(); line++) {
+ mnr = 10 * mnr + (*line - '0');
+ }
+ if (*line != ' ' || p == line) {
+ return -1;
+ }
+ /* Wget will accept only 1.0 and higher HTTP-versions. The value of
+ minor version can be safely ignored. */
+ if (mjr < 1) {
+ return -1;
+ }
+ ++line;
+
+ /* Calculate status code. */
+ if (!(QChar(*line).isDigit() && QChar(line[1]).isDigit() && QChar(line[2])
+ .isDigit())) {
+ return -1;
+ }
+ statcode = 100 *(*line - '0') + 10 *(line[1] - '0') + (line[2] - '0');
+
+ /* Set up the reason phrase pointer. */
+ line += 3;
+ /* RFC2068 requires SPC here, but we allow the string to finish
+ here, in case no reason-phrase is present. */
+ if (*line != ' ') {
+ if (! *line) {
+ *reason_phrase_ptr = line;
+ } else {
+ return -1;
+ }
+ } else {
+ *reason_phrase_ptr = line + 1;
+ }
+
+ return statcode;
+}
+
+
+//==============================================================================
+//FQTermTelnetSocket
+//==============================================================================
+
+FQTermTelnetSocket::FQTermTelnetSocket()
+ : FQTermSocket() {
+ private_socket_ = new FQTermSocketPrivate();
+ FQ_VERIFY(connect(private_socket_, SIGNAL(connected()), this, SIGNAL(connected())));
+ FQ_VERIFY(connect(private_socket_, SIGNAL(hostFound()), this, SIGNAL(hostFound())));
+ 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(readyRead()), this, SIGNAL(readyRead())));
+ 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))));
+}
+
+FQTermTelnetSocket::~FQTermTelnetSocket() {
+ delete private_socket_;
+}
+
+void FQTermTelnetSocket::flush() {
+ private_socket_->flush();
+}
+
+void FQTermTelnetSocket::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);
+}
+
+void FQTermTelnetSocket::connectToHost(const QString &host, quint16 port) {
+ private_socket_->connectToHost(host, port);
+}
+
+void FQTermTelnetSocket::close() {
+ private_socket_->close();
+}
+
+QByteArray FQTermTelnetSocket::readBlock(unsigned long maxlen) {
+ return private_socket_->readBlock(maxlen);
+}
+
+long FQTermTelnetSocket::writeBlock(const QByteArray &data) {
+ return private_socket_->writeBlock(data);
+}
+
+unsigned long FQTermTelnetSocket::bytesAvailable() {
+ return private_socket_->bytesAvailable();
+}
+
+
+FQTermLocalSocket::FQTermLocalSocket( const QString& shell_bin ) : shell_bin_(shell_bin)
+{
+ shell_process_ = new QProcess();
+ shell_process_->setProcessChannelMode(QProcess::MergedChannels);
+ FQ_VERIFY(connect(shell_process_, SIGNAL(started()), this, SIGNAL(connected())));
+ FQ_VERIFY(connect(shell_process_, SIGNAL(stateChanged(QProcess::ProcessState)), this , SLOT(stateChanged(QProcess::ProcessState))));
+ FQ_VERIFY(connect(shell_process_, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(finished(int, QProcess::ExitStatus))));
+ //FQ_VERIFY(connect(shell_process_, SIGNAL(delayedCloseFinished()), this, SIGNAL(delayedCloseFinished())));
+ FQ_VERIFY(connect(shell_process_, SIGNAL(readyRead()), this, SIGNAL(readyRead())));
+ //TODO: Error
+ //FQ_VERIFY(connect(shell_process_, SIGNAL(error(QAbstractSocket::SocketError)), this, SIGNAL(error(QAbstractSocket::SocketError))));
+ //FQ_VERIFY(connect(shell_process_, SIGNAL(socketState(int)), this, SIGNAL(socketState(int))));
+}
+
+FQTermLocalSocket::~FQTermLocalSocket()
+{
+ delete shell_process_;
+}
+
+void FQTermLocalSocket::connectToHost( const QString &host, quint16 port )
+{
+ shell_process_->start("E:/yxl/test.exe"/*C:/Windows/System32/cmd.exeshell_bin*/, QIODevice::ReadWrite | QIODevice::Unbuffered);
+}
+
+void FQTermLocalSocket::close()
+{
+ shell_process_->close();
+}
+
+QByteArray FQTermLocalSocket::readBlock( unsigned long maxlen )
+{
+ return shell_process_->read(maxlen);
+}
+
+long FQTermLocalSocket::writeBlock( const QByteArray &data )
+{
+ int count = shell_process_->write(data);
+
+ //char c;
+// shell_process_->getChar(&c);
+// shell_process_->ungetChar(c);
+ if (bytesAvailable())
+ {
+ emit readyRead();
+ }
+ return count;
+}
+
+unsigned long FQTermLocalSocket::bytesAvailable()
+{
+ return shell_process_->bytesAvailable();
+}
+
+void FQTermLocalSocket::finished( int exitCode, QProcess::ExitStatus exitStatus )
+{
+ emit connectionClosed();
+}
+void FQTermLocalSocket::stateChanged(QProcess::ProcessState newState)
+{
+ switch(newState)
+ {
+ case QProcess::NotRunning:
+ break;
+ case QProcess::Starting:
+ emit hostFound();
+ shell_process_->write("dir\n");
+ break;
+ case QProcess::Running:
+ break;
+
+ }
+}
+
+
+} // namespace FQTerm
+
+#include "fqterm_socket.moc"
diff --git a/src/protocol/fqterm_socket.h b/src/protocol/fqterm_socket.h
new file mode 100644
index 0000000..2d6240b
--- /dev/null
+++ b/src/protocol/fqterm_socket.h
@@ -0,0 +1,196 @@
+/***************************************************************************
+ * 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. *
+ ***************************************************************************/
+
+#ifndef FQTERM_SOCKET_H
+#define FQTERM_SOCKET_H
+
+// _OS_X_ not defined if i dont include it
+#include <QGlobalStatic>
+
+// different
+#if defined(Q_OS_WIN32) || defined(_OS_WIN32_)
+
+#include <winsock2.h>
+
+#elif defined(Q_OS_BSD4) || defined(_OS_FREEBSD_) \
+ || defined(Q_OS_MACX) || defined(Q_OS_DARWIN)
+
+#include <netdb.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#else
+
+#include <netdb.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <arpa/inet.h>
+
+#endif
+
+#include <QAbstractSocket>
+#include <QString>
+#include <QStringList>
+#include <QProcess>
+
+class QTcpSocket;
+namespace FQTerm {
+/*
+ * Socket with proxy support.
+ *
+ */
+class FQTermSocketPrivate: public QObject {
+
+ Q_OBJECT;
+ public:
+ FQTermSocketPrivate(QObject *parent_ = 0);
+
+ ~FQTermSocketPrivate();
+
+ void flush();
+ void setProxy(int nProxyType, bool bAuth, const QString &strProxyHost,
+ quint16 uProxyPort, const QString &strProxyUsr,
+ const QString &strProxyPwd);
+ void connectToHost(const QString &hostname, quint16 portnumber);
+ void close();
+ QByteArray readBlock(unsigned long maxlen);
+ long writeBlock(const QByteArray &data);
+ unsigned long bytesAvailable();
+
+ signals:
+ void connected();
+ void hostFound();
+ void connectionClosed();
+ void delayedCloseFinished();
+ void readyRead();
+ void error(QAbstractSocket::SocketError);
+ void socketState(int);
+
+ protected slots:
+ void socketConnected();
+ void socketReadyRead();
+
+ protected:
+ // socks5 function
+ void socks5_connect();
+ void socks5_auth();
+ void socks5_reply(const QByteArray &, int);
+
+ private:
+ // proxy
+ int proxy_type;
+ QString proxy_host;
+ QString proxy_usr;
+ quint16 proxy_port;
+ QString proxy_pwd;
+ QString host;
+ quint16 port;
+ int proxy_state;
+ bool bauth;
+
+ struct sockaddr_in addr_host;
+
+ QTcpSocket *m_socket;
+};
+
+// Virtual base class for FQTermTelnetSocket and FQTermSSHSocket
+class FQTermSocket: public QObject {
+
+ Q_OBJECT;
+ public:
+ FQTermSocket(QObject *parent = 0): QObject(parent) {}
+
+ virtual ~FQTermSocket() {}
+
+ virtual void flush() = 0;
+ virtual void setProxy(int nProxyType, bool bAuth,
+ const QString &strProxyHost,
+ quint16 uProxyPort,
+ const QString &strProxyUsr,
+ const QString &strProxyPwd) = 0;
+ virtual void connectToHost(const QString &host, quint16 port) = 0;
+ virtual void close() = 0;
+ virtual QByteArray readBlock(unsigned long maxlen) = 0;
+ virtual long writeBlock(const QByteArray &data) = 0;
+ virtual unsigned long bytesAvailable() = 0;
+ virtual bool readyForInput() {return true;}
+ virtual bool setTermSize(int col, int row) {return 0;}
+ signals:
+ void sshAuthOK();
+ void connected();
+ void hostFound();
+ void connectionClosed();
+ void delayedCloseFinished();
+ void readyRead();
+ void error(QAbstractSocket::SocketError);
+ void errorMessage(QString);
+ void socketState(int);
+ void requestUserPwd(QString *user, QString *pwd, bool *isOK);
+};
+
+class FQTermTelnetSocket: public FQTermSocket {
+ private:
+ FQTermSocketPrivate *private_socket_;
+
+ public:
+ FQTermTelnetSocket();
+
+ ~FQTermTelnetSocket();
+
+ void flush();
+ void setProxy(int nProxyType, bool bAuth, const QString &strProxyHost,
+ quint16 uProxyPort, const QString &strProxyUsr,
+ const QString &strProxyPwd);
+ void connectToHost(const QString &host, quint16 port);
+ void close();
+ QByteArray readBlock(unsigned long maxlen);
+ long writeBlock(const QByteArray &data);
+ unsigned long bytesAvailable();
+};
+
+class FQTermLocalSocket: public FQTermSocket {
+ Q_OBJECT;
+private:
+ QString shell_bin_;
+ QProcess* shell_process_;
+public:
+ FQTermLocalSocket(const QString& shell_bin);
+ ~FQTermLocalSocket();
+ void flush(){}
+ void setProxy(int nProxyType, bool bAuth, const QString &strProxyHost,
+ quint16 uProxyPort, const QString &strProxyUsr,
+ const QString &strProxyPwd){}
+ void connectToHost(const QString &host, quint16 port);
+ void close();
+ QByteArray readBlock(unsigned long maxlen);
+ long writeBlock(const QByteArray &data);
+ unsigned long bytesAvailable();
+
+public slots:
+ void stateChanged(QProcess::ProcessState newState);
+ void finished (int exitCode, QProcess::ExitStatus exitStatus);
+};
+
+
+
+} // namespace FQTerm
+
+#endif // FQTERM_SOCKET_H
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"
diff --git a/src/protocol/fqterm_ssh_socket.h b/src/protocol/fqterm_ssh_socket.h
new file mode 100644
index 0000000..aa6fef4
--- /dev/null
+++ b/src/protocol/fqterm_ssh_socket.h
@@ -0,0 +1,131 @@
+/***************************************************************************
+ * 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. *
+ ***************************************************************************/
+
+#ifndef FQTERM_SSH_SOCKET_H
+#define FQTERM_SSH_SOCKET_H
+
+#include "fqterm_socket.h"
+
+
+namespace FQTerm {
+
+
+class FQTermSocketPrivate;
+
+class FQTermSSHPacketReceiver;
+
+class FQTermSSHPacketSender;
+
+class FQTermSSHBuffer;
+
+class FQTermSSHKex;
+
+class FQTermSSHAuth;
+
+class FQTermSSHChannel;
+
+
+class FQTermSSHSocket: public FQTermSocket {
+
+ Q_OBJECT;
+private:
+ enum FQTermSSHSocketState {
+
+ BeforeSession, SockSession
+ } ssh_socket_state_;
+
+ FQTermSocketPrivate *private_socket_;
+
+
+ FQTermSSHBuffer *input_buffer_;
+
+ FQTermSSHBuffer *output_buffer_;
+
+ FQTermSSHBuffer *socket_buffer_;
+
+
+ FQTermSSHPacketReceiver *packet_receiver_;
+
+ FQTermSSHPacketSender *packet_sender_;
+
+
+ FQTermSSHKex *key_exchanger_;
+
+ FQTermSSHAuth *authentication_;
+
+ FQTermSSHChannel *ssh_channel_;
+
+ bool is_channel_ok_;
+ bool auth_ok_emitted_;
+
+ QByteArray init_user_, init_passwd_;
+
+ QString server_name_;
+
+ int ssh_version_;
+ int col_;
+ int row_;
+ QString termtype_;
+ int chooseVersion(const QString &ver);
+ unsigned long socketWriteBlock(const char *data, unsigned long len);
+ void parsePacket();
+
+private slots:
+ void handlePacket(int type);
+ void writeData();
+ void kexOK();
+ void authOK();
+ void channelOK();
+ void channelReadyRead(const char *data, int len);
+ void socketReadyRead();
+ void handleError(QString);
+
+public:
+ FQTermSSHSocket(int col = 80, int row = 24, const QString& termtype = "vt100", const char *sshuser = NULL, const char *sshpasswd = NULL);
+
+ ~FQTermSSHSocket();
+
+
+ void setProxy(int nProxyType, //0-no proxy; 1-wingate; 2-sock4; 3-socks5
+ bool bAuth, // if authentation needed
+ const QString &strProxyHost, quint16 uProxyPort, const QString &strProxyUsr,
+ const QString &strProxyPwd);
+
+ void connectToHost(const QString &host_name, quint16 port);
+
+ void init(int ssh_version);
+
+ QByteArray readBlock(unsigned long size);
+ long writeBlock(const QByteArray &data);
+
+ virtual bool readyForInput() {return is_channel_ok_;}
+ virtual bool setTermSize(int col, int row);
+ unsigned long bytesAvailable();
+
+ void flush();
+ void close();
+
+};
+
+} // namespace FQTerm
+
+
+#endif //FQTERM_SSH_SOCKET_H
+
diff --git a/src/protocol/internal/crc32.cpp b/src/protocol/internal/crc32.cpp
new file mode 100644
index 0000000..2039ac8
--- /dev/null
+++ b/src/protocol/internal/crc32.cpp
@@ -0,0 +1,84 @@
+/* $OpenBSD: crc32.c,v 1.9 2003/02/12 21:39:50 markus Exp $ */
+
+/*
+ * Copyright (c) 2003 Markus Friedl. 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 ``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 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.
+ */
+#include "crc32.h"
+
+namespace FQTerm {
+static const u_int32_t crc32tab[] = {
+ 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, 0x706af48fL,
+ 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, 0xe0d5e91eL, 0x97d2d988L,
+ 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L,
+ 0xf3b97148L, 0x84be41deL, 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L,
+ 0x136c9856L, 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L,
+ 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, 0xa2677172L,
+ 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, 0x35b5a8faL, 0x42b2986cL,
+ 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L,
+ 0x26d930acL, 0x51de003aL, 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L,
+ 0xcfba9599L, 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L,
+ 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, 0x01db7106L,
+ 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, 0x9fbfe4a5L, 0xe8b8d433L,
+ 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL,
+ 0x91646c97L, 0xe6635c01L, 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL,
+ 0x6c0695edL, 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L,
+ 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, 0xfbd44c65L,
+ 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, 0x4adfa541L, 0x3dd895d7L,
+ 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, 0x346ed9fcL, 0xad678846L, 0xda60b8d0L,
+ 0x44042d73L, 0x33031de5L, 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL,
+ 0xbe0b1010L, 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL,
+ 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, 0x2eb40d81L,
+ 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, 0x03b6e20cL, 0x74b1d29aL,
+ 0xead54739L, 0x9dd277afL, 0x04db2615L, 0x73dc1683L, 0xe3630b12L, 0x94643b84L,
+ 0x0d6d6a3eL, 0x7a6a5aa8L, 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L,
+ 0xf00f9344L, 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL,
+ 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, 0x67dd4accL,
+ 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, 0xd6d6a3e8L, 0xa1d1937eL,
+ 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL,
+ 0xd80d2bdaL, 0xaf0a1b4cL, 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L,
+ 0x316e8eefL, 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L,
+ 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, 0xb2bd0b28L,
+ 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, 0x2cd99e8bL, 0x5bdeae1dL,
+ 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL,
+ 0x72076785L, 0x05005713L, 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L,
+ 0x92d28e9bL, 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L,
+ 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, 0x18b74777L,
+ 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, 0x8f659effL, 0xf862ae69L,
+ 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L,
+ 0xa7672661L, 0xd06016f7L, 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL,
+ 0x40df0b66L, 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L,
+ 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, 0xcdd70693L,
+ 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, 0x5d681b02L, 0x2a6f2b94L,
+ 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, 0x2d02ef8dL
+};
+
+u_int32_t ssh_crc32(const u_char *buf, u_int32_t size) {
+ u_int32_t i, crc;
+
+ crc = 0;
+ for (i = 0; i < size; i++) {
+ crc = crc32tab[(crc ^ buf[i]) &0xff] ^ (crc >> 8);
+ } return crc;
+}
+
+} // namespace FQTerm
diff --git a/src/protocol/internal/crc32.h b/src/protocol/internal/crc32.h
new file mode 100644
index 0000000..ca95b89
--- /dev/null
+++ b/src/protocol/internal/crc32.h
@@ -0,0 +1,37 @@
+/* $OpenBSD: crc32.h,v 1.14 2003/02/12 21:39:50 markus Exp $ */
+
+/*
+ * Copyright (c) 2003 Markus Friedl. 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 ``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 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.
+ */
+
+#ifndef SSH_CRC32_H
+#define SSH_CRC32_H
+#include "fqterm_ssh_types.h"
+
+namespace FQTerm {
+
+u_int32_t ssh_crc32(const u_char *, u_int32_t);
+
+} // namespace FQTerm
+
+#endif
diff --git a/src/protocol/internal/fqterm_serialization.h b/src/protocol/internal/fqterm_serialization.h
new file mode 100644
index 0000000..7a858c0
--- /dev/null
+++ b/src/protocol/internal/fqterm_serialization.h
@@ -0,0 +1,54 @@
+/***************************************************************************
+ * 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. *
+ ***************************************************************************/
+
+#ifndef FQTERM_SERIALIZATION_H
+#define FQTERM_SERIALIZATION_H
+
+#if defined(WIN32)
+typedef unsigned __int16 u_int16_t;
+#else
+#include <arpa/inet.h>
+#endif
+
+namespace FQTerm {
+
+inline u_int16_t ntohu16(const unsigned char *buf) {
+ return (buf[0] << 8) | buf[1];
+}
+
+inline u_int32_t ntohu32(const unsigned char *buf) {
+ return (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
+}
+
+inline void htonu32(unsigned char *buf, u_int32_t number) {
+ buf[0] = (number >> 24) & 0xFF;
+ buf[1] = (number >> 16) & 0xFF;
+ buf[2] = (number >> 8) & 0xFF;
+ buf[3] = number & 0xFF;
+}
+
+inline void htonu16(unsigned char *buf, u_int16_t number) {
+ buf[0] = (number >> 8 & 0xFF);
+ buf[1] = (number & 0xFF);
+}
+
+} // namespace FQTerm
+
+#endif // FQTERM_SERIALIZATION_H
diff --git a/src/protocol/internal/fqterm_ssh1_packet.cpp b/src/protocol/internal/fqterm_ssh1_packet.cpp
new file mode 100644
index 0000000..ded7fe4
--- /dev/null
+++ b/src/protocol/internal/fqterm_ssh1_packet.cpp
@@ -0,0 +1,148 @@
+/***************************************************************************
+ * 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_trace.h"
+#include "fqterm_ssh_buffer.h"
+#include "fqterm_ssh1_packet.h"
+#include "fqterm_ssh_des.h"
+
+#include "fqterm_serialization.h"
+#include "crc32.h"
+
+namespace FQTerm {
+//==============================================================================
+//FQTermSSH1PacketSender
+//==============================================================================
+//==============================================================================
+//
+// SSH1 Packet Structure:
+// --------------------------------------------------------------------------
+// | length | padding | type | data | crc32 |
+// --------------------------------------------------------------------------
+// | uint32 | 1-7bytes | uchar | | 4bytes|
+// --------------------------------------------------------------------------
+// encrypt = padding + type + data + crc32
+// length = type + data + crc32
+//
+//==============================================================================
+
+void FQTermSSH1PacketSender::makePacket() {
+ int len, padding, i;
+ u_int32_t rand_val = 0;
+
+ delete output_buffer_;
+
+ len = buffer_->len() + 4; //CRC32
+ padding = 8-(len % 8);
+
+ output_buffer_ = new FQTermSSHBuffer(len + padding + 4); //pktlen and crc32
+ output_buffer_->putInt(len);
+
+ for (i = 0; i < padding; i++) {
+ if (i % 4 == 0) {
+ rand_val = rand(); // FIXME: rand() doesn't range from 0 to 2^32.
+ }
+
+ output_buffer_->putByte(rand_val &0xff);
+ rand_val >>= 8;
+ }
+
+ output_buffer_->putRawData((const char*)buffer_->data(), buffer_->len());
+ output_buffer_->putInt(ssh_crc32(output_buffer_->data() + 4, output_buffer_->len() - 4));
+
+ if (is_encrypt_) {
+ cipher_->encrypt(output_buffer_->data() + 4, output_buffer_->data() + 4, output_buffer_->len() - 4);
+ }
+
+}
+
+//==============================================================================
+//FQTermSSH1PacketReceiver
+//==============================================================================
+void FQTermSSH1PacketReceiver::parseData(FQTermSSHBuffer *input) {
+ u_int mycrc, gotcrc;
+ u_char *buf = NULL;
+ u_char *targetData = NULL;
+ u_char *sourceData = NULL;
+
+ // Get the length of the packet.
+ while (input->len() > 0) {
+ if (input->len() < 4) {
+ FQ_TRACE("ssh1packet", 3) << "The packet is too small.";
+ return ;
+ }
+ buf = input->data();
+ real_data_len_ = ntohu32(buf);
+
+ if (real_data_len_ > SSH_BUFFER_MAX) {
+ emit packetError(tr("parseData: The packet is too big"));
+ return ;
+ }
+
+ u_int total_len = (real_data_len_ + 8) &~7;
+ u_int padding_len = total_len - real_data_len_;
+
+ real_data_len_ -= 5;
+ buffer_->clear();
+
+ // Get the data of the packet.
+ if (input->len() - 4 < (long)total_len) {
+ FQ_TRACE("ssh1packet", 3) << "The packet is too small";
+ return ;
+ }
+
+ real_data_len_ = input->getInt() - 5;
+ targetData = new u_char[total_len];
+ sourceData = new u_char[total_len];
+ memset(targetData, 0, total_len);
+ memset(sourceData, 0, total_len);
+
+ input->getRawData((char*)sourceData, total_len);
+ if (is_decrypt_) {
+ cipher_->decrypt(sourceData, targetData, total_len);
+ } else {
+ memcpy(targetData, sourceData, total_len);
+ }
+
+ buffer_->putRawData((char*)targetData, total_len);
+
+ // Check the crc32.
+ buf = buffer_->data() + total_len - 4;
+ mycrc = ntohu32(buf);
+ gotcrc = ssh_crc32(buffer_->data(), total_len - 4);
+
+ if (mycrc != gotcrc) {
+ emit packetError(tr("parseData: bad CRC32"));
+ break;
+ }
+
+ // Drop the padding.
+ buffer_->consume(padding_len);
+
+ packet_type_ = buffer_->getByte();
+
+ emit packetAvaliable(packet_type_);
+
+ delete [] sourceData;
+ delete [] targetData;
+ }
+}
+
+} // namespace FQTerm
diff --git a/src/protocol/internal/fqterm_ssh1_packet.h b/src/protocol/internal/fqterm_ssh1_packet.h
new file mode 100644
index 0000000..a535635
--- /dev/null
+++ b/src/protocol/internal/fqterm_ssh1_packet.h
@@ -0,0 +1,40 @@
+/***************************************************************************
+ * 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. *
+ ***************************************************************************/
+
+#ifndef FQTERM_SSH1_PACKET_H
+#define FQTERM_SSH1_PACKET_H
+
+#include "fqterm_ssh_packet.h"
+
+namespace FQTerm {
+
+class FQTermSSH1PacketSender: public FQTermSSHPacketSender {
+protected:
+ virtual void makePacket();
+};
+
+class FQTermSSH1PacketReceiver: public FQTermSSHPacketReceiver {
+public:
+ virtual void parseData(FQTermSSHBuffer *input);
+};
+
+} // namespace FQTerm
+
+#endif // FQTERM_SSH1_PACKET
diff --git a/src/protocol/internal/fqterm_ssh2_kex.cpp b/src/protocol/internal/fqterm_ssh2_kex.cpp
new file mode 100644
index 0000000..acae08d
--- /dev/null
+++ b/src/protocol/internal/fqterm_ssh2_kex.cpp
@@ -0,0 +1,394 @@
+/***************************************************************************
+ * 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 <vector>
+#include <string>
+#include <openssl/bn.h>
+#include <openssl/sha.h>
+#include <openssl/des.h>
+#include <openssl/objects.h>
+#include <openssl/evp.h>
+
+#include "fqterm_ssh2_kex.h"
+#include "fqterm_ssh_md5.h"
+#include "fqterm_trace.h"
+
+namespace FQTerm {
+
+static const int g = 2;
+static const int q = 128;
+static const unsigned char p[q]={
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34,
+ 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
+ 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74,
+ 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22,
+ 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
+ 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B,
+ 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37,
+ 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
+ 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6,
+ 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B,
+ 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
+ 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5,
+ 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6,
+ 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE6, 0x53, 0x81,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+
+FQTermSSH2Kex::FQTermSSH2Kex(const char *V_C, const char *V_S)
+ : FQTermSSHKex(V_C, V_S) {
+ is_first_kex_ = true;
+ kex_state_ = FQTermSSH2Kex::BEFORE_KEXINIT;
+
+ I_C_len_ = 0;
+ I_C_ = NULL;
+ I_S_len_ = 0;
+ I_S_ = NULL;
+
+ bn_x_ = BN_new();
+ bn_e_ = BN_new();
+ bn_g_ = BN_new();
+ bn_p_ = BN_new();
+ ctx_ = BN_CTX_new();
+
+ bn_K_ = BN_new();
+ bn_f_ = BN_new();
+
+ BN_set_word(bn_g_, g);
+ BN_bin2bn(p, q, bn_p_);
+
+ session_id_ = NULL;
+}
+
+FQTermSSH2Kex::~FQTermSSH2Kex() {
+ delete[] I_C_;
+ delete[] I_S_;
+
+ BN_clear_free(bn_x_);
+ BN_clear_free(bn_e_);
+ BN_clear_free(bn_g_);
+ BN_clear_free(bn_p_);
+ BN_CTX_free(ctx_);
+
+ BN_clear_free(bn_K_);
+ BN_clear_free(bn_f_);
+
+ delete[] session_id_;
+}
+
+void FQTermSSH2Kex::initKex(FQTermSSHPacketReceiver *packetReceiver,
+ FQTermSSHPacketSender *packetSender) {
+ packet_receiver_ = packetReceiver;
+ packet_sender_ = packetSender;
+ packet_receiver_->disconnect(this);
+ FQ_VERIFY(connect(packet_receiver_, SIGNAL(packetAvaliable(int)),
+ this, SLOT(handlePacket(int))));
+ kex_state_ = FQTermSSH2Kex::BEFORE_KEXINIT;
+ emit reKex();
+}
+
+void FQTermSSH2Kex::handlePacket(int type) {
+ switch (kex_state_) {
+ case FQTermSSH2Kex::BEFORE_KEXINIT:
+ negotiateAlgorithms();
+ exchangeKey();
+ kex_state_ = FQTermSSH2Kex::WAIT_REPLY;
+ break;
+ case FQTermSSH2Kex::WAIT_REPLY:
+ if (verifyKey()) {
+ sendNewKeys();
+ kex_state_ = FQTermSSH2Kex::SESSIONKEY_SENT;
+ } else {
+ emit kexError(tr("Key exchange failed!"));
+ }
+ break;
+ case FQTermSSH2Kex::SESSIONKEY_SENT:
+ if (changeKeyAlg()) {
+ kex_state_ = FQTermSSH2Kex::KEYEX_OK;
+ emit kexOK();
+ }
+ break;
+ case FQTermSSH2Kex::KEYEX_OK:
+ // TODO: how about Key Re-Exchange (see RFC 4253, 9. Key Re-Exchange)
+ break;
+ }
+}
+
+void FQTermSSH2Kex::negotiateAlgorithms() {
+ FQ_FUNC_TRACE("ssh2kex", 10);
+
+ if (packet_receiver_->packetType() != SSH2_MSG_KEXINIT) {
+ emit kexError(tr("startKex: First packet is not SSH_MSG_KEXINIT"));
+ return ;
+ }
+
+ // 0. Backup the payload of this server packet.
+ I_S_len_ = packet_receiver_->packetDataLen() + 1; // add 1 bytes for packet type.
+ delete[] I_S_;
+ I_S_ = new char[I_S_len_];
+ I_S_[0] = SSH2_MSG_KEXINIT;
+ memcpy(I_S_ + 1, packet_receiver_->buffer_->data(), I_S_len_ - 1);
+
+ // 1. Parse server kex init packet
+ packet_receiver_->getRawData((char*)cookie_, 16);
+
+ std::vector<char> name_lists;
+ for (int i = 0; i < 10; ++i) {
+ int name_lists_len = packet_receiver_->getInt();
+ if (name_lists_len > 0) {
+ name_lists.resize(name_lists_len);
+ packet_receiver_->getRawData(&name_lists[0], name_lists_len);
+ FQ_TRACE("ssh2kex", 5) << "Algorithms: " << QString::fromAscii(&name_lists[0], name_lists_len);
+ } else {
+ FQ_TRACE("ssh2kex", 5) << "None Algorithms";
+ }
+ }
+
+ bool first_kex_packet_follows = packet_receiver_->getByte();
+ FQ_TRACE("ssh2kex", 5) << "first_kex_packet_follows: " << first_kex_packet_follows;
+
+ packet_receiver_->consume(4);
+
+ // 2. compose a kex init packet.
+ packet_sender_->startPacket(SSH2_MSG_KEXINIT);
+ packet_sender_->putRawData((const char*)cookie_, 16); // FIXME: generate new cookie_;
+ packet_sender_->putString("diffie-hellman-group1-sha1");
+ packet_sender_->putString("ssh-rsa");
+ packet_sender_->putString("3des-cbc");
+ packet_sender_->putString("3des-cbc");
+ packet_sender_->putString("hmac-sha1");
+ packet_sender_->putString("hmac-sha1");
+ packet_sender_->putString("none");
+ packet_sender_->putString("none");
+ packet_sender_->putString("");
+ packet_sender_->putString("");
+
+ packet_sender_->putByte(false);
+ packet_sender_->putInt(0);
+
+ // 3. backup the payload of this client packet.
+ I_C_len_ = packet_sender_->buffer_->len();
+ delete[] I_C_;
+ I_C_ = new char[I_C_len_];
+ memcpy(I_C_, packet_sender_->buffer_->data(), I_C_len_);
+
+ // 4. send packet to server
+ packet_sender_->write();
+}
+
+void FQTermSSH2Kex::exchangeKey() {
+ BN_rand(bn_x_, q, 0, -1);
+ BN_mod_exp(bn_e_, bn_g_, bn_x_, bn_p_, ctx_);
+
+ packet_sender_->startPacket(SSH2_MSG_KEXDH_INIT);
+ packet_sender_->putBN2(bn_e_);
+ packet_sender_->write();
+}
+
+static RSA *CreateRSAContext(unsigned char *host_key, int len);
+
+bool FQTermSSH2Kex::verifyKey() {
+ if (packet_receiver_->packetType() != SSH2_MSG_KEXDH_REPLY) {
+ emit kexError(tr("Expect a SSH_MSG_KEXDH_REPLY packet"));
+ return false;
+ }
+
+ // Extract data
+
+ int K_S_len = -1;
+ unsigned char *K_S = (unsigned char *)packet_receiver_->getString(&K_S_len);
+
+ packet_receiver_->getBN2(bn_f_);
+
+ int s_len = -1;
+ unsigned char *s = (unsigned char *)packet_receiver_->getString(&s_len);
+
+ BN_mod_exp(bn_K_, bn_f_, bn_x_, bn_p_, ctx_);
+
+ FQTermSSHBuffer *buffer = packet_sender_->output_buffer_;
+
+ buffer->clear();
+ buffer->putString(V_C_);
+ buffer->putString(V_S_);
+ buffer->putString(I_C_, I_C_len_);
+ buffer->putString(I_S_, I_S_len_);
+ buffer->putString((char *)K_S, K_S_len);
+ buffer->putSSH2BN(bn_e_);
+ buffer->putSSH2BN(bn_f_);
+ buffer->putSSH2BN(bn_K_);
+
+ SHA1(buffer->data(), buffer->len(), H_);
+
+ // Start verify
+ unsigned char s_H[SHA_DIGEST_LENGTH];
+ SHA1(H_, SHA_DIGEST_LENGTH, s_H);
+
+ // Ignore the first 15 bytes of the signature of H sent from server:
+ // algorithm_name_length[4], algorithm_name[7]("ssh-rsa") and signature_length[4].
+ RSA *rsactx = CreateRSAContext(K_S, K_S_len);
+ int sig_len = s_len - 15;
+ unsigned char *sig = s + 15;
+ int res = RSA_verify(NID_sha1, s_H, SHA_DIGEST_LENGTH,
+ sig, sig_len, rsactx);
+
+ RSA_free(rsactx);
+
+ delete K_S;
+ delete s;
+
+ return res == 1;
+}
+
+static RSA *CreateRSAContext(unsigned char *host_key, int len) {
+ FQTermSSHBuffer buffer(len);
+
+ buffer.putRawData((char *)host_key, len);
+
+ int algo_len = -1;
+ unsigned char *algo = (unsigned char *)buffer.getString(&algo_len);
+
+ FQ_VERIFY(std::string("ssh-rsa") == std::string((char *)algo));
+
+ int e_len = -1;
+ unsigned char *e = (unsigned char *)buffer.getString(&e_len);
+
+ int n_len = -1;
+ unsigned char *n = (unsigned char *)buffer.getString(&n_len);
+
+
+ RSA *rsa = RSA_new();
+ rsa->e = BN_new();
+ BN_bin2bn(e, e_len, rsa->e);
+
+ rsa->n = BN_new();
+ BN_bin2bn(n, n_len, rsa->n);
+
+ delete[] algo;
+ delete[] e;
+ delete[] n;
+
+ return rsa;
+}
+
+void FQTermSSH2Kex::sendNewKeys(){
+ packet_sender_->startPacket(SSH2_MSG_NEWKEYS);
+ packet_sender_->write();
+}
+
+bool FQTermSSH2Kex::changeKeyAlg() {
+ if (packet_receiver_->packetType() != SSH2_MSG_NEWKEYS) {
+ emit kexError(tr("Expect a SSH_MSG_NEWKEYS packet"));
+ return false;
+ }
+
+ if (session_id_ == NULL) {
+ session_id_ = new unsigned char[SHA_DIGEST_LENGTH];
+ memcpy(session_id_, H_, SHA_DIGEST_LENGTH);
+ }
+
+ packet_sender_->setEncryptionType(SSH_CIPHER_3DES);
+ packet_receiver_->setEncryptionType(SSH_CIPHER_3DES);
+
+ packet_sender_->setMacType(FQTERM_SSH_HMAC_SHA1);
+ packet_receiver_->setMacType(FQTERM_SSH_HMAC_SHA1);
+
+ // From RFC 4253 section 7.2:
+ // Initial IV client to server: HASH(K || H || "A" || session_id)
+ // (Here K is encoded as mpint and "A" as byte and session_id as raw
+ // data. "A" means the single character A, ASCII 65).
+ //
+ // Initial IV server to client: HASH(K || H || "B" || session_id)
+ //
+ // Encryption key client to server: HASH(K || H || "C" || session_id)
+ //
+ // Encryption key server to client: HASH(K || H || "D" || session_id)
+ //
+ // Integrity key client to server: HASH(K || H || "E" || session_id)
+ //
+ // Integrity key server to client: HASH(K || H || "F" || session_id)
+
+ int IV_c2s_len = packet_sender_->getIVSize();
+ unsigned char *IV_c2s = computeKey(IV_c2s_len, 'A');
+
+ int IV_s2c_len = packet_receiver_->getIVSize();
+ unsigned char *IV_s2c = computeKey(IV_s2c_len, 'B');
+
+ int key_c2s_len = packet_sender_->getKeySize();
+ unsigned char *key_c2s = computeKey(key_c2s_len, 'C');
+
+ int key_s2c_len = packet_receiver_->getKeySize();
+ unsigned char *key_s2c = computeKey(key_s2c_len, 'D');
+
+ int mac_key_c2s_len = packet_sender_->getMacKeySize();
+ unsigned char *mac_key_c2s = computeKey(mac_key_c2s_len, 'E');
+
+ int mac_key_s2c_len = packet_receiver_->getMacKeySize();
+ unsigned char *mac_key_s2c = computeKey(mac_key_s2c_len, 'F');
+
+
+ packet_sender_->startEncryption(key_c2s, IV_c2s);
+ packet_sender_->startMac(mac_key_c2s);
+
+ packet_receiver_->startEncryption(key_s2c, IV_s2c);
+ packet_receiver_->startMac(mac_key_s2c);
+
+
+ delete[] IV_c2s;
+ delete[] IV_s2c;
+ delete[] key_c2s;
+ delete[] key_s2c;
+ delete[] mac_key_c2s;
+ delete[] mac_key_s2c;
+
+ return true;
+}
+
+unsigned char *FQTermSSH2Kex::computeKey(int expected_len, char flag) {
+ unsigned char *key = new unsigned char[expected_len + SHA_DIGEST_LENGTH];
+
+ int len = 0;
+ SHA_CTX hash;
+
+ FQTermSSHBuffer K(BN_num_bytes(bn_K_) + 5);
+ K.putSSH2BN(bn_K_);
+
+ while (len < expected_len) {
+ SHA1_Init(&hash);
+ SHA1_Update(&hash, K.data(), K.len());
+ SHA1_Update(&hash, H_, SHA_DIGEST_LENGTH);
+
+ if (len == 0) {
+ SHA1_Update(&hash, &flag, 1);
+ SHA1_Update(&hash, session_id_, SHA_DIGEST_LENGTH);
+ } else {
+ SHA1_Update(&hash, key, len);
+ }
+
+ SHA1_Final(key + len, &hash);
+ len += SHA_DIGEST_LENGTH;
+ }
+
+ return key;
+}
+
+} // namespace FQTerm
+
+#include "fqterm_ssh2_kex.moc"
diff --git a/src/protocol/internal/fqterm_ssh2_kex.h b/src/protocol/internal/fqterm_ssh2_kex.h
new file mode 100644
index 0000000..671e094
--- /dev/null
+++ b/src/protocol/internal/fqterm_ssh2_kex.h
@@ -0,0 +1,105 @@
+/***************************************************************************
+ * 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. *
+ ***************************************************************************/
+
+#ifndef FQTERM_SSH2_KEX_H
+#define FQTERM_SSH2_KEX_H
+
+#include <openssl/sha.h>
+
+#include "fqterm_ssh_kex.h"
+
+namespace FQTerm {
+
+class FQTermSSHPacketReceiver;
+class FQTermSSHPacketSender;
+
+class FQTermSSH2Kex: public FQTermSSHKex {
+ Q_OBJECT;
+private:
+ enum FQTermSSH2KexState {
+ BEFORE_KEXINIT, WAIT_REPLY, SESSIONKEY_SENT, KEYEX_OK
+ } kex_state_;
+
+ /*
+ string V_C, the client's identification string (CR and LF
+ excluded)
+ string V_S, the server's identification string (CR and LF
+ excluded)
+ string I_C, the payload of the client's SSH_MSG_KEXINIT
+ string I_S, the payload of the server's SSH_MSG_KEXINIT
+ string K_S, the host key
+ mpint e, exchange value sent by the client
+ mpint f, exchange value sent by the server
+ mpint K, the shared secret
+ */
+
+ int I_C_len_;
+ char *I_C_;
+ int I_S_len_;
+ char *I_S_;
+
+ BIGNUM *bn_x_;
+ BIGNUM *bn_e_;
+ BIGNUM *bn_g_;
+ BIGNUM *bn_p_;
+ BN_CTX *ctx_;
+
+ BIGNUM *bn_K_;
+ BIGNUM *bn_f_;
+
+ unsigned char H_[SHA_DIGEST_LENGTH];
+
+ unsigned char *session_id_;
+
+
+
+
+ bool is_first_kex_;
+
+ FQTermSSHRSA *host_key_;
+ FQTermSSHRSA *server_key_;
+
+ u_char cookie_[16];
+ int server_flag_, ciphers_, auth_;
+// u_char session_id_[16];
+ u_char session_key_[32];
+
+ void negotiateAlgorithms();
+ void exchangeKey();
+ bool verifyKey();
+ void sendNewKeys();
+ bool changeKeyAlg();
+
+ unsigned char *computeKey(int len, char flag);
+
+public:
+ FQTermSSH2Kex(const char *V_C, const char *V_S);
+ ~FQTermSSH2Kex();
+
+ virtual void initKex(FQTermSSHPacketReceiver *packetReceiver,
+ FQTermSSHPacketSender *outputSender);
+
+public slots:
+ void handlePacket(int type);
+};
+
+} // namespace FQTerm
+
+#endif //FQTERM_SSH2_KEX_H
diff --git a/src/protocol/internal/fqterm_ssh2_packet.cpp b/src/protocol/internal/fqterm_ssh2_packet.cpp
new file mode 100644
index 0000000..d3094d3
--- /dev/null
+++ b/src/protocol/internal/fqterm_ssh2_packet.cpp
@@ -0,0 +1,267 @@
+/***************************************************************************
+ * 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_trace.h"
+#include "fqterm_ssh_buffer.h"
+#include "fqterm_ssh2_packet.h"
+#include "fqterm_ssh_des.h"
+
+#include "fqterm_serialization.h"
+#include "crc32.h"
+
+namespace FQTerm {
+//==============================================================================
+//FQTermSSH2PacketSender
+//==============================================================================
+// SSH2 Packet Structure:
+// uint32 packet_length
+// byte padding_length
+// byte[n1] payload; n1 = packet_length - padding_length - 1
+// byte[n2] random padding; n2 = padding_length
+// byte[m] mac (Message Authentication Code - MAC); m = mac_length
+//==============================================================================
+
+void FQTermSSH2PacketSender::makePacket() {
+ FQ_TRACE("ssh2packet", 9) << "----------------------------Send "
+ << (is_encrypt_ ? "Encrypted": "plain")
+ << " Packet---->>>>>>>";
+
+ // 0. compress
+ if (is_compressed_) {
+ FQ_VERIFY(false);
+ }
+
+ // 1. compute the padding length for padding.
+ int non_padding_len = 4 + 1 + buffer_->len();
+
+ int padding_block_len = 8;
+ if (is_encrypt_ && cipher_->blockSize() > padding_block_len) {
+ padding_block_len = cipher_->blockSize();
+ }
+
+ int padding_len = padding_block_len - (non_padding_len % padding_block_len);
+ if (padding_len < 4) {
+ padding_len += padding_block_len;
+ }
+
+ // 2. renew the output buffer.
+ int total_len = non_padding_len + padding_len;
+ if (is_mac_) {
+ total_len += mac_->digestSize();
+ }
+
+ delete output_buffer_;
+ output_buffer_ = new FQTermSSHBuffer(total_len);
+
+ // 3. Fill the output buffer.
+ int packet_len = 1 + buffer_->len() + padding_len;
+
+ output_buffer_->putInt(packet_len);
+ output_buffer_->putByte(padding_len);
+ output_buffer_->putRawData((const char*)buffer_->data(), buffer_->len());
+
+ u_int32_t rand_val = 0;
+ for (int i = 0; i < padding_len; i++) {
+ if (i % 4 == 0) {
+ rand_val = rand(); // FIXME: rand() doesn't range from 0 to 2^32.
+ }
+ output_buffer_->putByte(rand_val & 0xff);
+ rand_val >>= 8;
+ }
+
+ // 4. Add MAC on the entire unencrypted packet,
+ // including two length fields, 'payload' and 'random padding'.
+ if (is_mac_) {
+ const unsigned char *packet = output_buffer_->data();
+ int len = output_buffer_->len();
+
+ FQTermSSHBuffer buffer(4 + len);
+ buffer.putInt(sequence_no_);
+ buffer.putRawData((const char *)packet, len);
+
+ std::vector<u_char> digest(mac_->digestSize());
+ mac_->getDigest(buffer.data(), buffer.len(), &digest[0]);
+
+ FQ_TRACE("ssh2packet", 9) << "Making packets...";
+ FQ_TRACE("ssh2packet", 9) << "Append MAC with sequence_no_" << sequence_no_;
+ FQ_TRACE("ssh2packet", 9) << "Compute MAC with "
+ << buffer.len() << " bytes data:\n"
+ << dumpHexString << std::string((char *)buffer.data(), buffer.len());
+ FQ_TRACE("ssh2packet", 9) << "MAC data "
+ << digest.size() << " bytes:\n"
+ << dumpHexString << std::string((const char *)&digest[0], digest.size());
+
+ output_buffer_->putRawData((const char *)&digest[0], digest.size());
+ }
+
+ if (is_compressed_) {
+ FQ_VERIFY(false);
+ }
+
+ if (is_encrypt_) {
+ // as RFC 4253:
+ // When encryption is in effect, the packet length, padding
+ // length, payload, and padding fields of each packet MUST be encrypted
+ // with the given algorithm.
+
+ u_char *data = output_buffer_->data();
+ int len = output_buffer_->len() - mac_->digestSize();
+
+ FQ_TRACE("ssh2packet", 9) << "An packet (without MAC) to be encrypted:"
+ << len << " bytes:\n"
+ << dumpHexString << std::string((const char *)data, len);
+
+ cipher_->encrypt(data, data, len);
+
+ FQ_TRACE("ssh2packet", 9) << "An encrypted packet (without MAC) made:"
+ << len << " bytes:\n"
+ << dumpHexString << std::string((const char *)data, len);
+ }
+
+ ++sequence_no_;
+}
+
+void FQTermSSH2PacketSender::setEncryptionType(int cipherType) {
+ cipher_type_ = cipherType;
+
+ delete cipher_;
+ cipher_ = NULL;
+
+ switch (cipher_type_) {
+ case SSH_CIPHER_3DES:
+ cipher_ = new FQTermSSH2TripleDESCBC;
+ break;
+ }
+}
+
+
+//==============================================================================
+//FQTermSSH2PacketReceiver
+//==============================================================================
+void FQTermSSH2PacketReceiver::parseData(FQTermSSHBuffer *input) {
+ FQ_TRACE("ssh2packet", 9) << "----------------------------Receive "
+ << (is_decrypt_ ? "Encrypted": "plain")
+ << " Packet----<<<<<<<";
+ while (input->len() > 0) {
+ // 1. Check the ssh packet
+ if (input->len() < 16
+ || (is_decrypt_ && input->len() < cipher_->blockSize())
+ || input->len() < last_expected_input_length_
+ ) {
+ FQ_TRACE("ssh2packet", 3)
+ << "Got an incomplete packet. Wait for more data.";
+ return ;
+ }
+
+ if (last_expected_input_length_ == 0) {
+ if (is_decrypt_) {
+ // decrypte the first block to get the packet_length field.
+ cipher_->decrypt(input->data(), input->data(), cipher_->blockSize());
+ }
+ } else {
+ // last_expected_input_length_ != 0
+ // indicates an incomplete ssh2 packet received last time,
+ // the first block of data is already decrypted at that time,
+ // so it must not be decrypted again.
+ }
+
+ int packet_len = ntohu32(input->data());
+
+ if (packet_len > SSH_BUFFER_MAX) {
+ emit packetError(tr("parseData: packet too big"));
+ return ;
+ }
+
+ int expected_input_len = 4 + packet_len + (is_mac_ ? mac_->digestSize() : 0);
+
+ if (input->len() < (long)expected_input_len) {
+ FQ_TRACE("ssh2packet", 3)
+ << "The packet is too small. Wait for more data.";
+ last_expected_input_length_ = expected_input_len;
+ return ;
+ } else {
+ last_expected_input_length_ = 0;
+ }
+
+ // 2. decrypte data.
+ if (is_decrypt_) {
+ // decrypte blocks left.
+ unsigned char *tmp = input->data() + cipher_->blockSize();
+ int left_len = expected_input_len - cipher_->blockSize() - mac_->digestSize();
+ cipher_->decrypt(tmp, tmp, left_len);
+ }
+
+ // 3. check MAC
+ if (is_mac_) {
+ int digest_len = mac_->digestSize();
+ std::vector<u_char> digest(digest_len);
+ mac_->getDigest(input->data(), expected_input_len - digest_len, &digest[0]);
+
+ u_char *received_digest = input->data() + expected_input_len - digest_len;
+
+ if (memcmp(&digest[0], received_digest, digest_len) == 0) {
+ FQ_TRACE("ssh2packet", 0) << "incorrect MAC.";
+ return ;
+ }
+ }
+
+ // 4. get every field of the ssh packet.
+ packet_len = input->getInt();
+
+ std::vector<u_char> data(packet_len);
+
+ input->getRawData((char*)&data[0], packet_len);
+ if (is_mac_) {
+ input->consume(mac_->digestSize());
+ }
+
+ int padding_len = data[0];
+
+ real_data_len_ = packet_len - 1 - padding_len;
+
+ buffer_->clear();
+ buffer_->putRawData((char*)&data[0] + 1, real_data_len_);
+
+ FQ_TRACE("ssh2packet", 9) << "Receive " << real_data_len_ << " bytes payload:\n"
+ << dumpHexString << std::string((char *)&data[0] + 1, real_data_len_);
+
+ // 5. notify others a ssh packet is parsed successfully.
+ packet_type_ = buffer_->getByte();
+ real_data_len_ -= 1;
+ emit packetAvaliable(packet_type_);
+
+ ++sequence_no_;
+ }
+}
+
+void FQTermSSH2PacketReceiver::setEncryptionType(int cipherType) {
+ cipher_type_ = cipherType;
+
+ delete cipher_;
+ cipher_ = NULL;
+
+ switch (cipher_type_) {
+ case SSH_CIPHER_3DES:
+ cipher_ = new FQTermSSH2TripleDESCBC;
+ break;
+ }
+}
+
+} // namespace FQTerm
diff --git a/src/protocol/internal/fqterm_ssh2_packet.h b/src/protocol/internal/fqterm_ssh2_packet.h
new file mode 100644
index 0000000..c460984
--- /dev/null
+++ b/src/protocol/internal/fqterm_ssh2_packet.h
@@ -0,0 +1,51 @@
+/***************************************************************************
+ * 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. *
+ ***************************************************************************/
+
+#ifndef FQTERM_SSH2_PACKET_H
+#define FQTERM_SSH2_PACKET_H
+
+#include "fqterm_ssh_packet.h"
+
+namespace FQTerm {
+
+class FQTermSSH2PacketSender: public FQTermSSHPacketSender {
+ protected:
+ virtual void makePacket();
+
+ public:
+ virtual void setEncryptionType(int cipherType);
+};
+
+class FQTermSSH2PacketReceiver: public FQTermSSHPacketReceiver {
+ private:
+ // greater than 0 if last time an incomplete ssh2 packet received.
+ int last_expected_input_length_;
+ public:
+ FQTermSSH2PacketReceiver()
+ : last_expected_input_length_(0) {
+ }
+
+ virtual void parseData(FQTermSSHBuffer *input);
+ virtual void setEncryptionType(int cipherType);
+};
+
+} // namespace FQTerm
+
+#endif // FQTERM_SSH2_PACKET
diff --git a/src/protocol/internal/fqterm_ssh_auth.cpp b/src/protocol/internal/fqterm_ssh_auth.cpp
new file mode 100644
index 0000000..d063d14
--- /dev/null
+++ b/src/protocol/internal/fqterm_ssh_auth.cpp
@@ -0,0 +1,223 @@
+/***************************************************************************
+ * 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 <QString>
+
+#include "fqterm_ssh_auth.h"
+#include "fqterm_ssh_packet.h"
+#include "fqterm_ssh_const.h"
+#include "fqterm_trace.h"
+
+namespace FQTerm {
+
+//==============================================================================
+//FQTermSSH1PasswdAuth
+//==============================================================================
+
+FQTermSSH1PasswdAuth::FQTermSSH1PasswdAuth(const char *sshuser,
+ const char *sshpasswd)
+ : FQTermSSHPasswdAuth(sshuser, sshpasswd) {
+ is_tried_ = false;
+ ssh_pw_auth_state_ = FQTermSSH1PasswdAuth::BEFORE_AUTH;
+}
+
+void FQTermSSH1PasswdAuth::initAuth(FQTermSSHPacketReceiver *packet,
+ FQTermSSHPacketSender *output) {
+ packet_receiver_ = packet;
+ packet_sender_ = output;
+ packet_receiver_->disconnect(this);
+ FQ_VERIFY(connect(packet_receiver_, SIGNAL(packetAvaliable(int)),
+ this, SLOT(handlePacket(int))));
+ packet_sender_->startPacket(SSH1_CMSG_USER);
+ user_name_.clear();
+ passwd_.clear();
+
+ if (!default_user_.isEmpty() && !default_passwd_.isEmpty()) {
+ user_name_ = default_user_;
+ passwd_ = default_passwd_;
+ }
+
+ while (user_name_.isEmpty()) {
+ bool isOK = false;
+ emit requestUserPwd(&user_name_, &passwd_, &isOK);
+ // SSHLoginDialog login(&d_user, &d_passwd);
+ if (!isOK) {
+ emit authError(tr("UserCancel"));
+ return ;
+ }
+ }
+
+ packet_sender_->putString(user_name_.toLatin1());
+ packet_sender_->write();
+ ssh_pw_auth_state_ = USER_SENT;
+ is_tried_ = false;
+}
+
+void FQTermSSH1PasswdAuth::handlePacket(int type) {
+ switch (ssh_pw_auth_state_) {
+ case BEFORE_AUTH:
+ FQ_TRACE("sshauth", 0) << "Auth: We should not be here.";
+ break;
+ case USER_SENT:
+ if (type == SSH1_SMSG_SUCCESS) {
+ ssh_pw_auth_state_ = AUTH_OK;
+ emit authOK();
+ break;
+ }
+ if (type != SSH1_SMSG_FAILURE) {
+ emit authError(tr("Strange response from server"));
+ break;
+ }
+ if (is_tried_) {
+ bool isOK = false;
+ emit requestUserPwd(&user_name_, &passwd_, &isOK);
+ // SSHLoginDialog login(&d_user, &d_passwd);
+ if (!isOK) {
+ emit authError(tr("User canceled"));
+ break;
+ }
+ is_tried_ = false;
+ }
+ packet_sender_->startPacket(SSH1_CMSG_AUTH_PASSWORD);
+ packet_sender_->putString(passwd_.toLatin1());
+ packet_sender_->write();
+ is_tried_ = true;
+ break;
+ case AUTH_OK:
+ break;
+ default:
+ return ;
+ }
+}
+
+//==============================================================================
+//FQTermSSH2PasswdAuth
+//==============================================================================
+
+FQTermSSH2PasswdAuth::FQTermSSH2PasswdAuth(const char *sshuser,
+ const char *sshpasswd)
+ : FQTermSSHPasswdAuth(sshuser, sshpasswd) {
+ is_tried_ = false;
+ ssh_pw_auth_state_ = FQTermSSH2PasswdAuth::SERVICE_ACCEPTED;
+}
+
+void FQTermSSH2PasswdAuth::initAuth(FQTermSSHPacketReceiver *packet,
+ FQTermSSHPacketSender *output) {
+ packet_receiver_ = packet;
+ packet_sender_ = output;
+ packet_receiver_->disconnect(this);
+ FQ_VERIFY(connect(packet_receiver_, SIGNAL(packetAvaliable(int)),
+ this, SLOT(handlePacket(int))));
+
+ packet_sender_->startPacket(SSH2_MSG_SERVICE_REQUEST);
+ packet_sender_->putString("ssh-userauth");
+ packet_sender_->write();
+
+ ssh_pw_auth_state_ = FQTermSSH2PasswdAuth::SERVICE_ACCEPTED;
+}
+
+void FQTermSSH2PasswdAuth::handlePacket(int type) {
+ switch (ssh_pw_auth_state_) {
+ case FQTermSSH2PasswdAuth::SERVICE_ACCEPTED:
+ sendUserPasswd();
+ ssh_pw_auth_state_ = FQTermSSH2PasswdAuth::USER_PASSWD_SENT;
+ break;
+ case FQTermSSH2PasswdAuth::USER_PASSWD_SENT:
+ if (check()) {
+ FQ_TRACE("ssh2passwdauth", 3) << "Auth OK.";
+
+ emit authOK();
+ ssh_pw_auth_state_ = FQTermSSH2PasswdAuth::AUTH_OK;
+ }
+ break;
+ case FQTermSSH2PasswdAuth::AUTH_OK:
+ break;
+ default:
+ return ;
+ }
+}
+
+bool FQTermSSH2PasswdAuth::check() {
+ switch(packet_receiver_->packetType()) {
+ case SSH2_MSG_USERAUTH_SUCCESS:
+ return true;
+ break;
+ case SSH2_MSG_USERAUTH_BANNER:
+ // TODO: just ignore banner messages.
+ break;
+ case SSH2_MSG_USERAUTH_FAILURE:
+ emit authError(tr("Authentication failed!"));
+ break;
+ default:
+ emit authError(tr("Unexpected packet"));
+ }
+
+ return false;
+}
+
+void FQTermSSH2PasswdAuth::sendUserPasswd() {
+ if (packet_receiver_->packetType() != SSH2_MSG_SERVICE_ACCEPT) {
+ emit authError(tr("Expect a SSH2_MSG_SERVICE_ACCEPT packet"));
+ return;
+ }
+
+ u_char *service_name = (u_char *)packet_receiver_->getString();
+
+ if (std::string((char *)service_name) != std::string("ssh-userauth")) {
+ emit authError(tr("Error when sending username and password."));
+ return;
+ }
+
+ delete[] service_name;
+
+ // byte SSH_MSG_USERAUTH_REQUEST
+ // string user name in ISO-10646 UTF-8 encoding [RFC3629]
+ // string service name in US-ASCII
+ // string method name in US-ASCII
+ // .... method specific fields
+ user_name_.clear();
+ passwd_.clear();
+
+ if (!default_user_.isEmpty() && !default_passwd_.isEmpty()) {
+ user_name_ = default_user_;
+ passwd_ = default_passwd_;
+ }
+
+ while (user_name_.isEmpty()) {
+ bool isOK = false;
+ emit requestUserPwd(&user_name_, &passwd_, &isOK);
+ if (!isOK) {
+ emit authError(tr("UserCancel"));
+ return ;
+ }
+ }
+
+ packet_sender_->startPacket(SSH2_MSG_USERAUTH_REQUEST);
+ packet_sender_->putString(user_name_.toLatin1());
+ packet_sender_->putString("ssh-connection");
+ packet_sender_->putString("password");
+ packet_sender_->putByte(false);
+ packet_sender_->putString(passwd_.toLatin1());
+ packet_sender_->write();
+}
+
+} // namespace FQTerm
+
+#include <fqterm_ssh_auth.moc>
diff --git a/src/protocol/internal/fqterm_ssh_auth.h b/src/protocol/internal/fqterm_ssh_auth.h
new file mode 100644
index 0000000..6152a71
--- /dev/null
+++ b/src/protocol/internal/fqterm_ssh_auth.h
@@ -0,0 +1,112 @@
+/***************************************************************************
+ * 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. *
+ ***************************************************************************/
+
+#ifndef FQTERM_SSH_AUTH_H
+#define FQTERM_SSH_AUTH_H
+
+#include <QObject>
+
+class QString;
+
+namespace FQTerm {
+
+class FQTermSSHPacketReceiver;
+class FQTermSSHPacketSender;
+
+class FQTermSSHAuth: public QObject {
+
+ Q_OBJECT;
+protected:
+ QString user_name_;
+ FQTermSSHPacketReceiver *packet_receiver_;
+ FQTermSSHPacketSender *packet_sender_;
+
+public:
+ FQTermSSHAuth(const char *sshuser = NULL)
+ : user_name_(sshuser) {
+ }
+
+ ~FQTermSSHAuth() {}
+
+ virtual void initAuth(FQTermSSHPacketReceiver *packet,
+ FQTermSSHPacketSender *output) = 0;
+public slots:
+ virtual void handlePacket(int type) = 0;
+
+signals:
+ void requestUserPwd(QString *user, QString *pwd, bool *isOK);
+ void authOK();
+ void authError(QString);
+};
+
+class FQTermSSHPasswdAuth: public FQTermSSHAuth {
+ Q_OBJECT;
+protected:
+ QString passwd_;
+ bool is_tried_;
+
+ QString default_user_;
+ QString default_passwd_;
+
+public:
+ FQTermSSHPasswdAuth(const char *sshuser, const char *sshpasswd)
+ : FQTermSSHAuth(sshuser),
+ passwd_(sshpasswd),
+ default_user_(sshuser),
+ default_passwd_(sshpasswd) {
+ }
+};
+
+class FQTermSSH1PasswdAuth: public FQTermSSHPasswdAuth {
+ Q_OBJECT;
+private:
+ enum FQTermSSH1PasswdAuthState {
+ BEFORE_AUTH, USER_SENT, PASS_SENT, AUTH_OK
+ } ssh_pw_auth_state_;
+
+public:
+ FQTermSSH1PasswdAuth(const char *sshuser, const char *sshpasswd);
+
+public slots:
+ void handlePacket(int type);
+ void initAuth(FQTermSSHPacketReceiver *packet, FQTermSSHPacketSender *output);
+};
+
+class FQTermSSH2PasswdAuth: public FQTermSSHPasswdAuth {
+ Q_OBJECT;
+private:
+ enum FQTermSSH2PasswdAuthState {
+ SERVICE_ACCEPTED, USER_PASSWD_SENT, PASS_SENT, AUTH_OK
+ } ssh_pw_auth_state_;
+
+ void sendUserPasswd();
+ bool check();
+
+public:
+ FQTermSSH2PasswdAuth(const char *sshuser, const char *sshpasswd);
+
+public slots:
+ void handlePacket(int type);
+ void initAuth(FQTermSSHPacketReceiver *packet, FQTermSSHPacketSender *output);
+};
+
+} // namespace FQTerm
+
+#endif // FQTERM_SSH_AUTH_H
diff --git a/src/protocol/internal/fqterm_ssh_buffer.cpp b/src/protocol/internal/fqterm_ssh_buffer.cpp
new file mode 100644
index 0000000..d95497a
--- /dev/null
+++ b/src/protocol/internal/fqterm_ssh_buffer.cpp
@@ -0,0 +1,302 @@
+/***************************************************************************
+ * 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 <stdlib.h>
+#include <stdio.h>
+#include <vector>
+
+#include "fqterm_trace.h"
+#include "fqterm_ssh_buffer.h"
+#include "fqterm_serialization.h"
+
+namespace FQTerm {
+//==============================================================================
+// FQTermSSHBuffer
+//==============================================================================
+
+FQTermSSHBuffer::FQTermSSHBuffer(int size) {
+ alloc_size_ = size;
+ buffer_size_ = 0;
+ offset_ = 0;
+ buffer_ = new u_char[alloc_size_];
+}
+
+FQTermSSHBuffer::~FQTermSSHBuffer() {
+ delete [] buffer_;
+ offset_ = 0;
+ buffer_size_ = 0;
+ alloc_size_ = 0;
+}
+
+void FQTermSSHBuffer::ensure(int len) {
+ if (len <= (alloc_size_ - (offset_ + buffer_size_))) {
+ return ;
+ } else {
+ alloc_size_ = buffer_size_ + len;
+ rebuffer();
+ }
+}
+
+void FQTermSSHBuffer::rebuffer() {
+ u_char *newBuffer;
+ newBuffer = new u_char[alloc_size_];
+ memset(newBuffer, 0, alloc_size_);
+ memcpy(newBuffer, buffer_ + offset_, buffer_size_);
+ delete [] buffer_;
+ buffer_ = newBuffer;
+ offset_ = 0;
+}
+
+void FQTermSSHBuffer::clear() {
+ memset(buffer_, 0, alloc_size_);
+ offset_ = 0;
+ buffer_size_ = 0;
+}
+
+void FQTermSSHBuffer::consume(int len) {
+ if (len > buffer_size_) {
+ len = buffer_size_;
+ }
+
+ offset_ += len;
+ buffer_size_ -= len;
+}
+
+void FQTermSSHBuffer::putRawData(const char *data, int len) {
+ if (len < 0) {
+ FQ_TRACE("sshbuffer", 0) << "Write data error.";
+ }
+
+ ensure(len);
+ memcpy((buffer_ + offset_ + buffer_size_), data, len);
+ buffer_size_ += len;
+}
+
+void FQTermSSHBuffer::getRawData(char *data, int len) {
+ if (len <= buffer_size_ && len >= 0) {
+ memcpy(data, buffer_ + offset_, len);
+ consume(len);
+ } else {
+ FQ_TRACE("sshbuffer", 0) << "Read too many data: " << len << " bytes.";
+ }
+}
+
+//==============================================================================
+// Store an BIGNUM in the buffer with a 2-byte msb first bit count, followed by
+// (bits+7)/8 bytes of binary data, msb first.
+//==============================================================================
+
+void FQTermSSHBuffer::putSSH1BN(BIGNUM *bignum) {
+ int bits = BN_num_bits(bignum);
+ int bin_size = (bits + 7) / 8;
+ u_char *buf = new u_char[bin_size];
+ int oi;
+ u_char msg[2];
+
+ // Get the value of in binary
+ oi = BN_bn2bin(bignum, buf);
+ if (oi != bin_size) {
+ FQ_TRACE("sshbuffer", 0) << "BN_bn2bin() failed: oi = " << oi
+ << " != bin_size." << bin_size;
+ }
+
+ // Store the number of bits in the buffer in two bytes, msb first
+ htonu16(msg, bits);
+ putRawData((char*)msg, 2);
+ // Store the binary data.
+ putRawData((char*)buf, oi);
+
+ memset(buf, 0, bin_size);
+ delete [] buf;
+}
+
+
+
+void FQTermSSHBuffer::putSSH2BN(BIGNUM *bignum) {
+ // FIXME: support negative number and add error handling.
+
+ FQ_VERIFY(!bignum->neg); // currently we don't support negative big number.
+
+ if (BN_is_zero(bignum)) {
+ this->putInt(0);
+ } else {
+ u_int bytes = BN_num_bytes(bignum) + 1;
+
+ FQ_VERIFY(bytes >= 2); // currently we don't support big numbers so small
+
+ std::vector<u_char> buf(bytes);
+ buf[0] = 0;
+
+ int bin_len = BN_bn2bin(bignum, &buf[0] + 1);
+
+ FQ_VERIFY(bin_len == (int)bytes - 1);
+
+ u_int no_high_bit = (buf[1] & 0x80) ? 0 : 1;
+
+ this->putInt(bytes - no_high_bit);
+ this->putRawData((const char *)&buf[0] + no_high_bit, bytes - no_high_bit);
+ }
+}
+
+//==============================================================================
+// Retrieves a BIGNUM from the buffer.
+//==============================================================================
+
+void FQTermSSHBuffer::getSSH1BN(BIGNUM *bignum) {
+ int bits, bytes;
+ u_char buf[2];
+ u_char *bin;
+
+ // Get the number for bits.
+ getRawData((char*)buf, 2);
+ bits = ntohu16(buf);
+ // Compute the number of binary bytes that follow.
+ bytes = (bits + 7) / 8;
+ if (bytes > 8 *1024) {
+ FQ_TRACE("sshbuffer", 0) << "Can't handle BN of size " << bytes;
+ return ;
+ }
+ if (len() < bytes) {
+ FQ_TRACE("sshbuffer", 0) << "The input buffer is too small.";
+ return ;
+ }
+ bin = data();
+ BN_bin2bn(bin, bytes, bignum);
+ consume(bytes);
+}
+
+void FQTermSSHBuffer::getSSH2BN(BIGNUM *bignum) {
+ // FIXME: support negative numbers and error handling
+ int len;
+ unsigned char *hex_data = (unsigned char *)getString(&len);
+
+ FQ_VERIFY(!(len > 0 && (hex_data[0] & 0x80))); // don't support negative numbers.
+
+ FQ_VERIFY(len < 10240); // don't support so large numbers.
+
+ BIGNUM *res = BN_bin2bn(hex_data, len, bignum);
+
+ FQ_VERIFY(res != NULL);
+
+ delete hex_data;
+}
+
+u_short FQTermSSHBuffer::getWord() {
+ u_char buf[2];
+ u_short data;
+
+ getRawData((char*)buf, 2);
+ data = ntohu16(buf);
+ return data;
+}
+
+void FQTermSSHBuffer::putWord(u_short data) {
+ u_char buf[2];
+
+ htonu16(buf, data);
+ putRawData((char*)buf, 2);
+}
+
+u_int FQTermSSHBuffer::getInt() {
+ u_char buf[4];
+ u_int data;
+ getRawData((char*)buf, 4);
+ data = ntohu32(buf);
+ return data;
+}
+
+void FQTermSSHBuffer::putInt(u_int data) {
+ u_char buf[4];
+
+ htonu32(buf, data);
+ putRawData((char*)buf, 4);
+}
+
+//==============================================================================
+// Return a character from the buffer (0-255).
+//==============================================================================
+
+u_char FQTermSSHBuffer::getByte() {
+ u_char ch;
+
+ getRawData((char*) &ch, 1);
+ return ch;
+}
+
+//==============================================================================
+// Stores a character in the buffer.
+//==============================================================================
+
+void FQTermSSHBuffer::putByte(int data) {
+ u_char ch = data;
+
+ putRawData((char*) &ch, 1);
+}
+
+//==============================================================================
+// Stores an arbitrary binary string in the buffer.
+//==============================================================================
+
+void FQTermSSHBuffer::putString(const char *str, int len) {
+ if (str == NULL) {
+ FQ_TRACE("sshbuffer", 0) << "Can't put a null pointer string.";
+ return ;
+ }
+
+ if (len < 0) {
+ len = strlen(str);
+ }
+
+ putInt(len);
+ putRawData(str, len);
+}
+
+//==============================================================================
+// Return an arbitrary binary string from the buffer. The string cannot be
+// longer than 256k. The returned value points to memory allocated with new;
+// It is the responsibility of the calling function to free the data.
+//==============================================================================
+
+void *FQTermSSHBuffer::getString(int *length) {
+ u_char *data;
+ u_int len;
+
+ // Get the length.
+ len = getInt();
+ if ((long)len > buffer_size_) {
+ FQ_TRACE("sshbuffer", 0)
+ << "String length " << len
+ << " is greater than buffer size " << buffer_size_;
+ return 0;
+ }
+ // Allocate space for the string. Add one byte for a null character.
+ data = new u_char[len + 1];
+ // Get the string.
+ getRawData((char*)data, len);
+ // Append a null character to make processing easier.
+ data[len] = 0;
+ if (length != NULL) {
+ *length = len;
+ }
+ // return the length of the string.
+ return data;
+}
+
+} // namespace FQTerm
diff --git a/src/protocol/internal/fqterm_ssh_buffer.h b/src/protocol/internal/fqterm_ssh_buffer.h
new file mode 100644
index 0000000..e06872f
--- /dev/null
+++ b/src/protocol/internal/fqterm_ssh_buffer.h
@@ -0,0 +1,83 @@
+/***************************************************************************
+ * 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. *
+ ***************************************************************************/
+
+#ifndef FQTERM_SSH_BUFFER_H
+#define FQTERM_SSH_BUFFER_H
+
+#include <openssl/bn.h>
+#include <string.h>
+
+#include "fqterm_ssh_types.h"
+
+namespace FQTerm {
+
+#define SSH_BUFFER_MAX 10000000
+
+class FQTermSSHBuffer {
+ private:
+ u_char *buffer_;
+ int offset_;
+ int buffer_size_;
+ int alloc_size_;
+
+ void ensure(int len);
+ void rebuffer();
+
+ public:
+ FQTermSSHBuffer(int size);
+ ~FQTermSSHBuffer();
+
+ u_char *data() const {
+ return buffer_ + offset_;
+ }
+
+ int len() const {
+ return buffer_size_;
+ }
+
+ void consume(int len);
+
+ void clear();
+
+ void putRawData(const char *data, int len);
+ void getRawData(char *data, int len);
+
+ void putSSH1BN(BIGNUM *bignum);
+ void getSSH1BN(BIGNUM *bignum);
+
+ void putSSH2BN(BIGNUM *bignum);
+ void getSSH2BN(BIGNUM *bignum);
+
+ void putInt(u_int data);
+ u_int getInt();
+
+ void putWord(u_short data);
+ u_short getWord();
+
+ void putByte(int data);
+ u_char getByte();
+
+ void putString(const char *str, int len = -1);
+ void *getString(int *length = NULL);
+};
+
+} // namespace FQTerm
+
+#endif //FQTERM_SSH_BUFFER
diff --git a/src/protocol/internal/fqterm_ssh_channel.cpp b/src/protocol/internal/fqterm_ssh_channel.cpp
new file mode 100644
index 0000000..38a755a
--- /dev/null
+++ b/src/protocol/internal/fqterm_ssh_channel.cpp
@@ -0,0 +1,439 @@
+/***************************************************************************
+ * 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_channel.h"
+#include "fqterm_ssh_const.h"
+#include "fqterm_ssh_packet.h"
+#include "fqterm_trace.h"
+#include <QString>
+namespace FQTerm {
+
+//==============================================================================
+//FQTermSSH1Channel
+//==============================================================================
+
+FQTermSSH1Channel::FQTermSSH1Channel()
+ : FQTermSSHChannel() {
+ service_state_ = FQTermSSH1Channel::BEGIN_SERVICE;
+}
+
+void FQTermSSH1Channel::initChannel(FQTermSSHPacketReceiver *packet,
+ FQTermSSHPacketSender *output,
+ int col, int row, const QString& termtype) {
+ packet_receiver_ = packet;
+ packet_sender_ = output;
+ packet_receiver_->disconnect(this);
+ FQ_VERIFY(connect(packet_receiver_, SIGNAL(packetAvaliable(int)), this, SLOT(handlePacket(int))));
+ packet_sender_->startPacket(SSH1_CMSG_REQUEST_PTY);
+ // pty request is of no use in BBS, but we do this
+ packet_sender_->putString(termtype.toLatin1());
+ packet_sender_->putInt(row); // FIXME: hardcoded term size.
+ packet_sender_->putInt(col);
+ packet_sender_->putInt(0);
+ packet_sender_->putInt(0);
+ packet_sender_->putByte(0);
+ packet_sender_->write();
+ service_state_ = FQTermSSH1Channel::REQPTY_SENT;
+
+ is_closed_ = false;
+}
+
+void FQTermSSH1Channel::changeTermSize(int col, int row) {
+ packet_sender_->startPacket(SSH1_CMSG_WINDOW_SIZE);
+ packet_sender_->putInt(row);
+ packet_sender_->putInt(col);
+ packet_sender_->putInt(0);
+ packet_sender_->putInt(0);
+ packet_sender_->write();
+}
+
+void FQTermSSH1Channel::sendData(const char *data, int len) {
+ packet_sender_->startPacket(SSH1_CMSG_STDIN_DATA);
+ packet_sender_->putInt(len);
+ packet_sender_->putRawData(data, len);
+ packet_sender_->write();
+}
+
+void FQTermSSH1Channel::closeConnection(const char *reason) {
+ packet_sender_->startPacket(SSH1_MSG_DISCONNECT);
+ packet_sender_->putString(reason);
+ packet_sender_->write();
+ is_closed_ = true;
+}
+
+void FQTermSSH1Channel::handlePacket(int type) {
+ switch (service_state_) {
+ case BEGIN_SERVICE:
+ FQ_TRACE("sshchannel", 0) << "Channel: We should not be here";
+ break;
+ case REQPTY_SENT:
+ if (type != SSH1_SMSG_SUCCESS) {
+ emit channelError(tr("Server refused pty allocation!"));
+ }
+ packet_sender_->startPacket(SSH1_CMSG_EXEC_SHELL);
+ packet_sender_->write();
+ emit channelOK();
+ service_state_ = FQTermSSH1Channel::SERVICE_OK;
+ //emit msg to tell window we could process input.
+ break;
+ case SERVICE_OK:
+ switch (type) {
+ case SSH1_SMSG_STDOUT_DATA:
+ case SSH1_SMSG_STDERR_DATA:
+ {
+ const char *data = (const char *)packet_receiver_->buffer_->data() + 4;
+ int len = packet_receiver_->packetDataLen() - 4;
+ emit channelReadyRead(data, len);
+ }
+ break;
+ case SSH1_SMSG_X11_OPEN:
+ case SSH1_SMSG_AGENT_OPEN:
+ case SSH1_MSG_PORT_OPEN:
+ {
+ int i = packet_receiver_->getInt();
+ packet_sender_->startPacket(SSH1_MSG_CHANNEL_OPEN_FAILURE);
+ packet_sender_->putInt(i);
+ packet_sender_->write();
+ }
+ break;
+ case SSH1_SMSG_EXIT_STATUS:
+ packet_sender_->startPacket(SSH1_CMSG_EXIT_CONFIRMATION);
+ packet_sender_->write();
+ closeConnection("end");
+ is_closed_ = true;
+ break;
+ case SSH1_SMSG_SUCCESS:
+ case SSH1_SMSG_FAILURE:
+ break;
+ default:
+ FQ_TRACE("sshchannel", 0) << "Unimplemented message: "
+ << service_state_;
+ }
+ }
+}
+
+
+//==============================================================================
+//FQTermSSH2Channel
+//==============================================================================
+u_int32_t FQTermSSH2Channel::generateChannelID() {
+ static u_int32_t id = 0;
+ return id++;
+}
+
+FQTermSSH2Channel::FQTermSSH2Channel()
+ : FQTermSSHChannel(),
+ col_(80),
+ row_(24),
+ termtype_("vt100") {
+ channel_id_ = generateChannelID();
+ server_channel_id_ = 0xCCCCCCCC;
+ local_window_size_ = 0;
+ channel_state_ = FQTermSSH2Channel::BEGIN_CHANNEL;
+}
+
+void FQTermSSH2Channel::initChannel(FQTermSSHPacketReceiver *packet,
+ FQTermSSHPacketSender *output,
+ int col, int row, const QString& termtype) {
+ FQ_FUNC_TRACE("ssh2channel", 5);
+ col_ = col;
+ row_ = row;
+ termtype_ = termtype;
+ packet_receiver_ = packet;
+ packet_sender_ = output;
+ packet_receiver_->disconnect(this);
+ FQ_VERIFY(connect(packet_receiver_, SIGNAL(packetAvaliable(int)), this, SLOT(handlePacket(int))));
+
+ // byte SSH_MSG_CHANNEL_OPEN
+ // string channel type in US-ASCII only
+ // uint32 sender channel
+ // uint32 initial window size
+ // uint32 maximum packet size
+ // .... channel type specific data follows
+
+ packet_sender_->startPacket(SSH2_MSG_CHANNEL_OPEN);
+ packet_sender_->putString("session");
+ packet_sender_->putInt(channel_id_);
+ packet_sender_->putInt(MAX_LOCAL_WINDOW_SIZE); // TODO: what's the best window size?
+ packet_sender_->putInt(MAX_LOCAL_PACKET_SIZE); // TODO: what's the best maximum packet size?
+ packet_sender_->write();
+
+ server_window_size_ = 0;
+ server_max_packet_size_ = 0;
+ local_window_size_ = MAX_LOCAL_WINDOW_SIZE;
+
+ is_closed_ = false;
+}
+
+void FQTermSSH2Channel::changeTermSize(int col, int row) {
+ // byte SSH_MSG_CHANNEL_REQUEST
+ // uint32 recipient channel
+ // string "window-change"
+ // boolean FALSE
+ // uint32 terminal width, columns
+ // uint32 terminal height, rows
+ // uint32 terminal width, pixels
+ // uint32 terminal height, pixels
+
+ packet_sender_->startPacket(SSH2_MSG_CHANNEL_REQUEST);
+ packet_sender_->putInt(server_channel_id_);
+ packet_sender_->putString("window-change");
+ packet_sender_->putByte(false);
+ packet_sender_->putInt(col);
+ packet_sender_->putInt(row);
+ packet_sender_->putInt(640); // FIXME: hard-coded screen pixels.
+ packet_sender_->putInt(480);
+ packet_sender_->write();
+}
+
+void FQTermSSH2Channel::sendData(const char *data, int len) {
+ if (len > (int)server_window_size_ || len > (int)server_max_packet_size_) {
+ FQ_TRACE("ssh2channel", 3) << "Data length is greater than server's capacity: "
+ << "data length: " << len << ", "
+ << "server window: " << server_window_size_ << ", "
+ << "server packet max size: " << server_max_packet_size_;
+ return;
+ }
+
+ // byte SSH_MSG_CHANNEL_DATA
+ // uint32 recipient channel
+ // string data
+ packet_sender_->startPacket(SSH2_MSG_CHANNEL_DATA);
+ packet_sender_->putInt(server_channel_id_);
+ packet_sender_->putString(data, len);
+ packet_sender_->write();
+
+ server_window_size_ -= len;;
+
+ FQ_TRACE("ssh2channel", 5) << len
+ << " bytes data sent, server window size left: "
+ << server_window_size_;;
+}
+
+void FQTermSSH2Channel::closeConnection(const char *reason) {
+ packet_sender_->startPacket(SSH1_MSG_DISCONNECT);
+ packet_sender_->putString(reason);
+ packet_sender_->write();
+ is_closed_ = true;
+}
+
+void FQTermSSH2Channel::requestPty() {
+ if (packet_receiver_->packetType() == SSH2_MSG_CHANNEL_OPEN_FAILURE) {
+ // TODO: Here the error reason in the packet is ignored.
+ emit channelError(tr("Server refuces to open a channel."));
+ return;
+ }
+
+ if (packet_receiver_->packetType() != SSH2_MSG_CHANNEL_OPEN_CONFIRMATION) {
+ emit channelError(tr("Server error when opening a channel."));
+ return;
+ }
+
+ FQ_TRACE("ssh2channel", 5) << "Channel open. Try to request a pty.";
+
+ // byte SSH_MSG_CHANNEL_OPEN_CONFIRMATION
+ // uint32 recipient channel
+ // uint32 sender channel
+ // uint32 initial window size
+ // uint32 maximum packet size
+ // .... channel type specific data follows
+ packet_receiver_->consume(4);
+ server_channel_id_ = packet_receiver_->getInt();
+ server_window_size_ = packet_receiver_->getInt();
+ server_max_packet_size_ = packet_receiver_->getInt();
+
+ // byte SSH_MSG_CHANNEL_REQUEST
+ // uint32 recipient channel
+ // string "pty-req"
+ // boolean want_reply
+ // string TERM environment variable value (e.g., vt100)
+ // uint32 terminal width, characters (e.g., 80)
+ // uint32 terminal height, rows (e.g., 24)
+ // uint32 terminal width, pixels (e.g., 640)
+ // uint32 terminal height, pixels (e.g., 480)
+ // string encoded terminal modes
+ packet_sender_->startPacket(SSH2_MSG_CHANNEL_REQUEST);
+ packet_sender_->putInt(server_channel_id_);
+ packet_sender_->putString("pty-req");
+ packet_sender_->putByte(true);
+ packet_sender_->putString(termtype_.toLatin1()); // TODO: hardcoded term type.
+ packet_sender_->putInt(col_); // FIXME: hardcoded screen parameters.
+ packet_sender_->putInt(row_);
+ packet_sender_->putInt(640);
+ packet_sender_->putInt(480);
+ packet_sender_->putString(""); // TODO: no modes sent.
+ packet_sender_->write();
+}
+
+
+void FQTermSSH2Channel::requestShell() {
+ if (packet_receiver_->packetType() != SSH2_MSG_CHANNEL_SUCCESS) {
+ emit channelError(tr("Server refused pty allocation!"));
+ }
+ FQ_TRACE("ssh2channel", 5) << "Pty allocated. Now try to request a shell.";
+ // byte SSH_MSG_CHANNEL_REQUEST
+ // uint32 recipient channel
+ // string "shell"
+ // boolean want reply
+ packet_sender_->startPacket(SSH2_MSG_CHANNEL_REQUEST);
+ packet_sender_->putInt(server_channel_id_);
+ packet_sender_->putString("shell");
+ packet_sender_->putByte(true);
+ packet_sender_->write();
+}
+
+void FQTermSSH2Channel::checkLocalWindowSize() {
+ if (local_window_size_ < MAX_LOCAL_WINDOW_SIZE/2) {
+ // byte SSH_MSG_CHANNEL_WINDOW_ADJUST
+ // uint32 recipient channel
+ // uint32 bytes to add
+ int inc = MAX_LOCAL_WINDOW_SIZE/2;
+ local_window_size_ += inc;
+
+ packet_sender_->startPacket(SSH2_MSG_CHANNEL_WINDOW_ADJUST);
+ packet_sender_->putInt(server_channel_id_);
+ packet_sender_->putInt(inc);
+ packet_sender_->write();
+ }
+}
+
+void FQTermSSH2Channel::processChannelPacket() {
+ switch (packet_receiver_->packetType()) {
+ case SSH2_MSG_CHANNEL_WINDOW_ADJUST:
+ {
+ // byte SSH_MSG_CHANNEL_WINDOW_ADJUST
+ // uint32 recipient channel
+ // uint32 bytes to add
+ packet_receiver_->consume(4); // channel id is already checked.
+ int inc = packet_receiver_->getInt();
+ server_window_size_ += inc;
+ FQ_TRACE("ssh2channel", 5) << "Server window size increased from "
+ << server_window_size_ - inc
+ << " to " << server_window_size_;
+ }
+ break;
+ case SSH2_MSG_CHANNEL_DATA:
+ {
+ // byte SSH_MSG_CHANNEL_DATA
+ // uint32 recipient channel
+ // string data
+ packet_receiver_->consume(4);
+ int len = packet_receiver_->getInt();
+ const char *data = (const char *)packet_receiver_->buffer_->data();
+ local_window_size_ -= len;
+ checkLocalWindowSize();
+ emit channelReadyRead(data, len);
+ }
+ break;
+ case SSH2_MSG_CHANNEL_EXTENDED_DATA:
+ {
+ // byte SSH_MSG_CHANNEL_DATA
+ // uint32 recipient channel
+ // string data
+ packet_receiver_->consume(4);
+ int len = packet_receiver_->getInt();
+ const char *data = (const char *)packet_receiver_->buffer_->data();
+ local_window_size_ -= len;
+ checkLocalWindowSize();
+ emit channelReadyRead(data, len);
+ }
+ break;
+ case SSH2_MSG_CHANNEL_EOF:
+ case SSH2_MSG_CHANNEL_CLOSE:
+ // byte SSH_MSG_CHANNEL_EOF
+ // uint32 recipient channel
+ // FIXME: this error would cause the connection closed, while only the channel need be closed in ssh2.
+ emit channelError(tr("Channel closed by the server."));
+ break;
+ case SSH2_MSG_CHANNEL_REQUEST:
+ // byte SSH_MSG_CHANNEL_REQUEST
+ // uint32 recipient channel
+ // string "xon-xoff"
+ // boolean FALSE
+ // boolean client can do
+
+ // TODO: just ignore this message currently.
+ break;
+ }
+}
+
+void FQTermSSH2Channel::handlePacket(int type) {
+ // first check the channel id.
+ u_int32_t channel_id = ntohu32(packet_receiver_->buffer_->data());
+ if (channel_id != channel_id_) {
+ return;
+ }
+
+ switch (channel_state_) {
+ case FQTermSSH2Channel::BEGIN_CHANNEL:
+ requestPty();
+ channel_state_ = FQTermSSH2Channel::REQUEST_PTY_SENT;
+ break;
+ case FQTermSSH2Channel::REQUEST_PTY_SENT:
+ requestShell();
+ channel_state_ = FQTermSSH2Channel::REQUEST_SHELL_SENT;
+ break;
+ case FQTermSSH2Channel::REQUEST_SHELL_SENT:
+ switch (type) {
+ case SSH2_MSG_CHANNEL_REQUEST:
+ {
+ FQ_TRACE("ssh2channel", 8) << "SSH2_MSG_CHANNEL_REQUEST isn't supported, just send back a packet with SSH2_MSG_CHANNEL_SUCCESS if reply is needed.";
+
+ packet_receiver_->consume(4);
+ u_int32_t len = ntohu32(packet_receiver_->buffer_->data());
+ packet_receiver_->consume(4 + len);
+ bool replyNeeded = packet_receiver_->getByte();
+
+ if (replyNeeded) {
+ packet_sender_->startPacket(SSH2_MSG_CHANNEL_SUCCESS);
+ packet_sender_->putInt(server_channel_id_);
+ packet_sender_->write();
+ }
+ }
+ break;
+ case SSH2_MSG_CHANNEL_WINDOW_ADJUST:
+ {
+ packet_receiver_->consume(4);
+ u_int32_t inc = packet_receiver_->getInt();
+ server_window_size_ += inc;
+ }
+ break;
+ case SSH2_MSG_CHANNEL_SUCCESS:
+ emit channelOK();
+ channel_state_ = FQTermSSH2Channel::CHANNEL_OK;
+ //emit a msg to tell window we could process input.
+ break;
+ case SSH2_MSG_CHANNEL_FAILURE:
+ emit channelError(tr("Can't open a shell."));
+ break;
+ default:
+ emit channelError(tr("Unsupported packet."));
+ break;
+ }
+ break;
+ case FQTermSSH2Channel::CHANNEL_OK:
+ processChannelPacket();
+ break;
+ }
+}
+
+} // namespace FQTerm
+
+#include "fqterm_ssh_channel.moc"
diff --git a/src/protocol/internal/fqterm_ssh_channel.h b/src/protocol/internal/fqterm_ssh_channel.h
new file mode 100644
index 0000000..9922528
--- /dev/null
+++ b/src/protocol/internal/fqterm_ssh_channel.h
@@ -0,0 +1,130 @@
+/***************************************************************************
+ * 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. *
+ ***************************************************************************/
+
+#ifndef FQTERM_SSH_CHANNEL_H
+#define FQTERM_SSH_CHANNEL_H
+
+#include "fqterm_ssh_types.h"
+
+#include <QObject>
+
+namespace FQTerm {
+
+class FQTermSSHPacketReceiver;
+class FQTermSSHPacketSender;
+
+class FQTermSSHChannel: public QObject {
+ Q_OBJECT;
+protected:
+ bool is_closed_;
+
+ FQTermSSHPacketReceiver *packet_receiver_;
+ FQTermSSHPacketSender *packet_sender_;
+
+public:
+ FQTermSSHChannel(){}
+ virtual ~FQTermSSHChannel(){}
+ virtual void initChannel(FQTermSSHPacketReceiver *packet,
+ FQTermSSHPacketSender *output,
+ int col, int row, const QString& termtype) = 0;
+ virtual void closeConnection(const char *reason) = 0;
+
+ // TODO: it seems this function isn't used.
+ virtual void changeTermSize(int col, int row) = 0;
+
+ virtual void sendData(const char *data, int len) = 0;
+
+public slots:
+ virtual void handlePacket(int type) = 0;
+signals:
+ void channelOK();
+ void channelReadyRead(const char *data, int len);
+ void channelError(QString);
+};
+
+class FQTermSSH1Channel: public FQTermSSHChannel {
+ Q_OBJECT;
+private:
+ enum FQTermSSH1ChannelState {
+ BEGIN_SERVICE, REQPTY_SENT,
+ //REQCMD_SENT,
+ SERVICE_OK
+ } service_state_;
+
+public:
+ FQTermSSH1Channel();
+ virtual void initChannel(FQTermSSHPacketReceiver *packet, FQTermSSHPacketSender *output,
+ int col, int row, const QString& termtype);
+ virtual void closeConnection(const char *reason);
+ virtual void changeTermSize(int col, int row);
+ virtual void sendData(const char *data, int len);
+
+public slots:
+ virtual void handlePacket(int type);
+signals:
+ void channelReadyRead(const char *data, int len);
+};
+
+
+class FQTermSSH2Channel: public FQTermSSHChannel {
+ Q_OBJECT;
+private:
+ static u_int32_t generateChannelID();
+
+private:
+ int col_;
+ int row_;
+ QString termtype_;
+ enum FQTermSSH2ChannelState {
+ BEGIN_CHANNEL, REQUEST_PTY_SENT, REQUEST_SHELL_SENT, CHANNEL_OK
+ } channel_state_;
+
+ u_int32_t channel_id_;
+
+ enum {MAX_LOCAL_WINDOW_SIZE = 0x100000, MAX_LOCAL_PACKET_SIZE = 0x4000};
+
+ u_int32_t local_window_size_;
+
+ u_int32_t server_channel_id_;
+ u_int32_t server_window_size_; // connection packet window size;
+ u_int32_t server_max_packet_size_; // max size of each packet sent to server.
+
+ void requestPty();
+ void requestShell();
+ void processChannelPacket();
+ void checkLocalWindowSize();
+
+public:
+ FQTermSSH2Channel();
+ virtual void initChannel(FQTermSSHPacketReceiver *packet, FQTermSSHPacketSender *output,
+ int col, int row, const QString& termtype);
+ virtual void closeConnection(const char *reason);
+ virtual void changeTermSize(int col, int row);
+ virtual void sendData(const char *data, int len);
+
+public slots:
+ virtual void handlePacket(int type);
+signals:
+ void channelReadyRead(const char *data, int len);
+};
+
+} // namespace FQTerm
+
+#endif // FQTERM_SSH_CHANNEL_H
diff --git a/src/protocol/internal/fqterm_ssh_cipher.h b/src/protocol/internal/fqterm_ssh_cipher.h
new file mode 100644
index 0000000..13bc9f3
--- /dev/null
+++ b/src/protocol/internal/fqterm_ssh_cipher.h
@@ -0,0 +1,58 @@
+/***************************************************************************
+ * 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. *
+ ***************************************************************************/
+
+#ifndef FQTERM_SSH_CIPHER_H
+#define FQTERM_SSH_CIPHER_H
+
+#include <stdlib.h>
+
+#include "fqterm_ssh_types.h"
+#include "fqterm_ssh_const.h"
+
+namespace FQTerm {
+
+class FQTermSSHCipher {
+protected:
+ const char *d_name;
+
+public:
+ FQTermSSHCipher() {
+ d_name = NULL;
+ }
+
+ virtual ~FQTermSSHCipher(){}
+
+ const char *name() const {
+ return d_name;
+ }
+
+ virtual int blockSize() const = 0;
+ virtual int getKeySize() const = 0;
+ virtual int getIVSize() const = 0;
+
+ virtual void setIV(const u_char *data) = 0;
+ virtual void setKey(const u_char *data) = 0;
+ virtual void encrypt(const u_char *source, u_char *dest, int len) = 0;
+ virtual void decrypt(const u_char *source, u_char *dest, int len) = 0;
+};
+
+} // namespace FQTerm
+
+#endif // FQTERM_SSH_CIPHER_H
diff --git a/src/protocol/internal/fqterm_ssh_const.h b/src/protocol/internal/fqterm_ssh_const.h
new file mode 100644
index 0000000..412760e
--- /dev/null
+++ b/src/protocol/internal/fqterm_ssh_const.h
@@ -0,0 +1,146 @@
+/***************************************************************************
+ * 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. *
+ ***************************************************************************/
+
+#ifndef FQTERMCONST_H
+#define FQTERMCONST_H
+
+namespace FQTerm {
+
+#define SSH_CIPHER_SSH2 -3
+#define SSH_CIPHER_ILLEGAL -2
+#define SSH_CIPHER_NOT_SET -1
+#define SSH_CIPHER_NONE 0
+#define SSH_CIPHER_IDEA 1
+#define SSH_CIPHER_DES 2
+#define SSH_CIPHER_3DES 3
+#define SSH_CIPHER_BROKEN_TSS 4
+#define SSH_CIPHER_BROKEN_RC4 5
+#define SSH_CIPHER_BLOWFISH 6
+#define SSH_CIPHER_RESERVED 7
+#define SSH_CIPHER_MAX 31
+
+#define SSH1_MSG_DISCONNECT 1
+#define SSH1_SMSG_PUBLIC_KEY 2
+#define SSH1_CMSG_SESSION_KEY 3
+#define SSH1_CMSG_USER 4
+#define SSH1_CMSG_AUTH_RSA 6
+#define SSH1_SMSG_AUTH_RSA_CHALLENGE 7
+#define SSH1_CMSG_AUTH_RSA_RESPONSE 8
+#define SSH1_CMSG_AUTH_PASSWORD 9
+#define SSH1_CMSG_REQUEST_PTY 10
+#define SSH1_CMSG_WINDOW_SIZE 11
+#define SSH1_CMSG_EXEC_SHELL 12
+#define SSH1_CMSG_EXEC_CMD 13
+#define SSH1_SMSG_SUCCESS 14
+#define SSH1_SMSG_FAILURE 15
+#define SSH1_CMSG_STDIN_DATA 16
+#define SSH1_SMSG_STDOUT_DATA 17
+#define SSH1_SMSG_STDERR_DATA 18
+#define SSH1_CMSG_EOF 19
+#define SSH1_SMSG_EXIT_STATUS 20
+#define SSH1_MSG_CHANNEL_OPEN_CONFIRMATION 21
+#define SSH1_MSG_CHANNEL_OPEN_FAILURE 22
+#define SSH1_MSG_CHANNEL_DATA 23
+#define SSH1_MSG_CHANNEL_CLOSE 24
+#define SSH1_MSG_CHANNEL_CLOSE_CONFIRMATION 25
+#define SSH1_SMSG_X11_OPEN 27
+#define SSH1_CMSG_PORT_FORWARD_REQUEST 28
+#define SSH1_MSG_PORT_OPEN 29
+#define SSH1_CMSG_AGENT_REQUEST_FORWARDING 30
+#define SSH1_SMSG_AGENT_OPEN 31
+#define SSH1_MSG_IGNORE 32
+#define SSH1_CMSG_EXIT_CONFIRMATION 33
+#define SSH1_CMSG_X11_REQUEST_FORWARDING 34
+#define SSH1_CMSG_AUTH_RHOSTS_RSA 35
+#define SSH1_MSG_DEBUG 36
+#define SSH1_CMSG_REQUEST_COMPRESSION 37
+#define SSH1_CMSG_AUTH_TIS 39
+#define SSH1_SMSG_AUTH_TIS_CHALLENGE 40
+#define SSH1_CMSG_AUTH_TIS_RESPONSE 41
+#define SSH1_CMSG_AUTH_CCARD 70
+#define SSH1_SMSG_AUTH_CCARD_CHALLENGE 71
+#define SSH1_CMSG_AUTH_CCARD_RESPONSE 72
+#define SSH1_AUTH_TIS 5
+#define SSH1_PROTOFLAG_SCREEN_NUMBER 1
+#define SSH1_PROTOFLAGS_SUPPORTED 0
+
+
+#define SSH2_MSG_DISCONNECT 1
+#define SSH2_MSG_IGNORE 2
+#define SSH2_MSG_UNIMPLEMENTED 3
+#define SSH2_MSG_DEBUG 4
+#define SSH2_MSG_SERVICE_REQUEST 5
+#define SSH2_MSG_SERVICE_ACCEPT 6
+#define SSH2_MSG_KEXINIT 20
+#define SSH2_MSG_NEWKEYS 21
+#define SSH2_MSG_KEXDH_INIT 30
+#define SSH2_MSG_KEXDH_REPLY 31
+#define SSH2_MSG_KEX_DH_GEX_REQUEST_OLD 30
+#define SSH2_MSG_KEX_DH_GEX_GROUP 31
+#define SSH2_MSG_KEX_DH_GEX_INIT 32
+#define SSH2_MSG_KEX_DH_GEX_REPLY 33
+#define SSH2_MSG_KEX_DH_GEX_REQUEST 34
+#define SSH2_MSG_USERAUTH_REQUEST 50
+#define SSH2_MSG_USERAUTH_FAILURE 51
+#define SSH2_MSG_USERAUTH_SUCCESS 52
+#define SSH2_MSG_USERAUTH_BANNER 53
+#define SSH2_MSG_USERAUTH_PK_OK 60
+#define SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ 60
+#define SSH2_MSG_USERAUTH_INFO_REQUEST 60
+#define SSH2_MSG_USERAUTH_INFO_RESPONSE 61
+#define SSH2_MSG_GLOBAL_REQUEST 80
+#define SSH2_MSG_REQUEST_SUCCESS 81
+#define SSH2_MSG_REQUEST_FAILURE 82
+#define SSH2_MSG_CHANNEL_OPEN 90
+#define SSH2_MSG_CHANNEL_OPEN_CONFIRMATION 91
+#define SSH2_MSG_CHANNEL_OPEN_FAILURE 92
+#define SSH2_MSG_CHANNEL_WINDOW_ADJUST 93
+#define SSH2_MSG_CHANNEL_DATA 94
+#define SSH2_MSG_CHANNEL_EXTENDED_DATA 95
+#define SSH2_MSG_CHANNEL_EOF 96
+#define SSH2_MSG_CHANNEL_CLOSE 97
+#define SSH2_MSG_CHANNEL_REQUEST 98
+#define SSH2_MSG_CHANNEL_SUCCESS 99
+#define SSH2_MSG_CHANNEL_FAILURE 100
+#define SSH2_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT 1
+#define SSH2_DISCONNECT_PROTOCOL_ERROR 2
+#define SSH2_DISCONNECT_KEY_EXCHANGE_FAILED 3
+#define SSH2_DISCONNECT_HOST_AUTHENTICATION_FAILED 4
+#define SSH2_DISCONNECT_RESERVED 4
+#define SSH2_DISCONNECT_MAC_ERROR 5
+#define SSH2_DISCONNECT_COMPRESSION_ERROR 6
+#define SSH2_DISCONNECT_SERVICE_NOT_AVAILABLE 7
+#define SSH2_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED 8
+#define SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE 9
+#define SSH2_DISCONNECT_CONNECTION_LOST 10
+#define SSH2_DISCONNECT_BY_APPLICATION 11
+#define SSH2_DISCONNECT_TOO_MANY_CONNECTIONS 12
+#define SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER 13
+#define SSH2_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE 14
+#define SSH2_DISCONNECT_ILLEGAL_USER_NAME 15
+#define SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED 1
+#define SSH2_OPEN_CONNECT_FAILED 2
+#define SSH2_OPEN_UNKNOWN_CHANNEL_TYPE 3
+#define SSH2_OPEN_RESOURCE_SHORTAGE 4
+#define SSH2_EXTENDED_DATA_STDERR 1
+
+} // namespace FQTerm
+
+#endif // FQTERMCONST_H
diff --git a/src/protocol/internal/fqterm_ssh_des.cpp b/src/protocol/internal/fqterm_ssh_des.cpp
new file mode 100644
index 0000000..603b6fe
--- /dev/null
+++ b/src/protocol/internal/fqterm_ssh_des.cpp
@@ -0,0 +1,180 @@
+/***************************************************************************
+ * 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 <memory.h>
+#include <openssl/des.h>
+#include <openssl/objects.h>
+#include <openssl/evp.h>
+
+#include "fqterm_ssh_const.h"
+#include "fqterm_ssh_des.h"
+#include "fqterm_trace.h"
+
+namespace FQTerm {
+
+#define DES_ENCRYPT 1
+#define DES_DECRYPT 0
+
+//==============================================================================
+//FQTermSSH1DES3
+//==============================================================================
+
+FQTermSSH1DES3::FQTermSSH1DES3() {
+ d_name = "des3-cbc";
+
+ memset(d_IV1, 0, sizeof(d_IV1));
+ memset(d_IV2, 0, sizeof(d_IV2));
+ memset(d_IV3, 0, sizeof(d_IV3));
+}
+
+int FQTermSSH1DES3::blockSize() const {
+ return 8;
+}
+
+void FQTermSSH1DES3::decrypt(const u_char *source, u_char *dest, int len) {
+ DES_ncbc_encrypt(source, dest, len, &d_key3, &d_IV3, DES_DECRYPT);
+ DES_ncbc_encrypt(dest, dest, len, &d_key2, &d_IV2, DES_ENCRYPT);
+ DES_ncbc_encrypt(dest, dest, len, &d_key1, &d_IV1, DES_DECRYPT);
+}
+
+void FQTermSSH1DES3::encrypt(const u_char *source, u_char *dest, int len) {
+ DES_ncbc_encrypt(source, dest, len, &d_key1, &d_IV1, DES_ENCRYPT);
+ DES_ncbc_encrypt(dest, dest, len, &d_key2, &d_IV2, DES_DECRYPT);
+ DES_ncbc_encrypt(dest, dest, len, &d_key3, &d_IV3, DES_ENCRYPT);
+}
+
+int FQTermSSH1DES3::getKeySize() const {
+ return 3*DES_KEY_SZ;
+}
+
+int FQTermSSH1DES3::getIVSize() const {
+ return 0;
+}
+
+void FQTermSSH1DES3::setIV(const u_char *data) {
+ memset(d_IV1, 0, sizeof(d_IV1));
+ memset(d_IV2, 0, sizeof(d_IV2));
+ memset(d_IV3, 0, sizeof(d_IV3));
+}
+
+void FQTermSSH1DES3::setKey(const u_char *data) {
+ DES_cblock key;
+ memset(key, 0, sizeof(key));
+ memcpy(key, data, sizeof(key));
+ DES_set_key(&key, &d_key1);
+ data += 8;
+ memset(key, 0, sizeof(key));
+ memcpy(key, data, sizeof(key));
+ DES_set_key(&key, &d_key2);
+ data += 8;
+ memset(key, 0, sizeof(key));
+ memcpy(key, data, sizeof(key));
+ DES_set_key(&key, &d_key3);
+}
+
+
+//==================================================
+// TripleDES-CBC
+//==================================================
+
+FQTermSSH2TripleDESCBC::FQTermSSH2TripleDESCBC() {
+ d_name = "des3-cbc";
+ ctx_ = NULL;
+}
+
+FQTermSSH2TripleDESCBC::~FQTermSSH2TripleDESCBC() {
+ if (ctx_ != NULL) {
+ EVP_CIPHER_CTX_cleanup(ctx_);
+ delete ctx_;
+ }
+}
+
+
+int FQTermSSH2TripleDESCBC::blockSize() const {
+ return 8;
+}
+
+int FQTermSSH2TripleDESCBC::getKeySize() const {
+ return 24;
+}
+
+int FQTermSSH2TripleDESCBC::getIVSize() const {
+ return 8;
+}
+
+void FQTermSSH2TripleDESCBC::setIV(const u_char *data) {
+ memcpy(IV_, data, getIVSize());
+}
+
+void FQTermSSH2TripleDESCBC::setKey(const u_char *data) {
+ memcpy(key_, data, getKeySize());
+}
+
+void FQTermSSH2TripleDESCBC::encrypt(const u_char *source, u_char *dest, int len) {
+ FQ_TRACE("3DES_CBC", 9) << "Start encrypting";
+ FQ_TRACE("3DES_CBC", 9) << "data len:" << len;
+ FQ_TRACE("3DES_CBC", 9) << "Source: \n" << dumpHexString << std::string((char *)source, len);
+
+ int ret = 0;
+ if (ctx_ == NULL) {
+ // Lazy initialization.
+ ctx_ = new EVP_CIPHER_CTX;
+ EVP_CIPHER_CTX_init(ctx_);
+ ret = EVP_CipherInit(ctx_, EVP_des_ede3_cbc(), key_, IV_, 1);
+ FQ_VERIFY(ret == 1);
+ }
+
+ ret = EVP_Cipher(ctx_, dest, source, len);
+
+ FQ_VERIFY(ret == 1);
+
+ FQ_TRACE("3DES_CBC", 9) << "Dest: \n" << dumpHexString << std::string((char *)dest, len);
+
+ FQ_TRACE("3DES_CBC", 9) << "IV:\n" << dumpHexString << std::string((char *)IV_, getIVSize());
+ FQ_TRACE("3DES_CBC", 9) << "key:\n" << dumpHexString << std::string((char *)key_, getKeySize());
+
+}
+
+void FQTermSSH2TripleDESCBC::decrypt(const u_char *source, u_char *dest, int len) {
+ FQ_TRACE("3DES_CBC", 9) << "Start dencrypting";
+ FQ_TRACE("3DES_CBC", 9) << "data len:" << len;
+ FQ_TRACE("3DES_CBC", 9) << "Source: \n" << dumpHexString << std::string((char *)source, len);
+
+ int ret = 0;
+ if (ctx_ == NULL) {
+ // Lazy initialization.
+ ctx_ = new EVP_CIPHER_CTX;
+ EVP_CIPHER_CTX_init(ctx_);
+ ret = EVP_CipherInit(ctx_, EVP_des_ede3_cbc(), key_, IV_, 0);
+ FQ_VERIFY(ret == 1);
+ }
+
+ ret = EVP_Cipher(ctx_, dest, source, len);
+
+ FQ_VERIFY(ret == 1);
+
+ FQ_TRACE("3DES_CBC", 9) << "Dest: \n" << dumpHexString << std::string((char *)dest, len);
+
+ FQ_TRACE("3DES_CBC", 9) << "IV:\n" << dumpHexString << std::string((char *)IV_, getIVSize());
+ FQ_TRACE("3DES_CBC", 9) << "key:\n" << dumpHexString << std::string((char *)key_, getKeySize());
+}
+
+
+} // namespace FQTerm
diff --git a/src/protocol/internal/fqterm_ssh_des.h b/src/protocol/internal/fqterm_ssh_des.h
new file mode 100644
index 0000000..ba153f2
--- /dev/null
+++ b/src/protocol/internal/fqterm_ssh_des.h
@@ -0,0 +1,74 @@
+/***************************************************************************
+ * 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. *
+ ***************************************************************************/
+
+#ifndef FQTERM_SSH_DES_H
+#define FQTERM_SSH_DES_H
+
+#include <openssl/ssl.h>
+#include <openssl/des.h>
+
+#include "fqterm_ssh_cipher.h"
+#include "fqterm_ssh_types.h"
+
+namespace FQTerm {
+
+class FQTermSSH1DES3: public FQTermSSHCipher {
+ private:
+ DES_cblock d_IV1;
+ DES_cblock d_IV2;
+ DES_cblock d_IV3;
+ DES_key_schedule d_key1;
+ DES_key_schedule d_key2;
+ DES_key_schedule d_key3;
+ public:
+ FQTermSSH1DES3();
+
+ virtual int blockSize() const;
+ virtual int getKeySize() const;
+ virtual int getIVSize() const;
+ virtual void setIV(const u_char *data);
+ virtual void setKey(const u_char *data);
+ virtual void encrypt(const u_char *source, u_char *dest, int len);
+ virtual void decrypt(const u_char *source, u_char *dest, int len);
+};
+
+class FQTermSSH2TripleDESCBC: public FQTermSSHCipher {
+ private:
+ unsigned char IV_[8];
+ unsigned char key_[24];
+
+ EVP_CIPHER_CTX *ctx_;
+
+ public:
+ FQTermSSH2TripleDESCBC();
+ ~FQTermSSH2TripleDESCBC();
+
+ virtual int blockSize() const;
+ virtual int getKeySize() const;
+ virtual int getIVSize() const;
+ virtual void setIV(const u_char *data);
+ virtual void setKey(const u_char *data);
+ virtual void encrypt(const u_char *source, u_char *dest, int len);
+ virtual void decrypt(const u_char *source, u_char *dest, int len);
+};
+
+} // namespace FQTerm
+
+#endif // FQTERM_SSH_DES_H
diff --git a/src/protocol/internal/fqterm_ssh_hash.h b/src/protocol/internal/fqterm_ssh_hash.h
new file mode 100644
index 0000000..b4679f4
--- /dev/null
+++ b/src/protocol/internal/fqterm_ssh_hash.h
@@ -0,0 +1,47 @@
+/***************************************************************************
+ * 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. *
+ ***************************************************************************/
+
+#ifndef FQTERM_SSH_HASH_H
+#define FQTERM_SSH_HASH_H
+
+#include "fqterm_ssh_types.h"
+
+namespace FQTerm {
+
+class FQTermSSHHash {
+protected:
+ int d_digestLength;
+ int digestLength()const {
+ return d_digestLength;
+ }
+
+public:
+ FQTermSSHHash() {
+ d_digestLength = 0;
+ }
+ virtual ~FQTermSSHHash() {}
+
+ virtual void update(u_char *data, int len) = 0;
+ virtual void final(u_char *data) = 0;
+};
+
+} // namespace FQTerm
+
+#endif //FQTERM_SSH_HASH_H
diff --git a/src/protocol/internal/fqterm_ssh_kex.cpp b/src/protocol/internal/fqterm_ssh_kex.cpp
new file mode 100644
index 0000000..d27bf14
--- /dev/null
+++ b/src/protocol/internal/fqterm_ssh_kex.cpp
@@ -0,0 +1,210 @@
+/***************************************************************************
+ * 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_kex.h"
+#include "fqterm_ssh_md5.h"
+#include "fqterm_trace.h"
+
+namespace FQTerm {
+
+FQTermSSHKex::FQTermSSHKex(const char *V_C, const char *V_S) {
+ V_C_ = new char[strlen(V_C) + 1];
+ V_S_ = new char[strlen(V_S) + 1];
+
+ strcpy(V_C_, V_C);
+ strcpy(V_S_, V_S);
+}
+
+FQTermSSHKex::~FQTermSSHKex() {
+ delete[] V_C_;
+ delete[] V_S_;
+}
+
+
+//==============================================================================
+//FQTermSSH1Kex
+//==============================================================================
+
+FQTermSSH1Kex::FQTermSSH1Kex(const char *V_C, const char *V_S)
+ : FQTermSSHKex(V_C, V_S) {
+ is_first_kex_ = true;
+ kex_state_ = FQTermSSH1Kex::BEFORE_PUBLICKEY;
+}
+
+FQTermSSH1Kex::~FQTermSSH1Kex(){}
+
+void FQTermSSH1Kex::initKex(FQTermSSHPacketReceiver *packetReceiver,
+ FQTermSSHPacketSender *packetSender) {
+ packet_receiver_ = packetReceiver;
+ packet_sender_ = packetSender;
+ packet_receiver_->disconnect(this);
+ FQ_VERIFY(connect(packet_receiver_, SIGNAL(packetAvaliable(int)),
+ this, SLOT(handlePacket(int))));
+ kex_state_ = FQTermSSH1Kex::BEFORE_PUBLICKEY;
+ emit reKex();
+}
+
+void FQTermSSH1Kex::handlePacket(int type) {
+ switch (kex_state_) {
+ case FQTermSSH1Kex::BEFORE_PUBLICKEY:
+ makeSessionKey();
+ kex_state_ = FQTermSSH1Kex::SESSIONKEY_SENT;
+ break;
+ case FQTermSSH1Kex::SESSIONKEY_SENT:
+ if (type != SSH1_SMSG_SUCCESS) {
+ emit kexError(tr("Kex exchange failed!"));
+ break;
+ }
+ emit kexOK();
+ kex_state_ = FQTermSSH1Kex::KEYEX_OK;
+ break;
+ case FQTermSSH1Kex::KEYEX_OK:
+ break;
+ }
+}
+
+void FQTermSSH1Kex::makeSessionKey() {
+ int i;
+ BIGNUM *key;
+ u_int32_t rand_val;
+ int bits;
+ int rbits;
+
+ if (packet_receiver_->packetType() != SSH1_SMSG_PUBLIC_KEY) {
+ emit kexError(tr("startKex: First packet is not public key"));
+ return ;
+ }
+ packet_receiver_->getRawData((char*)cookie_, 8);
+
+ // Get the public key.
+ server_key_ = new FQTermSSHRSA;
+ bits = packet_receiver_->getInt();
+ packet_receiver_->getBN(server_key_->d_rsa->e);
+ packet_receiver_->getBN(server_key_->d_rsa->n);
+
+ rbits = BN_num_bits(server_key_->d_rsa->n);
+ if (bits != rbits) {
+ FQ_TRACE("sshkex", 0) << "Warning: Server lies about "
+ << "size of server public key: "
+ << "actual size: " << rbits
+ << " vs. anounced: " << bits;
+ FQ_TRACE("sshkex", 0) << "Warning: This may be due to "
+ << "an old implementation of ssh.";
+ }
+
+ // Get the host key.
+ host_key_ = new FQTermSSHRSA;
+ bits = packet_receiver_->getInt();
+ packet_receiver_->getBN(host_key_->d_rsa->e);
+ packet_receiver_->getBN(host_key_->d_rsa->n);
+
+ rbits = BN_num_bits(host_key_->d_rsa->n);
+ if (bits != rbits) {
+ FQ_TRACE("sshkex", 0) << "Warning: Server lies about "
+ << "size of server public key: "
+ << "actual size: " << rbits
+ << " vs. anounced: " << bits;
+ FQ_TRACE("sshkex", 0) << "Warning: This may be due to "
+ << "an old implementation of ssh.";
+ }
+
+ // Get protocol flags.
+ server_flag_ = packet_receiver_->getInt();
+ ciphers_ = packet_receiver_->getInt();
+ auth_ = packet_receiver_->getInt();
+
+ if ((ciphers_ &(1 << SSH_CIPHER_3DES)) == 0) {
+ FQ_VERIFY(false); // server do not support my cipher
+ }
+
+ makeSessionId();
+
+ // Generate an encryption key for the session. The key is a 256 bit
+ // random number, interpreted as a 32-byte key, with the least
+ // significant 8 bits being the first byte of the key.
+
+ for (i = 0; i < 32; i++) {
+ if (i % 4 == 0) {
+ rand_val = rand();
+ }
+
+ session_key_[i] = (rand_val &0xff);
+ rand_val >>= 8;
+ }
+
+ key = BN_new();
+
+ BN_set_word(key, 0);
+ for (i = 0; i < 32; i++) {
+ BN_lshift(key, key, 8);
+ if (i < 16) {
+ BN_add_word(key, session_key_[i] ^ session_id_[i]);
+ } else {
+ BN_add_word(key, session_key_[i]);
+ }
+ }
+
+ if (BN_cmp(server_key_->d_rsa->n, host_key_->d_rsa->n) < 0) {
+ server_key_->publicEncrypt(key, key);
+ host_key_->publicEncrypt(key, key);
+ } else {
+ host_key_->publicEncrypt(key, key);
+ server_key_->publicEncrypt(key, key);
+ }
+
+ delete host_key_;
+ delete server_key_;
+
+ packet_sender_->startPacket(SSH1_CMSG_SESSION_KEY);
+ packet_sender_->putByte(SSH_CIPHER_3DES);
+ packet_sender_->putRawData((const char*)cookie_, 8);
+ packet_sender_->putBN(key);
+
+ BN_free(key);
+
+ packet_sender_->putInt(1);
+ packet_sender_->write();
+
+ emit startEncryption(session_key_);
+}
+
+void FQTermSSH1Kex::makeSessionId() {
+ u_char *p;
+ FQTermSSHMD5 *md5;
+ int servlen, hostlen;
+
+ md5 = new FQTermSSHMD5;
+ servlen = BN_num_bytes(server_key_->d_rsa->n);
+ hostlen = BN_num_bytes(host_key_->d_rsa->n);
+
+ p = new u_char[servlen + hostlen];
+
+ BN_bn2bin(host_key_->d_rsa->n, p);
+ BN_bn2bin(server_key_->d_rsa->n, p + hostlen);
+ md5->update(p, servlen + hostlen);
+ md5->update(cookie_, 8);
+ md5->final(session_id_);
+ delete md5;
+ delete [] p;
+}
+
+} // namespace FQTerm
+
+#include "fqterm_ssh_kex.moc"
diff --git a/src/protocol/internal/fqterm_ssh_kex.h b/src/protocol/internal/fqterm_ssh_kex.h
new file mode 100644
index 0000000..b2f9c56
--- /dev/null
+++ b/src/protocol/internal/fqterm_ssh_kex.h
@@ -0,0 +1,100 @@
+/***************************************************************************
+ * 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. *
+ ***************************************************************************/
+
+#ifndef FQTERM_SSH_KEX_H
+#define FQTERM_SSH_KEX_H
+
+#include <openssl/bn.h>
+#include <QObject>
+
+#include "fqterm_ssh_types.h"
+#include "fqterm_ssh_rsa.h"
+#include "fqterm_ssh_packet.h"
+#include "fqterm_ssh_const.h"
+
+namespace FQTerm {
+
+class FQTermSSHPacketReceiver;
+class FQTermSSHPacketSender;
+
+class FQTermSSHKex: public QObject {
+ Q_OBJECT;
+protected:
+ FQTermSSHPacketReceiver *packet_receiver_;
+ FQTermSSHPacketSender *packet_sender_;
+
+ char *V_C_;
+ char *V_S_;
+public:
+ // Init with
+ // string V_C, the client's identification string (CR and LF
+ // excluded)
+ // string V_S, the server's identification string (CR and LF
+ // excluded)
+ FQTermSSHKex(const char *V_C, const char *V_S);
+ virtual ~FQTermSSHKex();
+
+ virtual void initKex(FQTermSSHPacketReceiver *packetReceiver,
+ FQTermSSHPacketSender *outputSender) = 0;
+
+public slots:
+ virtual void handlePacket(int type) = 0;
+
+signals:
+ void kexOK();
+ void reKex();
+ void kexError(QString);
+
+ void startEncryption(const u_char *sessionkey);
+
+};
+
+class FQTermSSH1Kex: public FQTermSSHKex {
+ Q_OBJECT;
+private:
+ enum FQTermSSH1KexState {
+ BEFORE_PUBLICKEY, SESSIONKEY_SENT, KEYEX_OK
+ } kex_state_;
+
+ bool is_first_kex_;
+
+ FQTermSSHRSA *host_key_;
+ FQTermSSHRSA *server_key_;
+
+ u_char cookie_[8];
+ int server_flag_, ciphers_, auth_;
+ u_char session_id_[16];
+ u_char session_key_[32];
+
+ void makeSessionId();
+ void makeSessionKey();
+
+public:
+ FQTermSSH1Kex(const char *V_C, const char *V_S);
+ ~FQTermSSH1Kex();
+ virtual void initKex(FQTermSSHPacketReceiver *packetReceiver,
+ FQTermSSHPacketSender *outputSender);
+
+ virtual void handlePacket(int type);
+};
+
+} // namespace FQTerm
+
+#endif //FQTERM_SSH_KEX_H
diff --git a/src/protocol/internal/fqterm_ssh_mac.cpp b/src/protocol/internal/fqterm_ssh_mac.cpp
new file mode 100644
index 0000000..e2d345f
--- /dev/null
+++ b/src/protocol/internal/fqterm_ssh_mac.cpp
@@ -0,0 +1,53 @@
+/***************************************************************************
+ * 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 <openssl/evp.h>
+#include <openssl/hmac.h>
+
+#include "fqterm_trace.h"
+#include "fqterm_ssh_mac.h"
+#include "fqterm_ssh_packet.h"
+
+namespace FQTerm {
+
+void FQTermSSHSHA1::setKey(const unsigned char *key) {
+ memcpy(key_, key, keySize());
+}
+
+int FQTermSSHSHA1::keySize() const {
+ return SHA_DIGEST_LENGTH;
+}
+
+int FQTermSSHSHA1::digestSize() const {
+ return SHA_DIGEST_LENGTH;
+}
+
+void FQTermSSHSHA1::getDigest(const unsigned char *data, int len, unsigned char *digest) const {
+ unsigned int tmp;
+ HMAC(EVP_sha1(), key_, SHA_DIGEST_LENGTH, data, len, digest, &tmp);
+
+ FQ_TRACE("SHA1", 9) << "Key: \n" << dumpHexString << std::string((char *)key_, SHA_DIGEST_LENGTH);
+ FQ_TRACE("SHA1", 9) << "data len:" << len;
+ FQ_TRACE("SHA1", 9) << "data:\n" << dumpHexString << std::string((char *)data, len);
+ FQ_TRACE("SHA1", 9) << "digest len:" << tmp;
+ FQ_TRACE("SHA1", 9) << "digest:\n" << dumpHexString << std::string((char *)digest, tmp);
+}
+
+} // namespace FQTerm
diff --git a/src/protocol/internal/fqterm_ssh_mac.h b/src/protocol/internal/fqterm_ssh_mac.h
new file mode 100644
index 0000000..545909c
--- /dev/null
+++ b/src/protocol/internal/fqterm_ssh_mac.h
@@ -0,0 +1,54 @@
+/***************************************************************************
+ * 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. *
+ ***************************************************************************/
+
+#ifndef FQTERM_SSH_MAC_H
+#define FQTERM_SSH_MAC_H
+
+#include <openssl/sha.h>
+
+namespace FQTerm {
+
+class FQTermSSHMac {
+public:
+ FQTermSSHMac() {}
+ virtual ~FQTermSSHMac() {}
+ virtual void setKey(const unsigned char *key) = 0;
+ virtual int keySize() const = 0;
+ virtual int digestSize() const = 0;
+ virtual void getDigest(const unsigned char *data, int len, unsigned char *digest) const = 0;
+};
+
+const int FQTERM_SSH_MAC_NONE = 0;
+const int FQTERM_SSH_HMAC_SHA1 = 1;
+
+class FQTermSSHSHA1: public FQTermSSHMac {
+ unsigned char key_[SHA_DIGEST_LENGTH];
+
+public:
+ virtual void setKey(const unsigned char *key);
+ virtual int keySize() const;
+ virtual int digestSize() const;
+ virtual void getDigest(const unsigned char *data, int len, unsigned char *digest) const;
+};
+
+
+} // namespace FQTerm
+
+#endif // FQTERM_SSH_MAC_H
diff --git a/src/protocol/internal/fqterm_ssh_md5.cpp b/src/protocol/internal/fqterm_ssh_md5.cpp
new file mode 100644
index 0000000..10713e9
--- /dev/null
+++ b/src/protocol/internal/fqterm_ssh_md5.cpp
@@ -0,0 +1,36 @@
+/***************************************************************************
+ * 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_md5.h"
+
+namespace FQTerm {
+
+//==============================================================================
+//FQTermSSHMD5
+//==============================================================================
+void FQTermSSHMD5::final(u_char *digest) {
+ MD5_Final(digest, &d_md5);
+}
+
+void FQTermSSHMD5::update(u_char *data, int len) {
+ MD5_Update(&d_md5, data, len);
+}
+
+} // namespace FQTerm
diff --git a/src/protocol/internal/fqterm_ssh_md5.h b/src/protocol/internal/fqterm_ssh_md5.h
new file mode 100644
index 0000000..abb8127
--- /dev/null
+++ b/src/protocol/internal/fqterm_ssh_md5.h
@@ -0,0 +1,50 @@
+/***************************************************************************
+ * 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. *
+ ***************************************************************************/
+
+#ifndef FQTERM_SSH_MD5_H
+#define FQTERM_SSH_MD5_H
+
+#include <stdio.h>
+
+#include <openssl/md5.h>
+
+#include "fqterm_ssh_types.h"
+#include "fqterm_ssh_hash.h"
+
+namespace FQTerm {
+
+class FQTermSSHMD5: public FQTermSSHHash {
+protected:
+ MD5_CTX d_md5;
+public:
+ FQTermSSHMD5()
+ : FQTermSSHHash() {
+ d_digestLength = 16;
+ MD5_Init(&d_md5);
+ }
+ virtual ~FQTermSSHMD5() {}
+
+ void update(u_char *data, int len);
+ void final(u_char *digest);
+};
+
+} // namespace FQTerm
+
+#endif //FQTERM_SSH_MD5_H
diff --git a/src/protocol/internal/fqterm_ssh_packet.cpp b/src/protocol/internal/fqterm_ssh_packet.cpp
new file mode 100644
index 0000000..9c4ff6c
--- /dev/null
+++ b/src/protocol/internal/fqterm_ssh_packet.cpp
@@ -0,0 +1,242 @@
+/***************************************************************************
+ * 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_trace.h"
+#include "fqterm_ssh_buffer.h"
+#include "fqterm_ssh_packet.h"
+#include "fqterm_ssh_des.h"
+
+#include "fqterm_serialization.h"
+#include "crc32.h"
+
+namespace FQTerm {
+
+//==============================================================================
+//FQTermSSHPacketSender
+//==============================================================================
+
+FQTermSSHPacketSender::FQTermSSHPacketSender() {
+ buffer_ = new FQTermSSHBuffer(1024);
+ output_buffer_ = new FQTermSSHBuffer(1024);
+
+ is_encrypt_ = false;
+ cipher_type_ = SSH_CIPHER_NONE;
+ cipher_ = NULL;
+ setEncryptionType(SSH_CIPHER_3DES);
+
+ is_mac_ = false;
+ mac_type_ = FQTERM_SSH_MAC_NONE;
+ mac_ = NULL;
+
+ is_compressed_ = false;
+
+ sequence_no_ = 0;
+}
+
+FQTermSSHPacketSender::~FQTermSSHPacketSender() {
+ delete buffer_;
+ delete output_buffer_;
+ if (is_encrypt_) {
+ delete cipher_;
+ }
+}
+
+void FQTermSSHPacketSender::putRawData(const char *data, int len) {
+ buffer_->putRawData(data, len);
+}
+
+void FQTermSSHPacketSender::putByte(int data) {
+ buffer_->putByte(data);
+}
+
+void FQTermSSHPacketSender::putInt(u_int data) {
+ buffer_->putInt(data);
+}
+
+void FQTermSSHPacketSender::putString(const char *string, int len) {
+ buffer_->putString(string, len);
+}
+
+void FQTermSSHPacketSender::putBN(BIGNUM *bn) {
+ buffer_->putSSH1BN(bn);
+}
+
+void FQTermSSHPacketSender::putBN2(BIGNUM *bn) {
+ buffer_->putSSH2BN(bn);
+}
+
+void FQTermSSHPacketSender::startPacket(int pkt_type) {
+ buffer_->clear();
+ buffer_->putByte(pkt_type);
+}
+
+void FQTermSSHPacketSender::write() {
+ makePacket();
+ emit dataToWrite();
+}
+
+void FQTermSSHPacketSender::setEncryptionType(int cipherType) {
+ cipher_type_ = cipherType;
+
+ delete cipher_;
+ cipher_ = NULL;
+
+ switch (cipher_type_) {
+ case SSH_CIPHER_3DES:
+ cipher_ = new FQTermSSH1DES3;
+ break;
+ }
+}
+
+void FQTermSSHPacketSender::startEncryption(const u_char *key, const u_char *IV) {
+ is_encrypt_ = true;
+ cipher_->setIV(IV);
+ cipher_->setKey(key);
+}
+
+void FQTermSSHPacketSender::resetEncryption() {
+ is_encrypt_ = false;
+}
+
+void FQTermSSHPacketSender::setMacType(int macType) {
+ mac_type_ = macType;
+
+ delete mac_;
+ mac_ = NULL;
+
+ switch (mac_type_) {
+ case FQTERM_SSH_HMAC_SHA1:
+ mac_ = new FQTermSSHSHA1;
+ break;
+ }
+}
+
+void FQTermSSHPacketSender::startMac(const u_char *key) {
+ is_mac_ = true;
+ mac_->setKey(key);
+}
+
+void FQTermSSHPacketSender::resetMac() {
+ is_mac_ = false;
+}
+
+//==============================================================================
+//FQTermSSHPacketReceiver
+//==============================================================================
+
+FQTermSSHPacketReceiver::FQTermSSHPacketReceiver() {
+ buffer_ = new FQTermSSHBuffer(1024);
+
+ is_decrypt_ = false;
+ cipher_type_ = SSH_CIPHER_NONE;
+ cipher_ = NULL;
+ setEncryptionType(SSH_CIPHER_3DES);
+
+ is_mac_ = false;
+ mac_type_ = FQTERM_SSH_MAC_NONE;
+ mac_ = NULL;
+
+ is_compressed_ = false;
+
+ sequence_no_ = 0;
+}
+
+FQTermSSHPacketReceiver::~FQTermSSHPacketReceiver() {
+ delete buffer_;
+ if (is_decrypt_) {
+ delete cipher_;
+ }
+}
+
+void FQTermSSHPacketReceiver::getRawData(char *data, int length) {
+ buffer_->getRawData(data, length);
+}
+
+u_char FQTermSSHPacketReceiver::getByte() {
+ return buffer_->getByte();
+}
+
+u_int FQTermSSHPacketReceiver::getInt() {
+ return buffer_->getInt();
+}
+
+void *FQTermSSHPacketReceiver::getString(int *length) {
+ return buffer_->getString(length);
+}
+
+void FQTermSSHPacketReceiver::getBN(BIGNUM *bignum) {
+ buffer_->getSSH1BN(bignum);
+}
+
+void FQTermSSHPacketReceiver::getBN2(BIGNUM *bignum) {
+ buffer_->getSSH2BN(bignum);
+}
+
+void FQTermSSHPacketReceiver::consume(int len) {
+ buffer_->consume(len);
+}
+
+void FQTermSSHPacketReceiver::setEncryptionType(int cipherType) {
+ cipher_type_ = cipherType;
+
+ delete cipher_;
+ cipher_ = NULL;
+
+ switch (cipher_type_) {
+ case SSH_CIPHER_3DES:
+ cipher_ = new FQTermSSH1DES3;
+ break;
+ }
+}
+
+void FQTermSSHPacketReceiver::startEncryption(const u_char *key, const u_char *IV) {
+ is_decrypt_ = true;
+ cipher_->setIV(IV);
+ cipher_->setKey(key);
+}
+
+void FQTermSSHPacketReceiver::resetEncryption() {
+ is_decrypt_ = false;
+}
+
+void FQTermSSHPacketReceiver::setMacType(int macType) {
+ mac_type_ = macType;
+ delete mac_;
+ mac_ = NULL;
+
+ switch (mac_type_) {
+ case FQTERM_SSH_HMAC_SHA1:
+ mac_ = new FQTermSSHSHA1;
+ break;
+ }
+}
+
+void FQTermSSHPacketReceiver::startMac(const u_char *key) {
+ is_mac_ = true;
+ mac_->setKey(key);
+}
+
+void FQTermSSHPacketReceiver::resetMac() {
+ is_mac_ = false;
+}
+
+} // namespace FQTerm
+
+#include "fqterm_ssh_packet.moc"
diff --git a/src/protocol/internal/fqterm_ssh_packet.h b/src/protocol/internal/fqterm_ssh_packet.h
new file mode 100644
index 0000000..ed5a488
--- /dev/null
+++ b/src/protocol/internal/fqterm_ssh_packet.h
@@ -0,0 +1,148 @@
+/***************************************************************************
+ * 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. *
+ ***************************************************************************/
+
+#ifndef FQTERM_SSH_PACKET_H
+#define FQTERM_SSH_PACKET_H
+
+#include <openssl/bn.h>
+
+#include <QObject>
+
+#include "fqterm_ssh_types.h"
+#include "fqterm_ssh_buffer.h"
+#include "fqterm_ssh_mac.h"
+#include "fqterm_ssh_cipher.h"
+#include "fqterm_serialization.h"
+
+namespace FQTerm {
+
+class FQTermSSHPacketSender: public QObject {
+ Q_OBJECT;
+ public:
+ FQTermSSHBuffer *output_buffer_;
+ FQTermSSHBuffer *buffer_;
+
+ FQTermSSHPacketSender();
+ virtual ~FQTermSSHPacketSender();
+
+ void startPacket(int pkt_type);
+ void putByte(int data);
+ void putInt(u_int data);
+ void putString(const char *string, int len = -1);
+ void putRawData(const char *data, int length);
+ void putBN(BIGNUM *bignum);
+ void putBN2(BIGNUM *bignum);
+ void write();
+
+ int getIVSize() const { return cipher_->getIVSize();}
+ int getKeySize() const { return cipher_->getKeySize();}
+ int getMacKeySize() const { return mac_->keySize();}
+
+ public slots:
+ virtual void setEncryptionType(int cipherType);
+ void startEncryption(const u_char *key, const u_char *IV = NULL);
+ void resetEncryption();
+
+ void setMacType(int macType);
+ void startMac(const u_char *sessionkey);
+ void resetMac();
+
+ void enableCompress(int enable) {is_compressed_ = enable;};
+
+ signals:
+ void dataToWrite();
+
+ protected:
+ bool is_encrypt_;
+ int cipher_type_;
+ FQTermSSHCipher *cipher_;
+
+ bool is_mac_;
+ int mac_type_;
+ FQTermSSHMac *mac_;
+
+ bool is_compressed_;
+
+ u_int32_t sequence_no_;
+
+ virtual void makePacket() = 0;
+};
+
+class FQTermSSHPacketReceiver: public QObject {
+ Q_OBJECT;
+ public:
+ FQTermSSHBuffer *buffer_;
+
+ FQTermSSHPacketReceiver();
+ virtual ~FQTermSSHPacketReceiver();
+
+ int packetType()const {
+ return packet_type_;
+ }
+
+ u_char getByte();
+ u_int getInt();
+ void *getString(int *length = NULL);
+ void getRawData(char *data, int length);
+ void getBN(BIGNUM *bignum);
+ void getBN2(BIGNUM *bignum);
+ void consume(int len);
+
+ virtual int packetDataLen() const { return real_data_len_;}
+ int getIVSize() const { return cipher_->getIVSize();}
+ int getKeySize() const { return cipher_->getKeySize();}
+ int getMacKeySize() const { return mac_->keySize();}
+
+ virtual void parseData(FQTermSSHBuffer *input) = 0;
+ public slots:
+ virtual void setEncryptionType(int cipherType);
+ void startEncryption(const u_char *key, const u_char *IV = NULL);
+ void resetEncryption();
+
+ void setMacType(int macType);
+ void startMac(const u_char *sessionkey);
+ void resetMac();
+
+ void enableCompress(int enable) {is_compressed_ = enable;};
+
+ signals:
+ void packetAvaliable(int type);
+ void packetError(QString);
+
+ protected:
+ bool is_decrypt_;
+ int cipher_type_;
+ FQTermSSHCipher *cipher_;
+
+ bool is_mac_;
+ int mac_type_;
+ FQTermSSHMac *mac_;
+
+ bool is_compressed_;
+
+ int packet_type_;
+ int real_data_len_;
+
+ u_int32_t sequence_no_;
+};
+
+} // namespace FQTerm
+
+#endif // FQTERM_SSH_PACKET
diff --git a/src/protocol/internal/fqterm_ssh_pubkey.h b/src/protocol/internal/fqterm_ssh_pubkey.h
new file mode 100644
index 0000000..0de957c
--- /dev/null
+++ b/src/protocol/internal/fqterm_ssh_pubkey.h
@@ -0,0 +1,37 @@
+/***************************************************************************
+ * 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. *
+ ***************************************************************************/
+
+#ifndef FQTERM_SSH_PUB_KEY_H
+#define FQTERM_SSH_PUB_KEY_H
+
+#include <openssl/bn.h>
+
+namespace FQTerm {
+
+class FQTermSSHPubKey {
+public:
+ FQTermSSHPubKey() {}
+ virtual ~FQTermSSHPubKey() {}
+ virtual void publicEncrypt(BIGNUM *, BIGNUM*) = 0;
+};
+
+} // namespace FQTerm
+
+#endif // FQTERM_SSH_PUB_KEY_H
diff --git a/src/protocol/internal/fqterm_ssh_rsa.cpp b/src/protocol/internal/fqterm_ssh_rsa.cpp
new file mode 100644
index 0000000..dd36898
--- /dev/null
+++ b/src/protocol/internal/fqterm_ssh_rsa.cpp
@@ -0,0 +1,67 @@
+/***************************************************************************
+ * 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 <QGlobalStatic>
+
+#include "fqterm_trace.h"
+#include "fqterm_ssh_types.h"
+#include "fqterm_ssh_rsa.h"
+
+namespace FQTerm {
+
+FQTermSSHRSA::FQTermSSHRSA() {
+ d_rsa = RSA_new();
+ d_rsa->n = BN_new();
+ d_rsa->e = BN_new();
+}
+
+FQTermSSHRSA::~FQTermSSHRSA() {
+ if (d_rsa != NULL) {
+ RSA_free(d_rsa);
+ }
+}
+
+void FQTermSSHRSA::publicEncrypt(BIGNUM *out, BIGNUM *in) {
+ u_char *inbuf, *outbuf;
+ int len, ilen, olen;
+
+ if (BN_num_bits(d_rsa->e) < 2 || !BN_is_odd(d_rsa->e)) {
+ FQ_VERIFY(false); // public_encrypt() exponent too small or not odd.
+ }
+
+ olen = BN_num_bytes(d_rsa->n);
+ outbuf = new u_char[olen];
+
+ ilen = BN_num_bytes(in);
+ inbuf = new u_char[ilen];
+ BN_bn2bin(in, inbuf);
+
+ if ((len = RSA_public_encrypt(ilen, inbuf, outbuf, d_rsa, RSA_PKCS1_PADDING)) <= 0) {
+ //RSA_PKCS1_PADDING = 1
+ FQ_VERIFY(false); // "rsa_public_encrypt() failed.
+ }
+
+ BN_bin2bn(outbuf, len, out);
+
+ delete [] outbuf;
+ delete [] inbuf;
+}
+
+} // namespace FQTerm
diff --git a/src/protocol/internal/fqterm_ssh_rsa.h b/src/protocol/internal/fqterm_ssh_rsa.h
new file mode 100644
index 0000000..bfe89ce
--- /dev/null
+++ b/src/protocol/internal/fqterm_ssh_rsa.h
@@ -0,0 +1,41 @@
+/***************************************************************************
+ * 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. *
+ ***************************************************************************/
+
+#ifndef FQTERM_SSH_RSA_H
+#define FQTERM_SSH_RSA_H
+
+#include <openssl/rsa.h>
+
+#include "fqterm_ssh_pubkey.h"
+
+namespace FQTerm {
+
+class FQTermSSHRSA: public FQTermSSHPubKey {
+public:
+ RSA *d_rsa;
+ FQTermSSHRSA();
+ virtual ~FQTermSSHRSA();
+
+ virtual void publicEncrypt(BIGNUM *, BIGNUM*);
+};
+
+} // namespace FQTerm
+
+#endif // FQTERM_SSH_RSA_H
diff --git a/src/protocol/internal/fqterm_ssh_types.h b/src/protocol/internal/fqterm_ssh_types.h
new file mode 100644
index 0000000..c10bfac
--- /dev/null
+++ b/src/protocol/internal/fqterm_ssh_types.h
@@ -0,0 +1,43 @@
+/***************************************************************************
+ * 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. *
+ ***************************************************************************/
+
+#ifndef FQTERM_SSH_TYPES_H
+#define FQTERM_SSH_TYPES_H
+
+#include <QGlobalStatic>
+#if !defined(Q_OS_WIN32) && !defined(_OS_WIN32_)
+#include <sys/types.h>
+#else
+typedef unsigned char u_char;
+typedef unsigned short u_short;
+typedef unsigned int u_int;
+typedef unsigned long u_long;
+
+#ifdef QT3_SUPPORT
+typedef Q_UINT32 u_int32_t;
+typedef Q_UINT64 u_int64_t;
+#else
+typedef quint32 u_int32_t;
+typedef quint64 u_int64_t;
+#endif // QT3_SUPPORT
+
+#endif // Q_OS_WIN32
+
+#endif // FQTERM_SSH_TYPES_H