Cmdr/src/session.cpp
2019-09-14 13:03:34 -04:00

425 lines
12 KiB
C++

#include "session.h"
// This file is part of Cmdr.
// Cmdr 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 3 of the License, or
// (at your option) any later version.
// Cmdr 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 Cmdr under the LICENSE.md file. If not, see
// <http://www.gnu.org/licenses/>.
Session::Session(QObject *parent) : QSslSocket(parent)
{
dSize = 0;
hook = 0;
dType = 0;
flags = 0;
reconnect = false;
connect(this, SIGNAL(encrypted()), this, SLOT(handShakeDone()));
connect(this, SIGNAL(connected()), this, SLOT(isConnected()));
connect(this, SIGNAL(disconnected()), this, SLOT(isDisconnected()));
connect(this, SIGNAL(readyRead()), this, SLOT(dataIn()));
connect(this, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(sockerr(QAbstractSocket::SocketError)));
}
void Session::hookedBinToServer(const QByteArray &data, uchar typeId)
{
if (*Shared::connectedToHost)
{
write(wrFrame(hook, data, typeId));
}
else
{
emit errTxtOut("err: not connected to a host.\n");
}
}
void Session::binToServer(quint16 cmdId, const QByteArray &data, uchar typeId)
{
if (*Shared::connectedToHost)
{
hook = cmdId;
emit setUserIO(HOST_HOOK);
write(wrFrame(cmdId, data, typeId));
}
else
{
emit errTxtOut("err: not connected to a host.\n");
}
}
void Session::sockerr(QAbstractSocket::SocketError err)
{
if (err == QAbstractSocket::RemoteHostClosedError)
{
emit mainTxtOut("\nThe remote host closed the session.\n");
}
else
{
emit errTxtOut("\nerr: " + errorString() + "\n");
if (state() == QAbstractSocket::UnconnectedState)
{
hook = 0;
}
}
}
void Session::isConnected()
{
// client header format: [4bytes(tag)][2bytes(major)][2bytes(minor)][2bytes(patch)][128bytes(appName)][272bytes(coName)]
// tag = 0x4D, 0x52, 0x43, 0x49 (MRCI)
// major = 16bit little endian int
// minor = 16bit little endian int
// patch = 16bit little endian int
// appName = UTF16LE string
// coName = UTF16LE string
emit mainTxtOut("Connected.\n");
QByteArray header;
QStringList ver = QString(MRCI_VERSION).split('.');
header.append(SERVER_HEADER_TAG);
header.append(wrInt(ver[0].toULongLong(), 16));
header.append(wrInt(ver[1].toULongLong(), 16));
header.append(wrInt(ver[2].toULongLong(), 16));
header.append(toTEXT(QString(QString(APP_NAME) + " v" + QString(APP_VERSION)).leftJustified(64, ' ', true)));
header.append(toTEXT(Shared::hostAddress->leftJustified(136, ' ', true)));
if (header.size() == CLIENT_HEADER_LEN)
{
write(header);
}
else
{
emit errTxtOut("\nerr: client bug! - header len not equal to " + QString::number(CLIENT_HEADER_LEN) + "\n");
disconnectFromHost();
}
}
void Session::handShakeDone()
{
QSslCipher cipher = sessionCipher();
QString txt;
QTextStream txtOut(&txt);
txtOut << "SSL Handshake sucessful." << endl
<< "cipher details:" << endl
<< " #cipher - " << cipher.name() << endl
<< " #protocol - " << cipher.protocolString() << endl
<< " #bit_depth - " << cipher.usedBits() << endl
<< " #key_exchange - " << cipher.keyExchangeMethod() << endl
<< " #authentication - " << cipher.authenticationMethod() << endl;
emit mainTxtOut(txt);
}
void Session::isDisconnected()
{
dSize = 0;
hook = 0;
dType = 0;
flags = 0;
*Shared::servMajor = 0;
*Shared::servMinor = 0;
*Shared::servPatch = 0;
*Shared::connectedToHost = false;
Shared::sessionId->clear();
Shared::hostCmds->clear();
Shared::genfileCmds->clear();
emit mainTxtOut("\nHost session ended. (disconnected)\n\n");
if (reconnect)
{
reconnect = false;
connectToServ();
}
}
void Session::connectToServ()
{
if (state() == QAbstractSocket::ConnectedState)
{
reconnect = true;
disconnectFromServ();
}
else
{
emit mainTxtOut("Connecting to address: " + *Shared::hostAddress + " port: " + QString::number(*Shared::hostPort) + "\n");
connectToHost(*Shared::hostAddress, *Shared::hostPort);
}
}
void Session::disconnectFromServ()
{
*Shared::connectedToHost = false;
if (state() == QAbstractSocket::ConnectedState)
{
emit mainTxtOut("Disconnecting.\n");
disconnectFromHost();
}
close();
}
void Session::termHostCmd()
{
if (hook != 0)
{
binToServer(*Shared::termCmdId, wrInt(hook, 16), CMD_ID);
}
}
void Session::enableGenFile(bool state)
{
if (state)
{
flags |= GEN_FILE_ON;
}
else
{
flags &= ~GEN_FILE_ON;
}
}
void Session::dataFromHost(const QByteArray &data)
{
if (cmdId == ASYNC_SYS_MSG)
{
if (dType == HOST_CERT)
{
QSslCertificate cert(data, QSsl::Pem);
QSslError selfSigned(QSslError::SelfSignedCertificate, cert);
emit mainTxtOut("SSL cert received.\n\n");
if (cert.isSelfSigned())
{
emit mainTxtOut("WARNING: the cert is self signed. be careful if in a public network.\n\n");
}
ignoreSslErrors(QList<QSslError>() << selfSigned);
addCaCertificate(cert);
startClientEncryption();
}
else if (dType == TEXT)
{
emit mainTxtOut(fromTEXT(data));
}
else if (dType == BIG_TEXT)
{
emit bigTxtOut(fromTEXT(data));
}
else if (dType == ERR)
{
emit errTxtOut(fromTEXT(data));
}
}
else if (cmdId == ASYNC_ADD_CMD)
{
if ((dType == NEW_CMD) && (data.size() >= 131))
{
quint16 id = static_cast<quint16>(rdInt(data.mid(0, 2)));
QString cmdName = fromTEXT(data.mid(3, 128)).trimmed();
if (cmdName.toLower() == "term")
{
*Shared::termCmdId = id;
}
if (!Shared::hostCmds->contains(id))
{
Shared::hostCmds->insert(id, cmdName);
if (data[2])
{
Shared::genfileCmds->insert(id, cmdName);
}
}
}
}
else if (cmdId == ASYNC_RM_CMD)
{
if ((dType == CMD_ID) && (data.size() == 2))
{
quint16 id = static_cast<quint16>(rdInt(data.mid(0, 2)));
if (id == hook) hook = 0;
if (Shared::hostCmds->contains(id))
{
Shared::genfileCmds->remove(id);
Shared::hostCmds->remove(id);
}
}
}
else if ((cmdId == ASYNC_RDY) || (cmdId == ASYNC_EXE_CRASH))
{
hook = 0;
if (dType == TEXT)
{
emit mainTxtOut(fromTEXT(data));
}
else if (dType == ERR)
{
emit errTxtOut(fromTEXT(data));
}
}
else if ((cmdId == hook) && (hook != 0))
{
if ((dType == TEXT) || (dType == PRIV_TEXT))
{
emit mainTxtOut(fromTEXT(data));
if (dType == PRIV_TEXT)
{
emit setUserIO(HIDDEN);
}
}
else if (dType == BIG_TEXT)
{
emit bigTxtOut(fromTEXT(data));
}
else if (dType == ERR)
{
emit errTxtOut(fromTEXT(data));
}
else if (dType == IDLE)
{
hook = 0;
emit hostFinished();
emit unsetUserIO(HOST_HOOK);
if (Shared::hostCmds->contains(cmdId))
{
emit mainTxtOut("\nFinished: " + Shared::hostCmds->value(cmdId) + "\n\n");
}
else
{
emit mainTxtOut("\nFinished: Unknown\n\n");
}
}
else if ((dType == GEN_FILE) && (flags & GEN_FILE_ON))
{
emit toGenFile(data);
}
}
}
void Session::dataIn()
{
if (flags & DSIZE_RDY)
{
if (bytesAvailable() >= dSize)
{
flags ^= DSIZE_RDY;
dataFromHost(read(dSize));
dataIn();
}
}
else if (flags & VER_OK)
{
if (bytesAvailable() >= FRAME_HEADER_LEN)
{
QByteArray header = read(FRAME_HEADER_LEN);
dType = static_cast<uchar>(header[0]);
cmdId = static_cast<quint16>(rdInt(header.mid(1, 2)));
dSize = static_cast<uint>(rdInt(header.mid(3, 3)));
flags |= DSIZE_RDY;
dataIn();
}
}
else if (bytesAvailable() >= SERVER_HEADER_LEN)
{
// host header format: [1byte(reply)][2bytes(major)][2bytes(minor)][2bytes(patch)][28bytes(sesId)]
// reply is an 8bit little endian int the indicates the result of the client header
// sent to the host from the isConnected() function.
// sesId = 224bit sha3 hash
// major = 16bit little endian uint (host ver major)
// minor = 16bit little endian uint (host ver minor)
// patch = 16bit little endian uint (host ver patch)
uchar reply = static_cast<uchar>(rdInt(read(1)));
if ((reply == 1) || (reply == 2))
{
*Shared::servMajor = static_cast<ushort>(rdInt(read(2)));
*Shared::servMinor = static_cast<ushort>(rdInt(read(2)));
*Shared::servPatch = static_cast<ushort>(rdInt(read(2)));
emit mainTxtOut("Detected host version: " + verText(*Shared::servMajor, *Shared::servMinor, *Shared::servPatch) + "\n");
if (*Shared::servMajor == 1)
{
*Shared::sessionId = read(28);
*Shared::connectedToHost = true;
flags |= VER_OK;
emit mainTxtOut("Host assigned session id: " + Shared::sessionId->toHex() + "\n");
if (reply == 2)
{
emit mainTxtOut("Awaiting SSL cert from the host.\n");
}
else
{
emit mainTxtOut("The host claims SSL is not needed. the connection will not be encrypted.\n");
}
}
else
{
emit errTxtOut("err: Host not compatible.\n");
disconnectFromHost();
}
}
else if (reply == 3)
{
emit errTxtOut("err: Client version rejected by the host.\n");
}
else if (reply == 4)
{
emit errTxtOut("err: The host was unable to find a SSL cert for common name: " + *Shared::hostAddress + ".\n");
}
else
{
emit errTxtOut("err: Invalid reply id from the host: " + QString::number(reply) + ".\n");
}
if ((reply != 1) && (reply != 2)) disconnectFromHost();
dataIn();
}
}