Files
qt5-legatus-legacy/Lts/Plugins/ProjectionWorkstation/Common/pws_vnc_handler.cpp
T
2026-08-13 18:33:10 +02:00

346 lines
11 KiB
C++
Executable File

/**
* @RDE_HEADER@
* $Id$
* $HeadURL$
* $Author$
*/
#include "pws_vnc_handler.h"
#include "pws_des_encryption.h"
namespace ProjectionWorkstation
{
VncHandler::VncHandler(const QHostAddress &host, quint16 port, QObject *parent)
: QObject(parent)
, m_logger(LtsProjectionWorkstation::getLogger("VncHandler"))
, m_nextType(RfbProtocol::STC::RFB_VERSION)
, m_socket(new QTcpSocket(this))
, m_host(host)
, m_port(port)
{
connect(m_socket, &QTcpSocket::readyRead, this, &VncHandler::onReadyRead);
connect(m_socket, QOverload<QAbstractSocket::SocketError>::of(&QTcpSocket::error), this, &VncHandler::error);
connect(m_socket, &QTcpSocket::disconnected, this, &VncHandler::slotOnDisconnected);
}
void VncHandler::connectToHost()
{
m_socket->connectToHost(m_host, m_port);
}
void VncHandler::connectToHost(const QHostAddress &host, quint16 port)
{
m_socket->connectToHost(host, port);
}
void VncHandler::onReadyRead()
{
m_buffer += m_socket->readAll();
QDataStream stream(m_buffer);
stream.setByteOrder(QDataStream::BigEndian);
qint64 readPos = 0;
while (handleData(stream))
readPos = stream.device()->pos();
// TODO: squeeze m_buffer only when needed (e.g is larger than x MB)
if (readPos > 0)
m_buffer = m_buffer.mid(readPos);
}
bool VncHandler::handleData(QDataStream &stream)
{
switch (m_nextType)
{
// -------------------------------------------------------------------------------------------
case RfbProtocol::STC::RFB_VERSION:
{
RfbProtocol::STC::RfbVersion version;
stream.startTransaction();
stream >> version;
if (!stream.commitTransaction())
return false;
if (handleVersion(version))
{
setNextExpectedMessageType(RfbProtocol::STC::RFB_SUP_SECURITY_TYPES);
DEBUG_LOG(m_logger) << "RfbVersion handled Successfully!";
return true;
}
ERROR_LOG(m_logger) << "RfbVersion handshake Error!";
break;
}
// -------------------------------------------------------------------------------------------
case RfbProtocol::STC::RFB_SUP_SECURITY_TYPES:
{
RfbProtocol::STC::RfbSupportedSecurityTypes supTypes;
stream.startTransaction();
stream >> supTypes;
if (!stream.commitTransaction())
return false;
if (handleSupportedSecurityTypes(supTypes))
{
setNextExpectedMessageType(RfbProtocol::STC::RFB_VNC_AUTHENTICATION);
DEBUG_LOG(m_logger) << "RfbSecurityType handled Successfully!";
return true;
}
ERROR_LOG(m_logger) << "RfbSecurityType handshake Error!";
break;
}
// -------------------------------------------------------------------------------------------
case RfbProtocol::STC::RFB_VNC_AUTHENTICATION:
{
RfbProtocol::STC::RfbVncAuthentication auth;
stream.startTransaction();
stream >> auth;
if (!stream.commitTransaction())
return false;
if (handleVncAuthentication(auth))
{
setNextExpectedMessageType(RfbProtocol::STC::RFB_SECURITY_RESULT);
DEBUG_LOG(m_logger) << "RfbVncAuthentication handled Successfully!";
return true;
}
ERROR_LOG(m_logger) << "RfbVncAuthentication Error!";
break;
}
// -------------------------------------------------------------------------------------------
case RfbProtocol::STC::RFB_SECURITY_RESULT:
{
RfbProtocol::STC::RfbSecurityResult result;
stream.startTransaction();
stream >> result;
if (!stream.commitTransaction())
return false;
if (handleSecurityResult(result))
{
setNextExpectedMessageType(RfbProtocol::STC::RFB_SERVER_INIT);
DEBUG_LOG(m_logger) << "RfbSecurityResult handled Successfully!";
return true;
}
ERROR_LOG(m_logger) << "RfbSecurityResult Error!";
break;
}
// -------------------------------------------------------------------------------------------
case RfbProtocol::STC::RFB_SERVER_INIT:
{
RfbProtocol::STC::RfbServerInit init;
stream.startTransaction();
stream >> init;
if (!stream.commitTransaction())
return false;
if (handleServerInit(init))
{
QByteArray data = RfbProtocol::CTS::RfbSetEncodings({qint32(RfbProtocol::HEXTILE_ENCODING),
qint32(RfbProtocol::RAW_ENCODING),
qint32(RfbProtocol::LAST_RECT_PSEUDO_ENCODING),
qint32(-247), // Compression Level Pseudo-encoding (-247 high .. -255 low)
qint32(-23), // JPEG Quality Level Pseudo-encoding (-23 high quality .. -32 low quality)
});
m_socket->write(data);
emit connected();
m_socket->write(RfbProtocol::CTS::RfbFrameBufferUpdateRequest(false, 0, 0, 1920, 1200));
setNextExpectedMessageType(RfbProtocol::STC::RFB_FRAMEBUFFER_UPDATE);
DEBUG_LOG(m_logger) << "RfbServerInit handled Successfully!";
}
return true;
}
// -------------------------------------------------------------------------------------------
case RfbProtocol::STC::RFB_FRAMEBUFFER_UPDATE:
{
// The handling of 'next expected type is flawed' - the order and next type is only defined during setup,
// not during runtime - here we can get any Server to Client Message defined in the rfb protocol. So we
// have to take a look at the protocol byte and handle accordingly
// by default we must only be able to decode 0 (Framebuffer update), 1 (SetColourMapEntries), 2 (Bell) and 3 (ServerCutText)
// also using QDataStream here only works by accident... but we won't change it now (never touch a running system)
stream.startTransaction();
quint8 msgType;
stream >> msgType;
DEBUG_LOG(m_logger) << "got message from server: " << QString::number(msgType, 16);
switch (msgType)
{
case RfbProtocol::STC::RFB_FRAMEBUFFER_UPDATE:
return handleFrameBufferUpdate(stream);
case RfbProtocol::STC::RFB_SET_COLORMAP:
return handleSetColormap(stream);
case RfbProtocol::STC::RFB_RING_BELL:
return handleRingBell(stream);
case RfbProtocol::STC::RFB_SERVER_CUTTEXT:
return handleServerCuttext(stream);
default:
WARNING_LOG(m_logger) << "Unknown message type from server (" << QString::number(msgType, 16) << ") - reconnecting";
stream.abortTransaction();
slotOnDisconnected();
return false;
}
return true;
}
default:
break;
}
Q_ASSERT(false);
stream.abortTransaction();
slotOnDisconnected();
return false;
}
bool VncHandler::handleFrameBufferUpdate(QDataStream &stream)
{
RfbProtocol::STC::RfbFramebufferUpdate update;
stream.startTransaction();
stream >> update;
bool bRet = stream.commitTransaction();
if (bRet)
{
if (!update.rectangles().isEmpty())
emit frameBufferUpdateReceived(update.rectangles());
m_socket->write(RfbProtocol::CTS::RfbFrameBufferUpdateRequest(true, 0, 0, 1920, 1200));
}
return bRet;
}
bool VncHandler::handleSetColormap(QDataStream &stream)
{
RfbProtocol::STC::RfbSetColormap setColormap;
stream.startTransaction();
stream >> setColormap;
return stream.commitTransaction();
}
bool VncHandler::handleRingBell(QDataStream &stream)
{
RfbProtocol::STC::RfbRingBell cutText;
stream.startTransaction();
stream >> cutText;
bool bRet = stream.commitTransaction();
emit beepReceived();
return bRet;
}
bool VncHandler::handleServerCuttext(QDataStream &stream)
{
RfbProtocol::STC::RfbServerCutText cutText;
stream.startTransaction();
stream >> cutText;
bool bRet = stream.commitTransaction();
if (bRet)
emit cutTextReceived(QString::fromLatin1(cutText.text()));
return bRet;
}
void VncHandler::slotOnDisconnected()
{
resetHandler();
emit disconnected();
}
void VncHandler::writeData(const QByteArray &data)
{
m_socket->write(data);
m_socket->flush();
}
void VncHandler::error()
{
ERROR_LOG(m_logger) << "SocketError " << m_socket->errorString();
}
void VncHandler::resetHandler()
{
m_buffer.clear();
setNextExpectedMessageType(RfbProtocol::STC::RFB_VERSION);
m_serverInit = RfbProtocol::STC::RfbServerInit();
}
// Data Handlers
bool VncHandler::handleVersion(const RfbProtocol::STC::RfbVersion &version)
{
if (version.majorVersion() == 3 && version.minorVersion() == 8)
{
m_socket->write(RfbProtocol::CTS::RfbVersion(version.majorVersion(),
version.minorVersion()));
return true;
}
ERROR_LOG(m_logger) << "Unsupported Version: " << version.majorVersion() << "." << version.minorVersion();
return false;
}
bool VncHandler::handleSupportedSecurityTypes(const RfbProtocol::STC::RfbSupportedSecurityTypes &supportedTypes)
{
if (supportedTypes.types().contains(2) && !supportedTypes.types().contains(0))
{
m_socket->write(RfbProtocol::CTS::RfbSecurityType(2));
return true;
}
QString typesString;
for (quint8 type : supportedTypes.types())
typesString.append(QString::number(type)).append(QLatin1Char(' '));
if (typesString.isEmpty()) typesString = QStringLiteral("EMPTY");
ERROR_LOG(m_logger) << "Unsupported Security Types: " << typesString;
return false;
}
bool VncHandler::handleVncAuthentication(const RfbProtocol::STC::RfbVncAuthentication &authentication)
{
quint64 key = convertPassword("ctc11de");
quint64 blockA = 0, blockB = 0;
DES des(key);
blockA = des.encrypt(authentication.challengeBlockA());
blockB = des.encrypt(authentication.challengeBlockB());
m_socket->write(RfbProtocol::CTS::RfbVncAuthentication(blockA, blockB));
return true;
}
// VNC Servers require the password with reversed bytes
// and a length of 8bytes (truncated or padded if longer/smaller)
quint64 VncHandler::convertPassword(const char *password)
{
quint64 result = 0;
const size_t len = strlen(password);
for (quint8 count = 0; count < 8; ++count)
{
quint8 data;
result <<= 8;
if (count < len)
data = reverseByte(quint8(password[count]));
else
data = 0;
result = (result | data);
}
return result;
}
quint8 VncHandler::reverseByte(quint8 b)
{
b = (b & 0xF0) >> 4 | (b & 0x0F) << 4;
b = (b & 0xCC) >> 2 | (b & 0x33) << 2;
b = (b & 0xAA) >> 1 | (b & 0x55) << 1;
return b;
}
bool VncHandler::handleSecurityResult(const RfbProtocol::STC::RfbSecurityResult &result)
{
if (result.resultCode() == 0)
{
m_socket->write(RfbProtocol::CTS::RfbClientInit(true));
return true;
}
else
{
ERROR_LOG(m_logger) << result.reason();
return false;
}
}
bool VncHandler::handleServerInit(const RfbProtocol::STC::RfbServerInit &serverInit)
{
m_serverInit = serverInit;
return true;
}
} // ProjectionWorkstation