This commit is contained in:
Lauri Kasanen
2022-07-26 10:38:14 +00:00
committed by Matthew McClaskey
parent ba902f8194
commit 3b40a92548
72 changed files with 3314 additions and 52 deletions

View File

@@ -45,6 +45,7 @@ ConnParams::ConnParams()
supportsWEBP(false),
supportsSetDesktopSize(false), supportsFence(false),
supportsContinuousUpdates(false), supportsExtendedClipboard(false),
supportsUdp(false),
compressLevel(2), qualityLevel(-1), fineQualityLevel(-1),
subsampling(subsampleUndefined), name_(0), cursorPos_(0, 0), verStrPos(0),
ledState_(ledUnknown), shandler(NULL)

View File

@@ -117,6 +117,8 @@ namespace rfb {
bool supportsContinuousUpdates;
bool supportsExtendedClipboard;
bool supportsUdp;
int compressLevel;
int qualityLevel;
int fineQualityLevel;

View File

@@ -622,7 +622,7 @@ Encoder *EncodeManager::startRect(const Rect& rect, int type, const bool trackQu
if (isWebp)
klass = encoderTightWEBP;
beforeLength = conn->getOutStream()->length();
beforeLength = conn->getOutStream(conn->cp.supportsUdp)->length();
stats[klass][activeType].rects++;
stats[klass][activeType].pixels += rect.area();
@@ -655,7 +655,7 @@ void EncodeManager::endRect(const uint8_t isWebp)
conn->writer()->endRect();
length = conn->getOutStream()->length() - beforeLength;
length = conn->getOutStream(conn->cp.supportsUdp)->length() - beforeLength;
klass = activeEncoders[activeType];
if (isWebp)
@@ -669,7 +669,7 @@ void EncodeManager::writeCopyPassRects(const std::vector<CopyPassRect>& copypass
Region lossyCopy;
beforeLength = conn->getOutStream()->length();
beforeLength = conn->getOutStream(conn->cp.supportsUdp)->length();
for (rect = copypassed.begin(); rect != copypassed.end(); ++rect) {
int equiv;
@@ -689,7 +689,7 @@ void EncodeManager::writeCopyPassRects(const std::vector<CopyPassRect>& copypass
lossyRegion.assign_union(lossyCopy);
}
copyStats.bytes += conn->getOutStream()->length() - beforeLength;
copyStats.bytes += conn->getOutStream(conn->cp.supportsUdp)->length() - beforeLength;
}
void EncodeManager::writeCopyRects(const Region& copied, const Point& delta)
@@ -699,7 +699,7 @@ void EncodeManager::writeCopyRects(const Region& copied, const Point& delta)
Region lossyCopy;
beforeLength = conn->getOutStream()->length();
beforeLength = conn->getOutStream(conn->cp.supportsUdp)->length();
copied.get_rects(&rects, delta.x <= 0, delta.y <= 0);
for (rect = rects.begin(); rect != rects.end(); ++rect) {
@@ -714,7 +714,7 @@ void EncodeManager::writeCopyRects(const Region& copied, const Point& delta)
rect->tl.y - delta.y);
}
copyStats.bytes += conn->getOutStream()->length() - beforeLength;
copyStats.bytes += conn->getOutStream(conn->cp.supportsUdp)->length() - beforeLength;
lossyCopy = lossyRegion;
lossyCopy.translate(delta);

View File

@@ -62,6 +62,8 @@ SConnection::SConnection()
defaultMinorVersion = 3;
cp.setVersion(defaultMajorVersion, defaultMinorVersion);
udps = new network::UdpStream;
}
SConnection::~SConnection()
@@ -72,6 +74,7 @@ SConnection::~SConnection()
delete writer_;
writer_ = 0;
strFree(clientClipboard);
delete udps;
}
void SConnection::setStreams(rdr::InStream* is_, rdr::OutStream* os_)
@@ -348,7 +351,7 @@ void SConnection::approveConnection(bool accept, const char* reason)
if (accept) {
state_ = RFBSTATE_INITIALISATION;
reader_ = new SMsgReader(this, is);
writer_ = new SMsgWriter(&cp, os);
writer_ = new SMsgWriter(&cp, os, udps);
authSuccess();
} else {
state_ = RFBSTATE_INVALID;

View File

@@ -24,6 +24,7 @@
#ifndef __RFB_SCONNECTION_H__
#define __RFB_SCONNECTION_H__
#include <network/Udp.h>
#include <rdr/InStream.h>
#include <rdr/OutStream.h>
#include <rfb/SMsgHandler.h>
@@ -173,7 +174,7 @@ namespace rfb {
SMsgWriter* writer() { return writer_; }
rdr::InStream* getInStream() { return is; }
rdr::OutStream* getOutStream() { return os; }
rdr::OutStream* getOutStream(const bool udp = false) { return udp ? udps : os; }
enum stateEnum {
RFBSTATE_UNINITIALISED,
@@ -219,6 +220,7 @@ namespace rfb {
int defaultMajorVersion, defaultMinorVersion;
rdr::InStream* is;
rdr::OutStream* os;
network::UdpStream *udps;
SMsgReader* reader_;
SMsgWriter* writer_;
SecurityServer security;

View File

@@ -104,4 +104,3 @@ void SMsgHandler::setDesktopSize(int fb_width, int fb_height,
cp.height = fb_height;
cp.screenLayout = layout;
}

View File

@@ -95,6 +95,9 @@ namespace rfb {
// handler will send a pseudo-rect back, signalling server support.
virtual void supportsQEMUKeyEvent();
virtual void udpUpgrade(const char *resp) = 0;
virtual void udpDowngrade() = 0;
ConnParams cp;
};
}

View File

@@ -17,6 +17,7 @@
* USA.
*/
#include <stdio.h>
#include <network/Udp.h>
#include <rdr/InStream.h>
#include <rdr/ZlibInStream.h>
@@ -96,6 +97,9 @@ void SMsgReader::readMsg()
case msgTypeQEMUClientMessage:
readQEMUMessage();
break;
case msgTypeUpgradeToUdp:
readUpgradeToUdp();
break;
default:
fprintf(stderr, "unknown message type %d\n", msgType);
throw Exception("unknown message type");
@@ -329,3 +333,27 @@ void SMsgReader::readQEMUKeyEvent()
}
handler->keyEvent(keysym, keycode, down);
}
void SMsgReader::readUpgradeToUdp()
{
char buf[4096], resp[4096];
rdr::U16 len = is->readU16();
if (len >= sizeof(buf)) {
vlog.error("Ignoring udp upgrade with too large payload");
is->skip(len);
return;
}
if (!len) {
handler->udpDowngrade();
return;
}
is->readBytes(buf, len);
buf[len] = '\0';
wuGotHttp(buf, len, resp);
handler->udpUpgrade(resp);
}

View File

@@ -63,6 +63,8 @@ namespace rfb {
void readQEMUMessage();
void readQEMUKeyEvent();
void readUpgradeToUdp();
SMsgHandler* handler;
rdr::InStream* is;
};

View File

@@ -37,8 +37,8 @@ using namespace rfb;
static LogWriter vlog("SMsgWriter");
SMsgWriter::SMsgWriter(ConnParams* cp_, rdr::OutStream* os_)
: cp(cp_), os(os_),
SMsgWriter::SMsgWriter(ConnParams* cp_, rdr::OutStream* os_, rdr::OutStream* udps_)
: cp(cp_), os(os_), udps(udps_),
nRectsInUpdate(0), nRectsInHeader(0),
needSetDesktopSize(false), needExtendedDesktopSize(false),
needSetDesktopName(false), needSetCursor(false),
@@ -362,6 +362,16 @@ void SMsgWriter::writeFramebufferUpdateEnd()
os->writeU16(0);
os->writeU16(0);
os->writeU32(pseudoEncodingLastRect);
// Send an UDP flip marker, if needed
if (cp->supportsUdp) {
udps->writeS16(0);
udps->writeS16(0);
udps->writeU16(0);
udps->writeU16(0);
udps->writeU32(pseudoEncodingLastRect);
udps->flush();
}
}
endMsg();
@@ -370,8 +380,13 @@ void SMsgWriter::writeFramebufferUpdateEnd()
void SMsgWriter::writeCopyRect(const Rect& r, int srcX, int srcY)
{
startRect(r,encodingCopyRect);
os->writeU16(srcX);
os->writeU16(srcY);
if (cp->supportsUdp) {
udps->writeU16(srcX);
udps->writeU16(srcY);
} else {
os->writeU16(srcX);
os->writeU16(srcY);
}
endRect();
}
@@ -380,16 +395,27 @@ void SMsgWriter::startRect(const Rect& r, int encoding)
if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
throw Exception("SMsgWriter::startRect: nRects out of sync");
os->writeS16(r.tl.x);
os->writeS16(r.tl.y);
os->writeU16(r.width());
os->writeU16(r.height());
os->writeU32(encoding);
if (cp->supportsUdp) {
udps->writeS16(r.tl.x);
udps->writeS16(r.tl.y);
udps->writeU16(r.width());
udps->writeU16(r.height());
udps->writeU32(encoding);
} else {
os->writeS16(r.tl.x);
os->writeS16(r.tl.y);
os->writeU16(r.width());
os->writeU16(r.height());
os->writeU32(encoding);
}
}
void SMsgWriter::endRect()
{
os->flush();
if (cp->supportsUdp)
udps->flush();
else
os->flush();
}
void SMsgWriter::startMsg(int type)
@@ -712,3 +738,14 @@ void SMsgWriter::writeQEMUKeyEventRect()
os->writeU16(0);
os->writeU32(pseudoEncodingQEMUKeyEvent);
}
void SMsgWriter::writeUdpUpgrade(const char *resp)
{
startMsg(msgTypeUpgradeToUdp);
rdr::U16 len = strlen(resp);
os->writeU16(len);
os->writeBytes(resp, len);
endMsg();
}

