Skip to content

Remote Desktop Stacks

Se.Cherkasov edited this page Aug 23, 2026 · 1 revision

Remote desktop stacks

English · Русский

Both protocols are implemented in this repository. Nothing is shelled out to and no third-party client is bundled.

What the UI sees

interface RemoteDesktopSession {
    val framebuffer: RemoteFramebuffer          // pixels
    val updates: Flow<RemoteDesktopUpdate>      // cold, single-collector; collecting runs the session
    val title: String
    val capabilities: RemoteDesktopCapabilities // which optional controls this protocol has
    val diagnostics: RemoteDesktopDiagnostics
    suspend fun sendPointer(x: Int, y: Int, buttonMask: Int)
    suspend fun sendKey(event: RemoteKeyEvent, down: Boolean)
    suspend fun sendClipboardText(text: String)
    …
}

VNC and RDP differ in nearly every wire detail and in none of the things a screen needs, so the protocol vocabulary — keysyms, scancodes, encodings — stops at the adapters (VncRemoteDesktop, RdpRemoteDesktop). The pointer mask follows the RFB convention because it is the more expressive of the two; RDP, which sends transitions, derives them from the change since the previous call.

RemoteFramebuffer is the shared ARGB buffer (resize / blit / fill / copyRect).

RDP

flowchart TD
  TCP[TCP + TLS<br/>RdpTcpTransport] --> X[X.224 connection request]
  X --> NLA[CredSSP / NTLM<br/>nla: TsRequest, Ntlm*, Der]
  NLA --> MCS[MCS + GCC<br/>Mcs, McsConnect, Gcc, Per, Ber]
  MCS --> LIC[Licensing<br/>LicenseExchange + RdpLicenseCrypto]
  LIC --> CAP[Capability exchange → font map<br/>RdpConnectionSequence]
  CAP --> LIVE{live session}
  LIVE --> FP[Fast-path updates<br/>FastPath, SurfaceCommands, PointerUpdate]
  LIVE --> DYC[Dynamic channels · MS-RDPEDYC<br/>DynamicChannelHandler]
  DYC --> EGFX[egfx: GraphicsChannel, GraphicsSurface]
  DYC --> CLIP[ClipboardChannel]
  DYC --> DISP[DisplayControlChannel]
  DYC --> AUD[AudioChannel]
Loading
Layer Files
Byte-level IO with bounds checks RdpIo (RdpReader, RdpWriter, RdpSource, RdpSink); malformed input → RdpProtocolException
Connection sequence RdpConnect (RdpConnectionSequence, RdpSessionState) — pure protocol over an injected source/sink, so the whole sequence is driven by a test
Entry point and live session RdpTransport (RdpTransport, RdpSession, RdpUpdate)
Bitmap codecs NsCodec, PlanarCodec, InterleavedRle, rfx/ (RemoteFX: DWT, RLGR, colour)
Graphics pipeline codecs egfx/: Progressive, ProgressiveDwt, ClearCodec, Zgfx, AvcCodec, AvcStream, Avc444Planes, H264Color
Platform crypto nla/NtlmCrypto, LicenseCrypto / RdpLicenseCrypto — injected, so the protocol stays platform-free
Certificate trust RdpCertificate / RdpCertificateVerifier (TOFU by fingerprint), RdpVerifyingTrustManager, FileRdpCertificateStore
Session extras RdpDisplayScale, RdpImageQuality, RdpClientSettings, RdpFile/RdpFileImport, ServerRedirection (farm routing)

H.264 is injected as egfx/H264Decoder: H264DecoderFactory + YuvFrame. The platform answers for the decoder only — the wire format and the 4:4:4 assembly belong to AvcCodec — and available is what decides whether AVC is advertised at all. Desktop resolves ffmpeg on PATH (process/resolveExecutableOnPath, FfmpegH264, Y4mFrameReader); Android uses MediaCodecH264.

Audio redirection goes through the shared audio/RemoteAudio contract (AudioOutputs, RemoteAudioPlayerFactory, RemoteAudioFormat) and audio/PcmPlayer — a platform sink answers for the device, never for when to open it.

VNC

Layer Files
Entry point, session, auth VncTransport (VncTransport, VncSession, VncUpdate, VncAuth)
Wire VncIo, RfbCodec, RfbEncodings, RfbStreaming, PixelFormat
Adapter to the shared contract VncRemoteDesktop
Injected platform codecs VncCodecPorts: InflaterFactory (zlib), VncImageDecoder (JPEG), VncChallengeResponder (the DES challenge VNC-Auth requires)
Transport VncTcpTransport (jvmShared)

VNC-Auth uses DES because RFC 6143 specifies it; that is a protocol constraint, not a choice, and the corresponding scanner alerts are closed as won't-fix for that reason.

Rules for both

  • Protocol types never leak into the UI — the shared contract in graphics/ is the boundary.
  • Every PDU read goes through the bounds-checked reader; malformed input raises a typed protocol exception rather than an index error.
  • Server-chosen text (clipboard previews, desktop names) is untrusted and is drawn through ui/design/UntrustedText.
  • Platform capabilities are injected, never detected inside the protocol code.

Clone this wiki locally