425 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			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();
 | |
|     }
 | |
| }
 |