diff --git a/.gitmodules b/.gitmodules index 8232a78..34b096c 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,7 +1,7 @@ [submodule "kasmweb"] path = kasmweb url = https://github.com/kasmtech/noVNC.git - branch = master + branch = feature/VNC-275_server_idle_timeout [submodule "kasmvnc-functional-tests"] path = kasmvnc-functional-tests url = git@gitlab.com:kasm-technologies/internal/kasmvnc-functional-tests.git diff --git a/common/rfb/ConnParams.cxx b/common/rfb/ConnParams.cxx index 31a0984..6624849 100644 --- a/common/rfb/ConnParams.cxx +++ b/common/rfb/ConnParams.cxx @@ -59,6 +59,7 @@ ConnParams::ConnParams() supportsWEBP(false), supportsQOI(false), supportsSetDesktopSize(false), supportsFence(false), supportsContinuousUpdates(false), supportsExtendedClipboard(false), + supportsDisconnectNotify(false), supportsUdp(false), compressLevel(2), qualityLevel(-1), fineQualityLevel(-1), subsampling(subsampleUndefined), name_(0), cursorPos_(0, 0), verStrPos(0), @@ -147,6 +148,7 @@ void ConnParams::setEncodings(int nEncodings, const rdr::S32* encodings) supportsQEMUKeyEvent = false; supportsWEBP = false; supportsQOI = false; + supportsDisconnectNotify = false; compressLevel = -1; qualityLevel = -1; fineQualityLevel = -1; @@ -215,6 +217,10 @@ void ConnParams::setEncodings(int nEncodings, const rdr::S32* encodings) supportsQOI = true; clientparlog("qoi", true); break; + case pseudoEncodingKasmDisconnectNotify: + supportsDisconnectNotify = true; + clientparlog("disconnectNotify", true); + break; case pseudoEncodingFence: supportsFence = true; clientparlog("fence", true); diff --git a/common/rfb/ConnParams.h b/common/rfb/ConnParams.h index 0f25055..96ce5cd 100644 --- a/common/rfb/ConnParams.h +++ b/common/rfb/ConnParams.h @@ -117,6 +117,7 @@ namespace rfb { bool supportsFence; bool supportsContinuousUpdates; bool supportsExtendedClipboard; + bool supportsDisconnectNotify; bool supportsUdp; diff --git a/common/rfb/SMsgHandler.cxx b/common/rfb/SMsgHandler.cxx index ac9e588..23cd5fc 100644 --- a/common/rfb/SMsgHandler.cxx +++ b/common/rfb/SMsgHandler.cxx @@ -104,3 +104,7 @@ void SMsgHandler::setDesktopSize(int fb_width, int fb_height, cp.height = fb_height; cp.screenLayout = layout; } + +void SMsgHandler::keepAlive() +{ +} diff --git a/common/rfb/SMsgHandler.h b/common/rfb/SMsgHandler.h index 8856b9f..b8a6e7f 100644 --- a/common/rfb/SMsgHandler.h +++ b/common/rfb/SMsgHandler.h @@ -61,6 +61,7 @@ namespace rfb { virtual void sendStats(const bool toClient = true) = 0; virtual void handleFrameStats(rdr::U32 all, rdr::U32 render) = 0; + virtual void keepAlive(); virtual bool canChangeKasmSettings() const = 0; diff --git a/common/rfb/SMsgReader.cxx b/common/rfb/SMsgReader.cxx index 06d7953..933b1d8 100644 --- a/common/rfb/SMsgReader.cxx +++ b/common/rfb/SMsgReader.cxx @@ -106,6 +106,9 @@ void SMsgReader::readMsg() case msgTypeUnixRelay: readUnixRelay(); break; + case msgTypeKeepAlive: + readKeepAlive(); + break; default: fprintf(stderr, "unknown message type %d\n", msgType); throw Exception("unknown message type"); @@ -319,6 +322,11 @@ void SMsgReader::readFrameStats() handler->handleFrameStats(all, render); } +void SMsgReader::readKeepAlive() +{ + handler->keepAlive(); +} + void SMsgReader::readQEMUMessage() { int subType = is->readU8(); diff --git a/common/rfb/SMsgReader.h b/common/rfb/SMsgReader.h index 60d49c0..77af609 100644 --- a/common/rfb/SMsgReader.h +++ b/common/rfb/SMsgReader.h @@ -59,6 +59,7 @@ namespace rfb { void readRequestStats(); void readFrameStats(); void readBinaryClipboard(); + void readKeepAlive(); void readQEMUMessage(); void readQEMUKeyEvent(); diff --git a/common/rfb/SMsgWriter.cxx b/common/rfb/SMsgWriter.cxx index eebb394..0282a17 100644 --- a/common/rfb/SMsgWriter.cxx +++ b/common/rfb/SMsgWriter.cxx @@ -791,3 +791,20 @@ void SMsgWriter::writeUserLeftSession(const std::string& username) os->writeString(username.c_str()); endMsg(); } + +void SMsgWriter::writeDisconnectNotify(bool graceful, const char *reason) +{ + if (!cp->supportsDisconnectNotify) + return; + + const char *msg = reason ? reason : ""; + size_t len = strlen(msg); + + startMsg(msgTypeServerDisconnect); + os->writeU8(graceful ? 1 : 0); + os->pad(3); + os->writeU32(len); + if (len > 0) + os->writeBytes(msg, len); + endMsg(); +} diff --git a/common/rfb/SMsgWriter.h b/common/rfb/SMsgWriter.h index 3a424bf..646c51a 100644 --- a/common/rfb/SMsgWriter.h +++ b/common/rfb/SMsgWriter.h @@ -135,6 +135,7 @@ namespace rfb { void writeUserJoinedSession(const std::string& username); void writeUserLeftSession(const std::string& username); + void writeDisconnectNotify(bool graceful, const char *reason); protected: void startMsg(int type); diff --git a/common/rfb/VNCSConnectionST.cxx b/common/rfb/VNCSConnectionST.cxx index cf3af0b..c234fe9 100644 --- a/common/rfb/VNCSConnectionST.cxx +++ b/common/rfb/VNCSConnectionST.cxx @@ -49,6 +49,10 @@ static LogWriter vlog("VNCSConnST"); static Cursor emptyCursor(0, 0, Point(0, 0), NULL); +namespace { +const rdr::U32 CLIENT_KEEPALIVE_KEYSYM = 1; +} + extern rfb::BoolParameter disablebasicauth; extern "C" char unixrelaynames[MAX_UNIX_RELAYS][MAX_UNIX_RELAY_NAME_LEN]; @@ -557,6 +561,13 @@ int VNCSConnectionST::checkIdleTimeout() return secsToMillis(idleTimeout); } if (timeLeft <= 0) { + if (cp.supportsDisconnectNotify) { + try { + writer()->writeDisconnectNotify(true, "Idle timeout"); + } catch (rdr::Exception& e) { + vlog.debug("Failed to send disconnect notice: %s", e.str()); + } + } close("Idle timeout"); return 0; } @@ -861,6 +872,10 @@ public: void VNCSConnectionST::keyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down) { rdr::U32 lookup; + if (keycode == 0 && keysym == CLIENT_KEEPALIVE_KEYSYM) { + return; + } + lastEventTime = time(0); server->lastUserInputTime = lastEventTime; if (!(accessRights & AccessKeyEvents)) return; @@ -1699,6 +1714,11 @@ void VNCSConnectionST::handleFrameStats(rdr::U32 all, rdr::U32 render) frameTracking = false; } +void VNCSConnectionST::keepAlive() +{ + // Keepalive traffic should not influence idle disconnect timers. +} + // setCursor() is called whenever the cursor has changed shape or pixel format. // If the client supports local cursor then it will arrange for the cursor to // be sent to the client. diff --git a/common/rfb/VNCSConnectionST.h b/common/rfb/VNCSConnectionST.h index 6d8068c..4148494 100644 --- a/common/rfb/VNCSConnectionST.h +++ b/common/rfb/VNCSConnectionST.h @@ -172,6 +172,7 @@ namespace rfb { virtual void sendStats(const bool toClient = true); virtual void handleFrameStats(rdr::U32 all, rdr::U32 render); + virtual void keepAlive(); bool is_owner() const { bool read, write, owner; diff --git a/common/rfb/encodings.h b/common/rfb/encodings.h index dbc4dc7..7fa6fa3 100644 --- a/common/rfb/encodings.h +++ b/common/rfb/encodings.h @@ -86,6 +86,7 @@ namespace rfb { const int pseudoEncodingVideoOutTimeLevel1 = -1986; const int pseudoEncodingVideoOutTimeLevel100 = -1887; const int pseudoEncodingQOI = -1886; + const int pseudoEncodingKasmDisconnectNotify = -1885; // VMware-specific const int pseudoEncodingVMwareCursor = 0x574d5664; diff --git a/common/rfb/msgTypes.h b/common/rfb/msgTypes.h index 9098b33..e3932ad 100644 --- a/common/rfb/msgTypes.h +++ b/common/rfb/msgTypes.h @@ -35,6 +35,8 @@ namespace rfb { const int msgTypeUpgradeToUdp = 181; const int msgTypeSubscribeUnixRelay = 182; const int msgTypeUnixRelay = 183; + const int msgTypeKeepAlive = 184; + const int msgTypeServerDisconnect = 185; const int msgTypeServerFence = 248; const int msgTypeUserAddedToSession = 253; @@ -61,6 +63,8 @@ namespace rfb { //const int msgTypeUpgradeToUdp = 181; //const int msgTypeSubscribeUnixRelay = 182; //const int msgTypeUnixRelay = 183; + //const int msgTypeKeepAlive = 184; + //const int msgTypeServerDisconnect = 185; const int msgTypeClientFence = 248; diff --git a/kasmweb b/kasmweb index da6b8f3..0dab999 160000 --- a/kasmweb +++ b/kasmweb @@ -1 +1 @@ -Subproject commit da6b8f3cfbed05891380f33aa4ca541c99d1f4c6 +Subproject commit 0dab9997d98ff5046682f15e1632af5ecf72f180