From c3e30dcea19f3d527bbadbf21f49c6dae6d90584 Mon Sep 17 00:00:00 2001 From: Lauri Kasanen Date: Mon, 15 Mar 2021 13:48:56 +0200 Subject: [PATCH] Add support for DLP_Region --- common/rfb/ServerCore.cxx | 4 + common/rfb/ServerCore.h | 1 + common/rfb/VNCServerST.cxx | 153 +++++++++++++++++++++++++++++++++++ common/rfb/VNCServerST.h | 8 ++ unix/xserver/hw/vnc/Xvnc.man | 6 ++ 5 files changed, 172 insertions(+) diff --git a/common/rfb/ServerCore.cxx b/common/rfb/ServerCore.cxx index 900a21f..94f1196 100644 --- a/common/rfb/ServerCore.cxx +++ b/common/rfb/ServerCore.cxx @@ -163,6 +163,10 @@ rfb::StringParameter rfb::Server::DLP_ClipLog ("DLP_Log", "Log clipboard/kbd actions. Accepts off, info or verbose", "off"); +rfb::StringParameter rfb::Server::DLP_Region +("DLP_Region", + "Black out anything outside this region", + ""); rfb::StringParameter rfb::Server::maxVideoResolution ("MaxVideoResolution", diff --git a/common/rfb/ServerCore.h b/common/rfb/ServerCore.h index bd97265..77314bd 100644 --- a/common/rfb/ServerCore.h +++ b/common/rfb/ServerCore.h @@ -49,6 +49,7 @@ namespace rfb { static IntParameter DLP_ClipDelay; static IntParameter DLP_KeyRateLimit; static StringParameter DLP_ClipLog; + static StringParameter DLP_Region; static IntParameter jpegVideoQuality; static IntParameter webpVideoQuality; static StringParameter maxVideoResolution; diff --git a/common/rfb/VNCServerST.cxx b/common/rfb/VNCServerST.cxx index a0b4998..1e2a12f 100644 --- a/common/rfb/VNCServerST.cxx +++ b/common/rfb/VNCServerST.cxx @@ -85,6 +85,42 @@ extern rfb::StringParameter basicauth; // -=- Constructors/Destructor +static void mixedPercentages() { + slog.error("Mixing percentages and absolute values in DLP_Region is not allowed"); + exit(1); +} + +static void parseRegionPart(const bool percents, rdr::U16 &pcdest, int &dest, + char **inptr) { + char *nextptr, *ptr; + ptr = *inptr; + int val = strtol(ptr, &nextptr, 10); + if (!*ptr || ptr == nextptr) { + slog.error("Invalid value for DLP_Region"); + exit(1); + } + ptr = nextptr; + if (*ptr == '%') { + if (!percents) + mixedPercentages(); + pcdest = val; + + if (val < 0 || val > 100) { + slog.error("Percent must be 0-100"); + exit(1); + } + + ptr++; + } else if (percents) { + mixedPercentages(); + } + dest = val; + + for (; *ptr && *ptr == ','; ptr++); + + *inptr = ptr; +} + VNCServerST::VNCServerST(const char* name_, SDesktop* desktop_) : blHosts(&blacklist), desktop(desktop_), desktopStarted(false), blockCounter(0), pb(0), ledState(ledUnknown), @@ -98,6 +134,64 @@ VNCServerST::VNCServerST(const char* name_, SDesktop* desktop_) lastUserInputTime = lastDisconnectTime = time(0); slog.debug("creating single-threaded server %s", name.buf); + DLPRegion.enabled = DLPRegion.percents = false; + + if (Server::DLP_Region[0]) { + unsigned len = strlen(Server::DLP_Region); + unsigned i; + unsigned commas = 0; + int val; + char *ptr, *nextptr; + + for (i = 0; i < len; i++) { + if (Server::DLP_Region[i] == ',') + commas++; + } + + if (commas != 3) { + slog.error("DLP_Region must contain four values"); + exit(1); + } + + ptr = (char *) (const char *) Server::DLP_Region; + + val = strtol(ptr, &nextptr, 10); + if (!*ptr || ptr == nextptr) { + slog.error("Invalid value for DLP_Region"); + exit(1); + } + ptr = nextptr; + if (*ptr == '%') { + DLPRegion.percents = true; + DLPRegion.pcx1 = val; + ptr++; + } + DLPRegion.x1 = val; + + for (; *ptr && *ptr == ','; ptr++); + + parseRegionPart(DLPRegion.percents, DLPRegion.pcy1, DLPRegion.y1, + &ptr); + parseRegionPart(DLPRegion.percents, DLPRegion.pcx2, DLPRegion.x2, + &ptr); + parseRegionPart(DLPRegion.percents, DLPRegion.pcy2, DLPRegion.y2, + &ptr); + + // Validity checks + if (!DLPRegion.percents) { + if (DLPRegion.x1 > 0 && DLPRegion.x2 > 0 && DLPRegion.x2 <= DLPRegion.x1) { + slog.error("DLP_Region x2 must be > x1"); + exit(1); + } + if (DLPRegion.y1 > 0 && DLPRegion.y2 > 0 && DLPRegion.y2 <= DLPRegion.y1) { + slog.error("DLP_Region y2 must be > y1"); + exit(1); + } + } + + DLPRegion.enabled = 1; + } + kasmpasswdpath[0] = '\0'; wordexp_t wexp; if (!wordexp(rfb::Server::kasmPasswordFile, &wexp, WRDE_NOCMD)) @@ -706,6 +800,62 @@ static void checkAPIMessages(network::GetAPIMessager *apimessager) pthread_mutex_unlock(&apimessager->userMutex); } +void VNCServerST::blackOut() +{ + // Compute the region, since the resolution may have changed + rdr::U16 x1, y1, x2, y2; + + if (DLPRegion.percents) { + x1 = DLPRegion.pcx1 ? DLPRegion.pcx1 * pb->getRect().width() / 100 : 0; + y1 = DLPRegion.pcy1 ? DLPRegion.pcy1 * pb->getRect().height() / 100 : 0; + x2 = DLPRegion.pcx2 ? (100 - DLPRegion.pcx2) * pb->getRect().width() / 100 : pb->getRect().width(); + y2 = DLPRegion.pcy2 ? (100 - DLPRegion.pcy2) * pb->getRect().height() / 100 : pb->getRect().height(); + } else { + x1 = abs(DLPRegion.x1); + y1 = abs(DLPRegion.y1); + x2 = pb->getRect().width(); + y2 = pb->getRect().height(); + + if (DLPRegion.x2 < 0) + x2 += DLPRegion.x2; + else if (DLPRegion.x2 > 0) + x2 = DLPRegion.x2; + + if (DLPRegion.y2 < 0) + y2 += DLPRegion.y2; + else if (DLPRegion.y2 > 0) + y2 = DLPRegion.y2; + } + + if (y2 > pb->getRect().height()) + y2 = pb->getRect().height() - 1; + if (x2 > pb->getRect().width()) + x2 = pb->getRect().width() - 1; + + //slog.info("DLP_Region vals %u,%u %u,%u", x1, y1, x2, y2); + + ManagedPixelBuffer *mpb = (ManagedPixelBuffer *) pb; + int stride; + rdr::U8 *data = mpb->getBufferRW(mpb->getRect(), &stride); + stride *= 4; + + rdr::U16 y; + const rdr::U16 w = pb->getRect().width(); + const rdr::U16 h = pb->getRect().height(); + for (y = 0; y < h; y++) { + if (y < y1 || y > y2) { + memset(data, 0, stride); + } else { + if (x1) + memset(data, 0, x1 * 4); + if (x2) + memset(&data[x2 * 4], 0, (w - x2) * 4); + } + + data += stride; + } +} + // writeUpdate() is called on a regular interval in order to see what // updates are pending and propagates them to the update tracker for // each client. It uses the ComparingUpdateTracker's compare() method @@ -723,6 +873,9 @@ void VNCServerST::writeUpdate() assert(blockCounter == 0); assert(desktopStarted); + if (DLPRegion.enabled) + blackOut(); + comparer->getUpdateInfo(&ui, pb->getRect()); toCheck = ui.changed.union_(ui.copied); diff --git a/common/rfb/VNCServerST.h b/common/rfb/VNCServerST.h index 2432cd4..b42f63c 100644 --- a/common/rfb/VNCServerST.h +++ b/common/rfb/VNCServerST.h @@ -235,6 +235,7 @@ namespace rfb { void stopFrameClock(); int msToNextUpdate(); void writeUpdate(); + void blackOut(); Region getPendingRegion(); const RenderedCursor* getRenderedCursor(); @@ -256,6 +257,13 @@ namespace rfb { int inotifyfd; network::GetAPIMessager *apimessager; + + struct { + bool enabled; + int x1, y1, x2, y2; + bool percents; + rdr::U16 pcx1, pcy1, pcx2, pcy2; + } DLPRegion; }; }; diff --git a/unix/xserver/hw/vnc/Xvnc.man b/unix/xserver/hw/vnc/Xvnc.man index f8ace17..299fe7d 100644 --- a/unix/xserver/hw/vnc/Xvnc.man +++ b/unix/xserver/hw/vnc/Xvnc.man @@ -280,6 +280,12 @@ Kasm exposes a few settings to the client the standard VNC does not. This param lets the server ignore those. . .TP +.B \-DLP_Region \fIx1,y1,x2,y2\fP +Black out anything outside this region. x1,y1 is the upper-left corner, +and x2,y2 the lower-left. In addition to absolute pixel values, percentages +are allowed, zero means "default", and a negative number means "border". +. +.TP .B \-DLP_ClipSendMax \fIbytes\fP Limit clipboard bytes to send to clients in one transaction. Default 10,000. 0 disables the limit, use \fBSendCutText\fP to disable clipboard sending entirely.