Add VNC support (Milestone 6, issue #3)

Implements RFB (RFC 6143) directly against QTcpSocket. No permissively
licensed VNC client library exists to vendor the way FreeRDP was for RDP:
LibVNCClient is GPLv2, gtk-vnc is LGPL but GTK-tied, and vendoring either
would force a licensing decision on the whole (MIT) project. This is
from-scratch protocol code instead, threaded like SshSessionBackend (a
QObject on its own QThread driven by Qt's own async socket signals)
rather than RdpSessionBackend's manual worker-thread/blocking-loop
pattern, since QTcpSocket is already async.

Scope, matching SessionTab's existing SSH/RDP dispatch pattern
(session_backend_factory.cpp, session_tab.cpp's widget construction and
signal wiring) and VncDisplayWidget mirroring RdpDisplayWidget's
scale-to-fit rendering:
- Protocol handshake: RFB 3.3/3.7/3.8 negotiated explicitly (the
  SecurityResult message only exists in 3.8; pre-3.8 servers signal auth
  failure by closing the socket, which the disconnect handler accounts
  for)
- VNC Authentication (DES challenge-response, via OpenSSL's classic DES
  API) and no-auth security types
- Raw + CopyRect framebuffer decoding into a persistent QImage, requesting
  a fixed 32bpp format whose byte layout matches QImage::Format_RGB32
  directly (same zero-conversion trick RdpSessionBackend uses for
  FreeRDP's GDI buffer)
- Keyboard (Qt key -> X11 keysym, including the Unicode-beyond-Latin-1
  keysym convention) and mouse/wheel input forwarding

Explicit non-goals for this pass (see docs/PROGRESS.md for the full
list): Apple's Screen Sharing auth (so this can't yet reach macOS's
built-in VNC server), compression encodings beyond Raw/CopyRect, dynamic
resize, remote cursor shape sync, clipboard sync.

19 unit tests (tests/test_vnc_session_backend.cpp): pure-function
coverage (DES key prep verified against an independently documented test
vector for password "COW", X11 keysym mapping, socket-error mapping) plus
state-machine coverage against a scripted in-process fake RFB server
covering all three protocol-version handshake shapes, auth success/
failure, unsupported security types, and pixel-accurate Raw decoding.
That harness caught a real re-entrancy bug: QAbstractSocket::abort()
synchronously re-emits disconnected() before returning, so
failConnection() calling it was silently letting a second, generic
disconnected-socket handler overwrite an already-correct, specific error
message.

Also verified live against a real, independently implemented VNC server
(TightVNC on Windows): connect with VNC Authentication, correct
framebuffer dimensions and pixel data, clean disconnect, reconnect.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-15 18:02:49 -06:00
co-authored by Claude Sonnet 5
parent dbcc20d155
commit 6da9dc6ca5
12 changed files with 2097 additions and 21 deletions
+7 -4
View File
@@ -13,11 +13,12 @@ Supported target platforms:
OrbitHub is in active development.
- Milestones completed: M0-M5, and M7-M9
- Milestones completed: M0-M9
- Current milestone: Milestone 10 (v1.0 Stabilization)
- Deferred milestone: Milestone 6 (VNC Fully Working)
- Latest checkpoint tag: `v2026.9.15`
- VNC implementation milestone (M6) is currently deferred
- VNC (M6) covers standard VNC Authentication and no-auth servers; see
[docs/PROGRESS.md](docs/PROGRESS.md) for known gaps (Apple Screen
Sharing auth, compression encodings, resize, cursor sync, clipboard)
Progress and milestone details:
- [docs/PROGRESS.md](docs/PROGRESS.md)
@@ -181,4 +182,6 @@ See in-app `Help -> About OrbitHub` for license links and third-party inventory.
## Notes
- Passwords are requested at connect time and are not stored in the profile database.
- This repository currently prioritizes integrated SSH and RDP workflows while VNC implementation is pending.
- VNC support covers standard VNC Authentication and no-auth servers (e.g. TigerVNC, x11vnc,
TightVNC); it doesn't yet reach macOS's built-in Screen Sharing server, which uses a different
authentication scheme (see docs/PROGRESS.md, Milestone 6).