Initial commit

This commit is contained in:
torsten committed 2026-08-13 18:33:10 +02:00
commit b7d90b9afe
16245 files changed
+3396666

No files matched your search

+26
View File
@@ -0,0 +1,26 @@
#
# $Id$
# $HeadURL$
# $Author$
#
SET(pws_common_SRCS
pws_rfb_protocol.cpp
pws_rfb_protocol.h
pws_des_encryption.h
pws_des_encryption.cpp
pws_vnc_handler.h
pws_vnc_handler.cpp
)
NGL_ADD_LIBRARY2(WS PwsCommon SHARED ${pws_common_SRCS} )
NGL_TARGET_LINK_LIBRARIES(PwsCommon
PRIVATE
NGLLogger
ngltools
LtsSession
LtsProjectionWorkstation
PUBLIC
Qt5::Core
Qt5::Network
)
@@ -0,0 +1,318 @@
/**
* @RDE_HEADER@
* $Id$
* $HeadURL$
* $Author$
*/
#include "pws_des_encryption.h"
namespace ProjectionWorkstation
{
constexpr quint32 LB32_MASK = 0x00000001;
constexpr quint64 LB64_MASK = 0x0000000000000001;
constexpr quint64 L64_MASK = 0x00000000ffffffff;
// Permuted Choice 1 Table [7*8]
constexpr char PC1[] =
{
57, 49, 41, 33, 25, 17, 9,
1, 58, 50, 42, 34, 26, 18,
10, 2, 59, 51, 43, 35, 27,
19, 11, 3, 60, 52, 44, 36,
63, 55, 47, 39, 31, 23, 15,
7, 62, 54, 46, 38, 30, 22,
14, 6, 61, 53, 45, 37, 29,
21, 13, 5, 28, 20, 12, 4
};
// Permuted Choice 2 Table [6*8]
constexpr char PC2[] =
{
14, 17, 11, 24, 1, 5,
3, 28, 15, 6, 21, 10,
23, 19, 12, 4, 26, 8,
16, 7, 27, 20, 13, 2,
41, 52, 31, 37, 47, 55,
30, 40, 51, 45, 33, 48,
44, 49, 39, 56, 34, 53,
46, 42, 50, 36, 29, 32
};
// Iteration Shift Array
constexpr char ITERATION_SHIFT[] =
{
// 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1
};
// Initial Permutation Table [8*8]
constexpr char IP[] =
{
58, 50, 42, 34, 26, 18, 10, 2,
60, 52, 44, 36, 28, 20, 12, 4,
62, 54, 46, 38, 30, 22, 14, 6,
64, 56, 48, 40, 32, 24, 16, 8,
57, 49, 41, 33, 25, 17, 9, 1,
59, 51, 43, 35, 27, 19, 11, 3,
61, 53, 45, 37, 29, 21, 13, 5,
63, 55, 47, 39, 31, 23, 15, 7
};
// Inverse Initial Permutation Table [8*8]
constexpr char FP[] =
{
40, 8, 48, 16, 56, 24, 64, 32,
39, 7, 47, 15, 55, 23, 63, 31,
38, 6, 46, 14, 54, 22, 62, 30,
37, 5, 45, 13, 53, 21, 61, 29,
36, 4, 44, 12, 52, 20, 60, 28,
35, 3, 43, 11, 51, 19, 59, 27,
34, 2, 42, 10, 50, 18, 58, 26,
33, 1, 41, 9, 49, 17, 57, 25
};
// Expansion table [6*8]
constexpr char EXPANSION[] =
{
32, 1, 2, 3, 4, 5,
4, 5, 6, 7, 8, 9,
8, 9, 10, 11, 12, 13,
12, 13, 14, 15, 16, 17,
16, 17, 18, 19, 20, 21,
20, 21, 22, 23, 24, 25,
24, 25, 26, 27, 28, 29,
28, 29, 30, 31, 32, 1
};
// The S-Box tables [8*16*4]
constexpr char SBOX[8][64] =
{
{
// S1
14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7,
0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8,
4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0,
15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13
},
{
// S2
15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10,
3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5,
0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15,
13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9
},
{
// S3
10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8,
13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1,
13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7,
1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12
},
{
// S4
7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15,
13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9,
10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4,
3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14
},
{
// S5
2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9,
14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6,
4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14,
11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3
},
{
// S6
12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11,
10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8,
9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6,
4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13
},
{
// S7
4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1,
13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6,
1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2,
6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12
},
{
// S8
13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7,
1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2,
7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8,
2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11
}
};
// Post S-Box permutation [4*8]
constexpr char PBOX[] =
{
16, 7, 20, 21,
29, 12, 28, 17,
1, 15, 23, 26,
5, 18, 31, 10,
2, 8, 24, 14,
32, 27, 3, 9,
19, 13, 30, 6,
22, 11, 4, 25
};
DES::DES(quint64 key)
{
keygen(key);
}
quint64 DES::encrypt(quint64 block)
{
return des(block, false);
}
quint64 DES::decrypt(quint64 block)
{
return des(block, true);
}
quint64 DES::encrypt(quint64 block, quint64 key)
{
DES des(key);
return des.des(block, false);
}
quint64 DES::decrypt(quint64 block, quint64 key)
{
DES des(key);
return des.des(block, true);
}
void DES::keygen(quint64 key)
{
// initial key schedule calculation
quint64 permuted_choice_1 = 0; // 56 bits
for (quint8 i = 0; i < 56; i++)
{
permuted_choice_1 <<= 1;
permuted_choice_1 |= (key >> (64 - PC1[i])) & LB64_MASK;
}
// 28 bits
quint32 C = (quint32)((permuted_choice_1 >> 28) & 0x000000000fffffff);
quint32 D = (quint32)(permuted_choice_1 & 0x000000000fffffff);
// Calculation of the 16 keys
for (quint8 i = 0; i < 16; i++)
{
// key schedule, shifting Ci and Di
for (quint8 j = 0; j < ITERATION_SHIFT[i]; j++)
{
C = (0x0fffffff & (C << 1)) | (0x00000001 & (C >> 27));
D = (0x0fffffff & (D << 1)) | (0x00000001 & (D >> 27));
}
quint64 permuted_choice_2 = (((quint64) C) << 28) | (quint64) D;
m_subKey[i] = 0; // 48 bits (2*24)
for (quint8 j = 0; j < 48; j++)
{
m_subKey[i] <<= 1;
m_subKey[i] |= (permuted_choice_2 >> (56 - PC2[j])) & LB64_MASK;
}
}
}
quint64 DES::des(quint64 block, bool mode)
{
// applying initial permutation
block = ip(block);
// dividing T' into two 32-bit parts
quint32 L = (quint32)(block >> 32) & L64_MASK;
quint32 R = (quint32)(block & L64_MASK);
// 16 rounds
for (quint8 i = 0; i < 16; i++)
{
quint32 F = mode ? f(R, m_subKey[15 - i]) : f(R, m_subKey[i]);
feistel(L, R, F);
}
// swapping the two parts
block = (((quint64) R) << 32) | (quint64) L;
// applying final permutation
return fp(block);
}
quint64 DES::ip(quint64 block) // initial Permutation
{
// initial permutation
quint64 result = 0;
for (quint8 i = 0; i < 64; i++)
{
result <<= 1;
result |= (block >> (64 - IP[i])) & LB64_MASK;
}
return result;
}
quint64 DES::fp(quint64 block) // final Permutation
{
// inverse initial permutation
quint64 result = 0;
for (quint8 i = 0; i < 64; i++)
{
result <<= 1;
result |= (block >> (64 - FP[i])) & LB64_MASK;
}
return result;
}
void DES::feistel(quint32 &L, quint32 &R, quint32 F)
{
quint32 temp = R;
R = L ^ F;
L = temp;
}
quint32 DES::f(quint32 R, quint64 k) // f(R,k) function
{
// applying expansion permutation and returning 48-bit data
quint64 s_input = 0;
for (quint8 i = 0; i < 48; i++)
{
s_input <<= 1;
s_input |= (quint64)((R >> (32 - EXPANSION[i])) & LB32_MASK);
}
// XORing expanded Ri with Ki, the round key
s_input = s_input ^ k;
// applying S-Boxes function and returning 32-bit data
quint32 s_output = 0;
for (quint8 i = 0; i < 8; i++)
{
// Outer bits
char row = (char)((s_input & (0x0000840000000000 >> 6 * i)) >> (42 - 6 * i));
row = (row >> 4) | (row & 0x01);
// Middle 4 bits of input
char column = (char)((s_input & (0x0000780000000000 >> 6 * i)) >> (43 - 6 * i));
s_output <<= 4;
s_output |= (quint32)(SBOX[i][16 * row + column] & 0x0f);
}
// applying the round permutation
quint32 f_result = 0;
for (quint8 i = 0; i < 32; i++)
{
f_result <<= 1;
f_result |= (s_output >> (32 - PBOX[i])) & LB32_MASK;
}
return f_result;
}
} // ProjectionWorkstation
@@ -0,0 +1,41 @@
/**
* @RDE_HEADER@
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include <QtGlobal>
#include "pwscommon_export.h"
namespace ProjectionWorkstation
{
class PWSCOMMON_EXPORT DES
{
public:
explicit DES(quint64 key);
quint64 des(quint64 block, bool mode);
quint64 encrypt(quint64 block);
quint64 decrypt(quint64 block);
static quint64 encrypt(quint64 block, quint64 key);
static quint64 decrypt(quint64 block, quint64 key);
protected:
void keygen(quint64 key);
static quint64 ip(quint64 block);
static quint64 fp(quint64 block);
static void feistel(quint32 &L, quint32 &R, quint32 F);
static quint32 f(quint32 R, quint64 k);
private:
quint64 m_subKey[16]; // 48 bits each
};
} // ProjectionWorkstation
@@ -0,0 +1,226 @@
/**
* @RDE_HEADER@
* $Id$
* $HeadURL$
* $Author$
*/
#include "pws_rfb_protocol.h"
// see https://vncdotool.readthedocs.io/en/0.8.0/rfbproto.html
namespace ProjectionWorkstation
{
namespace RfbProtocol
{
namespace STC
{
static constexpr int s_bytePerPixel = 4;
enum HextileSubEncoding : uint8_t
{
Raw = 0x01,
BackgroundSpecified = 0x02,
ForegroundSpecified = 0x04,
AnySubrects = 0x08,
SubrectsColoured = 0x10,
};
static inline int highNibble(uint8_t b)
{
return b >> 4;
}
static inline int lowNibble(uint8_t b)
{
return b & 0x0f;
}
static inline bool testBit(uint8_t bit, HextileSubEncoding enc)
{
return (bit & enc) == enc;
}
static inline uint32_t swap(uint32_t in)
{
// compiler: bswap $eax ...
uint32_t out = 0;
out |= ((in >> 24) & 0x000000ff);
out |= ((in >> 8) & 0x0000ff00);
out |= ((in << 8) & 0x00ff0000);
out |= ((in << 24) & 0xff000000);
return out;
}
static inline void fillHextileRect(RfbRectangle &rect, int xOfs, int yOfs, int w, int h, uint32_t col)
{
uint32_t *dst = reinterpret_cast<uint32_t *>(rect.pixelData.data());
for (int y = 0; y < h; ++y)
{
for (int x = 0; x < w; ++x)
{
Q_ASSERT(x + xOfs < rect.xpos + rect.width);
Q_ASSERT(y + yOfs < rect.ypos + rect.height);
dst[rect.width * (y + yOfs) + (x + xOfs)] = col;
}
}
}
static inline void copyHextileRect(RfbRectangle &rect, int xOfs, int yOfs, int w, int h, const QByteArray &srcBa)
{
uint32_t *dst = reinterpret_cast<uint32_t *>(rect.pixelData.data());
const uint32_t *src = reinterpret_cast<const uint32_t *>(srcBa.data());
for (int y = 0; y < h; ++y)
{
for (int x = 0; x < w; ++x)
{
Q_ASSERT(x + xOfs < rect.xpos + rect.width);
Q_ASSERT(y + yOfs < rect.ypos + rect.height);
dst[rect.width * (y + yOfs) + (x + xOfs)] = src[w * y + x];
}
}
}
static bool decodeHextile(RfbRectangle &rect, QDataStream &ds)
{
uint32_t bg = 0;
uint32_t fg = 0;
for (int yRect = rect.ypos; yRect < rect.ypos + rect.height; yRect += 16)
{
for (int xRect = rect.xpos; xRect < rect.xpos + rect.width; xRect += 16)
{
int w = 16;
int h = 16;
if (rect.xpos + rect.width - xRect < 16)
w = rect.xpos + rect.width - xRect;
if (rect.ypos + rect.height - yRect < 16)
h = rect.ypos + rect.height - yRect;
const int x = xRect - rect.xpos;
const int y = yRect - rect.ypos;
uint8_t subencoding = 0;
ds >> subencoding;
if (ds.status() != QDataStream::Ok)
return false;
if (testBit(subencoding, HextileSubEncoding::Raw))
{
QByteArray buf(w * h * s_bytePerPixel, Qt::Uninitialized);
ds.readRawData(buf.data(), buf.size());
if (ds.status() != QDataStream::Ok)
return false;
copyHextileRect(rect, x, y, w, h, buf);
continue;
}
if (testBit(subencoding, HextileSubEncoding::BackgroundSpecified))
{
ds >> bg;
bg = swap(bg);
if (ds.status() != QDataStream::Ok)
return false;
}
fillHextileRect(rect, x, y, w, h, bg);
if (testBit(subencoding, HextileSubEncoding::ForegroundSpecified))
{
ds >> fg;
fg = swap(fg);
if (ds.status() != QDataStream::Ok)
return false;
}
if (!testBit(subencoding, HextileSubEncoding::AnySubrects))
continue;
uint8_t nSubrects;
ds >> nSubrects;
if (ds.status() != QDataStream::Ok)
return false;
if (testBit(subencoding, HextileSubEncoding::SubrectsColoured))
{
uint8_t xy = 0;
uint8_t wh = 0;
for (int i = 0; i < nSubrects; i++)
{
ds >> fg >> xy >> wh;
fg = swap(fg);
if (ds.status() != QDataStream::Ok)
return false;
int xOfs = highNibble(xy);
int yOfs = lowNibble(xy);
int wOfs = highNibble(wh) + 1;
int hOfs = lowNibble(wh) + 1;
fillHextileRect(rect, x + xOfs, y + yOfs, wOfs, hOfs, fg);
}
}
else
{
uint8_t xy = 0;
uint8_t wh = 0;
for (int i = 0; i < nSubrects; i++)
{
ds >> xy >> wh;
if (ds.status() != QDataStream::Ok)
return false;
int xOfs = highNibble(xy);
int yOfs = lowNibble(xy);
int wOfs = highNibble(wh) + 1;
int hOfs = lowNibble(wh) + 1;
fillHextileRect(rect, x + xOfs, y + yOfs, wOfs, hOfs, fg);
}
}
}
}
return true;
}
void RfbFramebufferUpdate::fillDataFromStream(QDataStream &ds)
{
quint8 padding;
ds >> padding;
if (ds.status() != QDataStream::Ok)
return;
quint16 numberOfRectangles;
ds >> numberOfRectangles;
if (ds.status() != QDataStream::Ok)
return;
RfbRectangle rect;
for (int count = 0; count < numberOfRectangles; ++count)
{
ds >> rect.xpos >> rect.ypos >> rect.width >> rect.height >> rect.encoding;
if (ds.status() != QDataStream::Ok)
return;
const int calculatedPixelData = rect.width * rect.height * s_bytePerPixel;
if (calculatedPixelData <= 0)
continue;
rect.pixelData.resize(calculatedPixelData);
if (rect.encoding == RAW_ENCODING)
{
ds.readRawData(rect.pixelData.data(), calculatedPixelData);
if (ds.status() != QDataStream::Ok)
return;
m_rectangles.append(rect);
}
else if (rect.encoding == HEXTILE_ENCODING)
{
decodeHextile(rect, ds);
if (ds.status() != QDataStream::Ok)
return;
m_rectangles.append(rect);
}
else if (rect.encoding == LAST_RECT_PSEUDO_ENCODING)
break;
}
}
} // namespace STC
} // namespace RfbProtocol
} // namespace ProjectionWorkstation
+393
View File
@@ -0,0 +1,393 @@
/**
* @RDE_HEADER@
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include <QtGlobal>
#include <QVector>
#include <QDataStream>
namespace ProjectionWorkstation
{
namespace RfbProtocol
{
struct RfbPixelFormat
{
quint8 bitsPerPixel = 0;
quint8 depth = 0;
quint8 bigEndianFlag = 0;
quint8 trueColourFlag = 0;
quint8 redShift = 0;
quint8 greenShift = 0;
quint8 blueShift = 0;
quint16 redMax = 0;
quint16 greenMax = 0;
quint16 blueMax = 0;
};
struct RfbRectangle
{
QByteArray pixelData;
quint16 xpos = 0;
quint16 ypos = 0;
quint16 width = 0;
quint16 height = 0;
qint32 encoding = 0;
};
static constexpr int RAW_ENCODING = 0;
static constexpr int HEXTILE_ENCODING = 5;
static constexpr int LAST_RECT_PSEUDO_ENCODING = -224;
namespace STC
{
enum Type
{
// Official Values v
RFB_FRAMEBUFFER_UPDATE = 0,
RFB_SET_COLORMAP = 1,
RFB_RING_BELL = 2,
RFB_SERVER_CUTTEXT = 3,
// User Defined Values v
RFB_VERSION = 256,
RFB_SUP_SECURITY_TYPES = 257,
RFB_VNC_AUTHENTICATION = 258,
RFB_SECURITY_RESULT = 259,
RFB_SERVER_INIT = 260
};
class ServerToClient
{
public:
virtual ~ServerToClient() = default;
friend QDataStream &operator>>(QDataStream &ds, ServerToClient &stc)
{
stc.fillDataFromStream(ds);
return ds;
}
protected:
explicit ServerToClient(Type type) : m_type(type) {}
virtual void fillDataFromStream(QDataStream &ds) = 0;
Type m_type;
}; // ServerToClient
// --------------------------------------------------
class RfbVersion final : public ServerToClient
{
quint8 m_minor = 0;
quint8 m_major = 0;
public:
constexpr static Type TYPE = RFB_VERSION;
RfbVersion() : ServerToClient(TYPE) {}
quint8 majorVersion() const { return m_major; }
quint8 minorVersion() const { return m_minor; }
void fillDataFromStream(QDataStream &ds) override final
{
QVarLengthArray<char, 12> data(12);
ds.readRawData(data.data(), 12);
if (ds.status() == QDataStream::Ok)
{
m_major = data[6] - '0';
m_minor = data[10] - '0';
}
}
}; // RfbVersion
// --------------------------------------------------
class RfbSupportedSecurityTypes final : public ServerToClient
{
QVector<quint8> m_secTypes;
public:
constexpr static Type TYPE = RFB_SUP_SECURITY_TYPES;
RfbSupportedSecurityTypes() : ServerToClient(TYPE) {}
const QVector<quint8> &types() const { return m_secTypes; }
void fillDataFromStream(QDataStream &ds) override final
{
quint8 amountOfSupportedTypes;
ds >> amountOfSupportedTypes;
for (int count = 0; count < amountOfSupportedTypes; ++count)
{
quint8 type;
ds >> type;
m_secTypes.append(type);
}
}
}; // RfbSupportedSecurityTypes
// --------------------------------------------------
class RfbVncAuthentication final : public ServerToClient
{
quint64 m_challengeBlockA = 0;
quint64 m_challengeBlockB = 0;
public:
constexpr static Type TYPE = RFB_VNC_AUTHENTICATION;
RfbVncAuthentication() : ServerToClient(TYPE) {}
quint64 challengeBlockA() const { return m_challengeBlockA; }
quint64 challengeBlockB() const { return m_challengeBlockB; }
void fillDataFromStream(QDataStream &ds) override final
{
ds >> m_challengeBlockA;
ds >> m_challengeBlockB;
}
}; // RfbVncAuthentication
// --------------------------------------------------
class RfbSecurityResult : public ServerToClient
{
QByteArray m_reason;
quint32 m_resultCode = 0;
public:
constexpr static Type TYPE = RFB_SECURITY_RESULT;
RfbSecurityResult() : ServerToClient(TYPE) {}
quint32 resultCode() const { return m_resultCode; }
const QByteArray &reason() const { return m_reason; }
void fillDataFromStream(QDataStream &ds) override final
{
ds >> m_resultCode;
if (ds.status() == QDataStream::Ok)
{
if (m_resultCode == 0)
return;
else
{
quint32 reasonLength;
ds >> reasonLength;
QByteArray reason(reasonLength, Qt::Uninitialized);
ds.readRawData(reason.data(), reasonLength);
m_reason = reason;
}
}
}
}; // RfbSecurityResult
// --------------------------------------------------
class RfbServerInit final : public ServerToClient
{
QByteArray m_name;
RfbPixelFormat m_pixelFormat;
quint16 m_frameBufferWidth = 0;
quint16 m_frameBufferHeight = 0;
public:
constexpr static Type TYPE = RFB_SERVER_INIT;
RfbServerInit() : ServerToClient(TYPE) {}
quint16 frameBufferWidth() const { return m_frameBufferWidth; }
quint16 frameBufferHeight() const { return m_frameBufferHeight; }
const RfbPixelFormat &pixelFormat() const { return m_pixelFormat; }
const QByteArray &name() const { return m_name; }
void fillDataFromStream(QDataStream &ds) override final
{
ds >> m_frameBufferWidth >> m_frameBufferHeight;
ds >> m_pixelFormat.bitsPerPixel >> m_pixelFormat.depth >> m_pixelFormat.bigEndianFlag >> m_pixelFormat.trueColourFlag
>> m_pixelFormat.redMax >> m_pixelFormat.greenMax >> m_pixelFormat.blueMax
>> m_pixelFormat.redShift >> m_pixelFormat.greenShift >> m_pixelFormat.blueShift;
ds.skipRawData(3); // padding
quint32 nameLength;
ds >> nameLength;
QByteArray nameData(nameLength, Qt::Uninitialized);
ds.readRawData(nameData.data(), nameLength);
m_name = nameData;
}
}; // RfbServerInit
// --------------------------------------------------
class RfbFramebufferUpdate final : public ServerToClient
{
QVector<RfbRectangle> m_rectangles;
public:
RfbFramebufferUpdate() : ServerToClient(RFB_FRAMEBUFFER_UPDATE) {}
const QVector<RfbRectangle> &rectangles() const { return m_rectangles; }
void fillDataFromStream(QDataStream &ds) override final;
}; // RfbFramebufferUpdate
// --------------------------------------------------
class RfbSetColormap final : public ServerToClient
{
public:
RfbSetColormap() : ServerToClient(RFB_SET_COLORMAP) {}
void fillDataFromStream(QDataStream &ds) override final
{
if (ds.status() == QDataStream::Ok)
{
quint8 padding;
ds >> padding;
quint16 firstColor, numberOfColors;
ds >> firstColor >> numberOfColors;
if (ds.status() != QDataStream::Ok)
return;
qint32 payloadLen = numberOfColors + 3 * sizeof(quint16); // rgb, 16 bit
// we don't care
ds.skipRawData(payloadLen);
}
else
ds.setStatus(QDataStream::ReadPastEnd);
}
}; // RfbSetColormap
// --------------------------------------------------
class RfbRingBell final : public ServerToClient
{
public:
RfbRingBell() : ServerToClient(RFB_RING_BELL) {}
void fillDataFromStream(QDataStream &) override final
{
// nothing to do here
}
}; // RfbRingBell
// --------------------------------------------------
class RfbServerCutText final : public ServerToClient
{
QByteArray m_text;
public:
RfbServerCutText() : ServerToClient(RFB_SERVER_CUTTEXT) {}
const QByteArray &text() const { return m_text; }
void fillDataFromStream(QDataStream &ds) override final
{
if (ds.status() == QDataStream::Ok)
{
ds.skipRawData(3); //padding
quint32 length;
ds >> length;
QByteArray data(length, Qt::Uninitialized);
ds.readRawData(data.data(), length);
m_text = data;
}
else ds.setStatus(QDataStream::ReadPastEnd);
}
}; // RfbServerCutText
// --------------------------------------------------
} // STC
namespace CTS
{
inline QByteArray RfbVersion(quint8 majorVersion, quint8 minorVersion)
{
QByteArray data;
QString version = QStringLiteral("RFB 00%1.00%2\n");
data.append(version.arg(majorVersion).arg(minorVersion).toLatin1());
return data;
}
inline QByteArray RfbSecurityType(quint8 type)
{
QByteArray data;
data.append(type);
return data;
}
inline QByteArray RfbVncAuthentication(quint64 blockA, quint64 blockB)
{
QByteArray data;
QDataStream stream(&data, QIODevice::WriteOnly);
stream << blockA << blockB;
return data;
}
inline QByteArray RfbClientInit(bool sharedConnection)
{
quint8 share = sharedConnection ? 1 : 0;
QByteArray data;
data.append(share);
return data;
}
inline QByteArray RfbFrameBufferUpdateRequest(bool isIncremental, quint16 xPos, quint16 yPos, quint16 width, quint16 height)
{
quint8 type = 3;
quint8 incremental = isIncremental ? 1 : 0;
QByteArray data;
QDataStream stream(&data, QIODevice::WriteOnly);
stream << type << incremental << xPos << yPos << width << height;
return data;
}
inline QByteArray RfbPointerEvent(quint8 buttonMask, quint16 xpos, quint16 ypos)
{
quint8 type = 5;
QByteArray data;
QDataStream stream(&data, QIODevice::WriteOnly);
stream << type << buttonMask << xpos << ypos;
return data;
}
inline QByteArray RfbKeyEvent(quint32 key, bool isPressed)
{
quint8 type = 4;
quint8 pressed = isPressed ? 1 : 0;
QByteArray data;
QDataStream stream(&data, QIODevice::WriteOnly);
stream << type << pressed << quint16(0) << key;
return data;
}
inline QByteArray RfbSetEncodings(const QVector<qint32> &encodings)
{
quint8 type = 2;
QByteArray data;
QDataStream stream(&data, QIODevice::WriteOnly);
stream << type << quint8(0) << quint16(encodings.length());
for (qint32 encoding : encodings)
stream << encoding;
return data;
}
} // CTS
} // RfbProtocol
} // ProjectionWorkstation
@@ -0,0 +1,345 @@
/**
* @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
+70
View File
@@ -0,0 +1,70 @@
/**
* @RDE_HEADER@
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include "pws_rfb_protocol.h"
#include <QObject>
#include <QTcpSocket>
#include <QDataStream>
#include <QHostAddress>
#include <Plugins/ProjectionWorkstation/Common/pwscommon_export.h>
#include <LtsProjectionWorkstation/logger.h>
namespace ProjectionWorkstation
{
class PWSCOMMON_EXPORT VncHandler : public QObject
{
Q_OBJECT
public:
VncHandler(const QHostAddress &host, quint16 port, QObject *parent = nullptr);
void setNextExpectedMessageType(RfbProtocol::STC::Type type) { m_nextType = type; }
void connectToHost();
void connectToHost(const QHostAddress &host, quint16 port);
void writeData(const QByteArray &data);
signals:
void connected();
void disconnected();
void frameBufferUpdateReceived(const QVector<RfbProtocol::RfbRectangle> &rectangles);
void beepReceived();
void cutTextReceived(const QString &txt);
private slots:
void onReadyRead();
void slotOnDisconnected();
void error();
private:
bool handleData(QDataStream &stream);
bool handleFrameBufferUpdate(QDataStream &stream);
bool handleSetColormap(QDataStream &stream);
bool handleRingBell(QDataStream &stream);
bool handleServerCuttext(QDataStream &stream);
void resetHandler();
bool handleVersion(const RfbProtocol::STC::RfbVersion &version);
bool handleSupportedSecurityTypes(const RfbProtocol::STC::RfbSupportedSecurityTypes &supportedTypes);
bool handleVncAuthentication(const RfbProtocol::STC::RfbVncAuthentication &authentication);
static quint64 convertPassword(const char *password);
static quint8 reverseByte(quint8 b);
bool handleSecurityResult(const RfbProtocol::STC::RfbSecurityResult &result);
bool handleServerInit(const RfbProtocol::STC::RfbServerInit &serverInit);
LtsProjectionWorkstation::LOGGER_T m_logger;
RfbProtocol::STC::Type m_nextType;
RfbProtocol::STC::RfbServerInit m_serverInit;
QTcpSocket *m_socket;
QByteArray m_buffer;
QHostAddress m_host;
quint16 m_port;
};
} // ProjectionWorkstation