View File

@@ -38,7 +38,7 @@ namespace rfb {
class SMsgWriter {
public:
SMsgWriter(ConnParams* cp, rdr::OutStream* os);
SMsgWriter(ConnParams* cp, rdr::OutStream* os, rdr::OutStream *udps);
virtual ~SMsgWriter();
// writeServerInit() must only be called at the appropriate time in the
@@ -127,6 +127,8 @@ namespace rfb {
void startRect(const Rect& r, int enc);
void endRect();
void writeUdpUpgrade(const char *resp);
protected:
void startMsg(int type);
void endMsg();
@@ -157,6 +159,7 @@ namespace rfb {
ConnParams* cp;
rdr::OutStream* os;
rdr::OutStream* udps;
int nRectsInUpdate;
int nRectsInHeader;

View File

@@ -217,6 +217,16 @@ rfb::StringParameter rfb::Server::kasmPasswordFile
"Password file for BasicAuth, created with the kasmvncpasswd utility.",
"~/.kasmpasswd");
rfb::StringParameter rfb::Server::publicIP
("publicIP",
"The server's public IP, for UDP negotiation. If not set, will be queried via the internet.",
"");
rfb::IntParameter rfb::Server::udpFullFrameFrequency
("udpFullFrameFrequency",
"Send a full frame every N frames for clients using UDP. 0 to disable",
0, 0, 1000);
static void bandwidthPreset() {
rfb::Server::dynamicQualityMin.setParam(2);
rfb::Server::dynamicQualityMax.setParam(9);

View File

@@ -60,7 +60,9 @@ namespace rfb {
static IntParameter videoOutTime;
static IntParameter videoArea;
static IntParameter videoScaling;
static IntParameter udpFullFrameFrequency;
static StringParameter kasmPasswordFile;
static StringParameter publicIP;
static BoolParameter printVideoArea;
static BoolParameter protocol3_3;
static BoolParameter alwaysShared;

View File

@@ -104,7 +104,7 @@ void TightEncoder::writeSolidRect(int width, int height,
{
rdr::OutStream* os;
os = conn->getOutStream();
os = conn->getOutStream(conn->cp.supportsUdp);
os->writeU8(tightFill << 4);
writePixels(colour, pf, 1, os);
@@ -165,9 +165,11 @@ void TightEncoder::writeFullColourRect(const PixelBuffer* pb, const Palette& pal
const rdr::U8* buffer;
int stride, h;
os = conn->getOutStream();
os->writeU8(streamId << 4);
os = conn->getOutStream(conn->cp.supportsUdp);
if (conn->cp.supportsUdp)
os->writeU8((streamId << 4) | (1 << streamId));
else
os->writeU8(streamId << 4);
// Set up compression
if ((pb->getPF().bpp != 32) || !pb->getPF().is888())
@@ -238,13 +240,15 @@ rdr::OutStream* TightEncoder::getZlibOutStream(int streamId, int level, size_t l
// Minimum amount of data to be compressed. This value should not be
// changed, doing so will break compatibility with existing clients.
if (length < 12)
return conn->getOutStream();
return conn->getOutStream(conn->cp.supportsUdp);
assert(streamId >= 0);
assert(streamId < 4);
zlibStreams[streamId].setUnderlying(&memStream);
zlibStreams[streamId].setCompressionLevel(level);
if (conn->cp.supportsUdp)
zlibStreams[streamId].resetDeflate();
return &zlibStreams[streamId];
}
@@ -261,7 +265,7 @@ void TightEncoder::flushZlibOutStream(rdr::OutStream* os_)
zos->flush();
zos->setUnderlying(NULL);
os = conn->getOutStream();
os = conn->getOutStream(conn->cp.supportsUdp);
writeCompact(os, memStream.length());
os->writeBytes(memStream.data(), memStream.length());

View File

@@ -38,9 +38,12 @@ void TightEncoder::writeMonoRect(int width, int height,
assert(palette.size() == 2);
os = conn->getOutStream();
os = conn->getOutStream(conn->cp.supportsUdp);
os->writeU8((streamId | tightExplicitFilter) << 4);
if (conn->cp.supportsUdp)
os->writeU8(((streamId | tightExplicitFilter) << 4) | (1 << streamId));
else
os->writeU8((streamId | tightExplicitFilter) << 4);
os->writeU8(tightFilterPalette);
// Write the palette
@@ -125,9 +128,12 @@ void TightEncoder::writeIndexedRect(int width, int height,
assert(palette.size() > 0);
assert(palette.size() <= 256);
os = conn->getOutStream();
os = conn->getOutStream(conn->cp.supportsUdp);
os->writeU8((streamId | tightExplicitFilter) << 4);
if (conn->cp.supportsUdp)
os->writeU8(((streamId | tightExplicitFilter) << 4) | (1 << streamId));
else
os->writeU8((streamId | tightExplicitFilter) << 4);
os->writeU8(tightFilterPalette);
// Write the palette

View File

@@ -147,7 +147,7 @@ void TightJPEGEncoder::writeOnly(const std::vector<uint8_t> &out) const
{
rdr::OutStream* os;
os = conn->getOutStream();
os = conn->getOutStream(conn->cp.supportsUdp);
os->writeU8(tightJpeg << 4);
@@ -184,7 +184,7 @@ void TightJPEGEncoder::writeRect(const PixelBuffer* pb, const Palette& palette)
jc.compress(buffer, stride, pb->getRect(),
pb->getPF(), quality, subsampling);
os = conn->getOutStream();
os = conn->getOutStream(conn->cp.supportsUdp);
os->writeU8(tightJpeg << 4);

View File

@@ -188,7 +188,7 @@ void TightWEBPEncoder::writeOnly(const std::vector<uint8_t> &out) const
{
rdr::OutStream* os;
os = conn->getOutStream();
os = conn->getOutStream(conn->cp.supportsUdp);
os->writeU8(tightWebp << 4);
@@ -248,7 +248,7 @@ void TightWEBPEncoder::writeRect(const PixelBuffer* pb, const Palette& palette)
vlog.error("WEBP error %u", pic.error_code);
}
os = conn->getOutStream();
os = conn->getOutStream(conn->cp.supportsUdp);
os->writeU8(tightWebp << 4);

View File

@@ -53,7 +53,7 @@ extern rfb::BoolParameter disablebasicauth;
VNCSConnectionST::VNCSConnectionST(VNCServerST* server_, network::Socket *s,
bool reverse)
: sock(s), reverseConnection(reverse),
: upgradingToUdp(false), sock(s), reverseConnection(reverse),
inProcessMessages(false),
pendingSyncFence(false), syncFence(false), fenceFlags(0),
fenceDataLen(0), fenceData(NULL), congestionTimer(this),
@@ -63,7 +63,8 @@ VNCSConnectionST::VNCSConnectionST(VNCServerST* server_, network::Socket *s,
continuousUpdates(false), encodeManager(this, &server_->encCache),
needsPermCheck(false), pointerEventTime(0),
clientHasCursor(false),
accessRights(AccessDefault), startTime(time(0)), frameTracking(false)
accessRights(AccessDefault), startTime(time(0)), frameTracking(false),
udpFramesSinceFull(0)
{
setStreams(&sock->inStream(), &sock->outStream());
peerEndpoint.buf = sock->getPeerEndpoint();
@@ -1231,7 +1232,7 @@ bool VNCSConnectionST::isCongested()
if (sock->outStream().bufferUsage() > 0)
return true;
if (!cp.supportsFence)
if (!cp.supportsFence || cp.supportsUdp)
return false;
congestion.updatePosition(sock->outStream().length());
@@ -1462,6 +1463,14 @@ void VNCSConnectionST::writeDataUpdate()
if (!pending.is_empty())
ui.copypassed.clear();
// Do we need to send a full frame?
if (Server::udpFullFrameFrequency && cp.supportsUdp) {
if (udpFramesSinceFull >= (unsigned) Server::udpFullFrameFrequency) {
udpFramesSinceFull = 0;
ui.changed.assign_union(Region(Rect(0, 0, cp.width, cp.height)));
}
}
// Return if there is nothing to send the client.
const unsigned losslessThreshold = 80 + 2 * 1000 / Server::frameRate;
@@ -1518,6 +1527,9 @@ void VNCSConnectionST::writeDataUpdate()
updates.subtract(req);
requested.clear();
if (Server::udpFullFrameFrequency && cp.supportsUdp)
udpFramesSinceFull++;
}
void VNCSConnectionST::writeBinaryClipboard()
@@ -1745,3 +1757,22 @@ bool VNCSConnectionST::checkOwnerConn() const
return false;
}
void VNCSConnectionST::udpUpgrade(const char *resp)
{
if (resp[0] == 'H') {
vlog.info("Client %s requested upgrade to udp, but WebUdp refused", sock->getPeerAddress());
} else {
vlog.info("Client %s requesting upgrade to udp", sock->getPeerAddress());
upgradingToUdp = true;
}
writer()->writeUdpUpgrade(resp);
}
void VNCSConnectionST::udpDowngrade()
{
cp.supportsUdp = false;
cp.useCopyRect = true;
vlog.info("Client %s downgrading from udp", sock->getPeerAddress());
}

View File

@@ -196,6 +196,8 @@ namespace rfb {
return encodeManager.getScalingTime();
}
bool upgradingToUdp;
private:
// SConnection callbacks
@@ -217,6 +219,8 @@ namespace rfb {
int x, int y, int w, int h);
virtual void handleClipboardAnnounce(bool available);
virtual void handleClipboardAnnounceBinary(const unsigned num, const char mimes[][32]);
virtual void udpUpgrade(const char *resp);
virtual void udpDowngrade();
virtual void supportsLocalCursor();
virtual void supportsFence();
virtual void supportsContinuousUpdates();
@@ -318,6 +322,7 @@ namespace rfb {
std::vector<CopyPassRect> copypassed;
bool frameTracking;
uint32_t udpFramesSinceFull;
};
}
#endif

View File

@@ -52,6 +52,7 @@
#include <stdlib.h>
#include <network/GetAPI.h>
#include <network/Udp.h>
#include <rfb/cpuid.h>
#include <rfb/ComparingUpdateTracker.h>
@@ -66,6 +67,7 @@
#include <rdr/types.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <sys/inotify.h>
#include <unistd.h>
@@ -796,8 +798,43 @@ int VNCServerST::msToNextUpdate()
return frameTimer.getRemainingMs();
}
static void upgradeClientToUdp(const network::GetAPIMessager::action_data &act,
std::list<VNCSConnectionST*> &clients)
{
std::list<VNCSConnectionST*>::iterator ci, ci_next;
for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
ci_next = ci; ci_next++;
if (!(*ci)->upgradingToUdp)
continue;
char buf[32];
inet_ntop(AF_INET, &act.udp.ip, buf, 32);
const char * const who = (*ci)->getPeerEndpoint();
const char *start = strchr(who, '@');
if (!start)
continue;
start++;
// Slightly inaccurate, if several clients on the same IP try to upgrade at the same time
if (strncmp(start, buf, strlen(buf)))
continue;
(*ci)->upgradingToUdp = false;
(*ci)->cp.useCopyRect = false;
((network::UdpStream *)(*ci)->getOutStream(true))->setClient((WuClient *) act.udp.client);
(*ci)->cp.supportsUdp = true;
slog.info("%s upgraded to UDP", who);
return;
}
}
static void checkAPIMessages(network::GetAPIMessager *apimessager,
rdr::U8 &trackingFrameStats, char trackingClient[])
rdr::U8 &trackingFrameStats, char trackingClient[],
std::list<VNCSConnectionST*> &clients)
{
if (pthread_mutex_lock(&apimessager->userMutex))
return;
@@ -826,6 +863,9 @@ static void checkAPIMessages(network::GetAPIMessager *apimessager,
trackingFrameStats = act.action;
memcpy(trackingClient, act.data.password, 128);
break;
case network::GetAPIMessager::UDP_UPGRADE:
upgradeClientToUdp(act, clients);
break;
}
}
@@ -991,7 +1031,7 @@ void VNCServerST::writeUpdate()
shottime = msSince(&shotstart);
trackingFrameStats = 0;
checkAPIMessages(apimessager, trackingFrameStats, trackingClient);
checkAPIMessages(apimessager, trackingFrameStats, trackingClient, clients);
}
const rdr::U8 origtrackingFrameStats = trackingFrameStats;

View File

@@ -43,7 +43,6 @@ namespace rfb {
class ListConnInfo;
class PixelBuffer;
class KeyRemapper;
class network::GetAPIMessager;
class VNCServerST : public VNCServer,
public Timer::Callback,

View File

@@ -27,6 +27,7 @@ namespace rfb {
const int encodingCoRRE = 4;
const int encodingHextile = 5;
const int encodingTight = 7;
const int encodingUdp = 8;
const int encodingZRLE = 16;
const int encodingMax = 255;

View File

@@ -32,6 +32,7 @@ namespace rfb {
const int msgTypeStats = 178;
const int msgTypeRequestFrameStats = 179;
const int msgTypeBinaryClipboard = 180;
const int msgTypeUpgradeToUdp = 181;
const int msgTypeServerFence = 248;
@@ -50,7 +51,9 @@ namespace rfb {
// kasm
const int msgTypeRequestStats = 178;
const int msgTypeFrameStats = 179;
// same as the other direction
//const int msgTypeBinaryClipboard = 180;
//const int msgTypeUpgradeToUdp = 181;
const int msgTypeClientFence = 248;