Commit Graph

4999 Commits

Author SHA1 Message Date
Mariano Abad
1fcc15488c drm: address the consumer review (login-screen uid, frame flow control, hotplug)
- Start the login-screen --server as the active seat0 greeter account instead
  of root, so the DRM capture GPU/EGL convert never loads the vendor GPU
  userspace in a privileged process. A genuine root graphical session has no
  lower uid to drop to and stays root, and if the greeter spawn fails we fall
  back to a root --server so the login screen stays remotable. Gated on the drm
  feature so the non-drm build is unchanged.
- Bound the number of frames in flight on the `_drm` channel: the consumer acks
  each frame it finishes converting and the producer only sends while it holds
  credit, waiting on the socket otherwise. Without this the producer kept
  writing descriptors into the socket faster than a slow convert drained them
  and the consumer worked through an ever-growing backlog of stale frames. A
  zero-byte read or write on the ack path is treated as a closed peer rather
  than as success.
- Forward a display list that became empty (last monitor unplugged) instead of
  dropping it, so the availability cache leaves Available rather than keep
  advertising removed displays.
- On a topology change, invalidate the Wayland geometry cache and reapply the
  uinput mouse range for the new layout. The refresh runs off the frame-receive
  loop and is coalesced across the per-display receivers, so a multi-monitor
  hotplug runs one worker and the final layout wins.
- Clear the prefer-CPU-convert hints on a topology change: display indices can
  be renumbered, so a hint learned for an old index no longer refers to the same
  physical display. Re-learned on the next convert failure.
- Report a non-DRM-backed display when the DRM list is shorter than the sync
  list or any entry is offline, covering the present-but-demoted case.
2026-07-24 08:50:17 -03:00
Mariano Abad
9edfc11ba6 drm: dlopen libdrmtap by absolute path + unit-test the _drm admission and re-auth (review 5e, 6a)
5e: the deb dropped /usr/lib/rustdesk into /etc/ld.so.conf.d so the private libdrmtap
could be found by soname -- a system-wide search-path entry that lets it shadow a
system library for every binary on the host, which Debian Policy 10.2 forbids. Resolve
it by absolute path (/usr/lib/rustdesk/libdrmtap.so.0) at the dlopen site instead, with
the bare sonames kept only as a dev fallback, and drop the ld.so.conf.d file and the
ldconfig/try-restart postinst entirely (the .so is present at its absolute path right
after unpack, so the pre-warm resolves with no linker-cache step). The dlopen site is
this PR's own code, so this is in scope, not a follow-up.

6a: extract the _drm admission bound and the per-frame re-auth decision into pure
helpers (drm_conn_admitted, drm_peer_authorized) and unit-test them: admission admits
strictly below MAX_DRM_CONNS and rejects at/above it; re-auth passes root always,
passes a non-root peer only while it equals the active-session uid, and fails closed on
a switched-away, unknown-session, or unknown-peer case. (The /proc/exe-mismatch
rejection is exercised by the accept-time authorize call; unit-testing it in isolation
would need a second process with a different exe, so it stays an integration concern.)
2026-07-24 08:50:17 -03:00
Mariano Abad
9047c6d164 drm: release the probe single-flight guard via RAII on the cold path too
The cold availability probe in is_available acquired DRM_PROBE_IN_FLIGHT and released
it with a plain store(false) after a synchronous body; a panic there (e.g. a poisoned
DRM_STATE lock) would leak the guard true and freeze both future probes and the
refresh path hardened in the previous commit, since they share the guard. Hoist the
release into a shared ProbeInFlightGuard used by both the cold probe and the refresh
closure, so any exit -- normal, early, or unwinding -- clears it.
2026-07-24 08:50:17 -03:00
Mariano Abad
163e0ac42f drm: address the self-review findings on the review rework
Five defects an adversarial pass found in the previous commits:
- refresh_available_async set the single-flight probe guard, then relied on the
  detached thread to clear it; if thread creation failed (EAGAIN) or the closure
  unwound, the guard leaked true and froze every future probe. Release it via RAII
  inside the closure and on a Builder::spawn error.
- The _drm per-frame re-auth called the cached active_uid(), which on a cache miss
  (exactly during a session switch) falls back to a blocking loginctl seat0 lookup --
  on the single-threaded _drm runtime, once per frame, a subprocess storm. Use a new
  cache-only accessor that never blocks and fails closed on a miss, and correct the
  comment: the stop is bounded by the active-uid cache cadence, not one frame.
- set_drm_cursor inserted unconditionally, so a still-draining predecessor stream
  could overwrite (then delete on teardown) the cursor a replacement stream published
  for the same index. Make it a compare-and-set that ignores an older epoch.
- recv_msg_timeout2 treated a spurious readable() wakeup with nothing consumed as a
  mid-frame stall and tore the stream down. Track whether any byte was consumed
  (drm_read_full sets it) and map a zero-progress deadline back to None (re-poll),
  reserving the hard error for a genuine partial-frame stall.
2026-07-24 08:50:17 -03:00
Mariano Abad
809ee1a7f1 drm: unit-test the bespoke _drm SCM_RIGHTS framing (review 6)
The _drm wire format is hand-rolled (length prefix plus an fd bound to the frame
first byte) because Framed/BytesCodec cannot carry ancillary data, so it had zero
tests. Add pure-userspace coverage over a socketpair:
- a control message round-trips with and without an attached fd, and the received fd
  refers to the same open file (a byte written into the source is read back through it)
- a raw length-prefixed body (cursor / CPU-fallback path) round-trips byte-for-byte
- a forged length prefix past the JSON cap is rejected at the prefix
- surplus fds packed into one cmsg keep only the first and close the rest
- a control message truncated past DRM_CMSG_CAP is rejected (MSG_CTRUNC), not consumed
- peer_uid_from_fd reads the socket peer credential the producer-auth path relies on
2026-07-24 08:50:17 -03:00
Mariano Abad
5baeb8feb5 drm: bound the _drm body read, stream-scope cursor teardown, refresh a stale verdict, drop dead clear (review 5)
- recv_msg_timeout2 only gated the wait for the first byte, so a peer that sent one
  byte then stalled pinned the task forever. The same budget now also bounds the body
  read; a body that overruns is a hard error that tears the stream down (recv_msg
  bodies are small JSON, so a healthy peer never trips it).
- The cursor cache is keyed by display index, which a rebuilt stream reuses, so a
  predecessor exiting after its replacement published a fresh cursor erased it. Stamp
  each entry with a monotonic per-stream epoch and compare-and-remove on teardown.
- ProbeState::Available had no TTL, so an idle hotplug left a phantom display in
  enumeration. Give it a timestamp and refresh the list off the hot path once it ages
  past POSITIVE_TTL. The verdict stays true across the refresh (never bounces a live
  session to the portal) and the probe runs on a background thread (never blocks the
  async enumeration).
- Remove the dead clear(): it is unreferenced, and wiring it into teardown would force
  the blocking re-probe on the next enumeration that swap_available_displays exists to
  avoid.
2026-07-24 08:50:17 -03:00
Mariano Abad
31485f83de drm: keep the demoted-display list consistent instead of stretching PipeWire (review 4.5)
A DRM display demoted to PipeWire has no geometry-consistent per-connector stream
on a multi-monitor host -- the portal exposes a single whole-desktop stream. The
fallthrough served that whole-desktop frame while the list still advertised the
demoted connector geometry, so the client stretched the frame and offset all input
by the connector origin (the primary-index-0 demotion reaches this even after the
get_capturer_for_display exact-index fix).

Dropping the display from the list is not an option: its position IS the capturer
index, so a drop would shift every later display and desync get_capturer_info. So
instead: get_display_infos advertises a multi-monitor demoted display OFFLINE at its
stable index, and get_capturer_for_display serves the PipeWire fallback only when
its rect matches the advertised geometry, else bails. A single-display host still
falls through (whole-desktop == that display). All new logic is drm-gated.
2026-07-24 08:50:17 -03:00
Mariano Abad
e00d7d7737 drm: validate cursor body length and coalesce _drm frames to latest-wins (review 4.1, 4.8)
4.1: the DrmCursor consumer handed the wire body straight to the client, which
renders width*height*4 RGBA bytes. Reject a body shorter than that so a truncated
cursor cannot make the client read past the buffer. The hidden-cursor sentinel is
0x0 with an empty body, for which the bound is 0 and the check is a no-op.
4.8: the _drm socket is a FIFO, so a consumer that drains slower than we produce
(a 4K convert on a modest GPU) fell seconds behind stale frames. Drain the producer
channel without blocking each tick and forward only the newest frame; replaced
frames drop in place, closing the zero-copy OwnedFd and freeing the CPU-path pixel
buffer. Cursor updates stay in order and are never coalesced away.
2026-07-24 08:50:17 -03:00
Mariano Abad
027178af27 drm: re-authorize the _drm stream per frame and auth the producer (review 3.3, 4.1)
3.3: DRM/KMS capture is not session-scoped -- the worker grabs a CRTC's physical
scanout regardless of which session owns the display -- but the peer was
authorized only once at accept. Capture the peer uid and re-check it at the top of
the forward loop: root is always allowed, any other peer must still be the
active-session uid, fail closed otherwise. A session change now tears the stream
down within one frame (~33ms) instead of leaking the incoming user's screen to the
outgoing user's --server.
4.1: connect_drm accepted any producer. Reject a non-root peer (peer_uid != 0) so a
process that won the socket-path race cannot feed the consumer a display list,
frames and dma-buf fds while the DRM path suppresses the portal consent prompt.
2026-07-24 08:50:17 -03:00
Mariano Abad
75af53b9bf drm: address review findings 3.1, 4.2, 4.3, 4.4, 4.7 + minors
3.1: snapshot the stock flutter bundle before the CI drm relink and restore it
before makepkg, so the official Arch package ships the stock cdylib, not the
drm-enabled one. 4.2: wrap the drm block in a failure-tolerant subshell so a
drm-only failure no longer aborts the stock deb/rpm/arch publish. 4.3: narrow the
publish glob to rustdesk-[0-9]*.deb so the consent-bypass unattended-wayland deb
stays an artifact, not on the public release. 4.4: rewrite the three stale
DRM_CAPTURE_SECURITY.md statements to the split (default path passes a read-only
scanout dma-buf fd over SCM_RIGHTS with an import-once cache; export validation is
metadata-only; BGRA-over-the-wire is the fallback) and document that grab_desc's
fd is O_RDONLY (DRM_RDWR dropped upstream, dup preserves it). 4.7: only
short-circuit to the DRM cursor when it is authoritative (visible, or hidden in a
pure-DRM session); fall through to the normal cursor path in a mixed
DRM+PipeWire session. minors: thread the deb variant by feature not glob; TODO
for the ld.so.conf.d system path; drop a stray blank line. All gated or
whitespace so the drm-off build stays byte-identical.
2026-07-24 08:50:17 -03:00
Mariano Abad
b642c75a64 drm: stop altering the stock (drm-off) Wayland path (review 3.2, 4.6)
3.2: get_capturer_for_display no longer falls back to cap_map[0] for a missing
index. CapturerPtr is a bare *mut Capturer cloned by raw-pointer copy, so aliasing
one entry to two display_idx values let two video-service threads call frame() on
the same Recorder unsynchronised (data race / UB), reachable in a plain build via
CaptureDisplays{set:[0,3]}. Restore the exact-index lookup + bail; a demoted DRM
index is dropped from the advertised list at the source instead.
4.6: revert check_init to upstream (flag set before the per-display loop, direct
insert). The staged-all-or-nothing variant turned a partial per-display failure
into a permanent 1Hz retry loop and was not drm-gated. Both restore the drm-off
build to byte-identical with upstream.
2026-07-24 08:50:17 -03:00
Mariano Abad
0cd544061e drm: do not restart-loop a demoted display PipeWire cannot serve
DRM and PipeWire do not share a display-index space: DRM enumerates one entry
per connector while the portal often exposes a single whole-desktop stream at
index 0. When a per-display DRM capture was demoted to PipeWire for a non-primary
DRM index, cap_map.get(&display_idx) was None and the bail Err made
ServiceTmpl::run retry get_capturer every 1s forever (a multi-monitor restart
loop, latent until a display demotes). Degrade to the whole-desktop stream
(index 0) PipeWire does provide instead of spinning. Healthy DRM displays return
before this and are unaffected.
2026-07-24 08:50:17 -03:00
Mariano Abad
cf77ee87a1 drm: second review pass on the phase-2 split
1- make PipeWire init atomic: build every per-display capturer into owned staging
first and publish them to CAP_DISPLAY_INFO only after all succeed, so a mid-loop
Capturer::new failure neither leaves partial entries (which the next check_init
would treat as already-initialized) nor leaks the raw pointers already created.
2- pin the immutable libdrmtap commit, not just the tag: git clone --branch
follows a mutable tag, so verify the cloned HEAD equals DRMTAP_SHA in both the CI
workflow and build.py, failing on a moved/compromised tag.
3- drop the stale comment claiming a libdrmtap-sys crate pin (the drm backend has
no such dependency).
2026-07-24 08:50:16 -03:00
Mariano Abad
6cc426e9f5 drm: address the phase-2 split review
1- do not depend on the libdrmtap-sys crate for the pin: its build.rs statically
compiles the whole libdrmtap C tree and a CAP_SYS_ADMIN helper and links
-ldrm/-lseccomp/-lcap, which defeats the runtime-dlopen model. keep drm a pure
dlopen backend and pin the .so by the build.py DRMTAP_REF release tag, guarded by
a strict vX.Y.Z regex. drops the now-moot Cargo.lock freshness CI checks.
2- render-node-less consumers no longer lose the stream: the --server signals
need_cpu on DrmStart when it cannot open a convert context, and the --service
streams the CPU-converted frame path for that connection instead of a dma-buf fd
the consumer cannot detile (which used to fall through to a PipeWire path nobody
can approve on an unattended seat).
3- mark PipeWire initialized only after every per-display capturer is created, so
a partial failure retries instead of the flag falsely reporting a complete init.
4- reject a degenerate (zero width/height) or short CPU frame before it reaches
PixelBuffer::new (which derives stride as data.len()/height, dividing by zero).
5- keep the export-ledger epoch at DRM_DISPLAY_GENERATION so a hotplug invalidates
cached buffers (elision stays off until the recycled-fb_id inode case is handled).
6- validate the udev uevent source (kernel nl_pid, multicast) with recvmsg so a
local process cannot unicast a spoofed drm-change event to the root listener.
2026-07-24 08:50:16 -03:00
Mariano Abad
3b6914b1b2 feat(drm): phase-2 split, pass the dma-buf fd instead of the converted frame
move the egl detile and rgba pack out of the root --service and into the
unprivileged --server. the root now calls only drmtap_open + drmtap_grab_desc
and exports a raw dma-buf fd; the fd rides the _drm channel over SCM_RIGHTS with
a small descriptor (geometry, per-plane offsets/pitches, modifier, hdr) instead
of the full rgba frame, dropping the per-frame copy. the --server imports the fd
with drmtap_open_render + drmtap_convert_dmabuf, keyed by the import-once egl
cache, and the render context is created and dropped on the recv thread.

the _drm transport moves off Framed<BytesCodec> (which cannot carry a fd) to a
bespoke sendmsg/recvmsg framing (DrmConn) that attaches one SCM_RIGHTS cmsg only
when a fd is present and rejects a truncated ancillary message. the split
symbols are bound optionally so an older libdrmtap still loads the cpu path, and
the whole thing degrades to the cpu BGRA path or PipeWire when no render node is
available. pins libdrmtap-sys to =0.4.13 with the Cargo.lock checksum. folds in
the DP-MST, ldconfig-restart and per-display PipeWire-fallback review fixes and a
udev hotplug refresh.
2026-07-24 08:50:16 -03:00
Mariano Abad
77a14339ad feat(drm): opt-in DRM/KMS screen capture for Linux/Wayland
adds an opt-in `drm` feature for unattended remote access on Wayland: it
captures below the compositor via libdrmtap, so there is no
xdg-desktop-portal consent dialog and it works at the login screen.

off by default. when the feature is off the build is byte-identical.
everything is gated behind feature = "drm" or lives only in the separate
rustdesk-unattended-wayland deb, whose package name is the informed consent.

architecture (agreed with the maintainer): the capture runs inside the root
--service, which already holds the privilege it needs, and streams frames to
the user --server over a service-scoped _drm ipc channel. libdrmtap is loaded
with dlopen at runtime (no link-time dependency, so the base build is
unchanged and it still runs on ubuntu 18), and the .so is built in ci from the
rustdesk-org/libdrmtap fork and shipped only in the drm deb. no setcap helper.

- service: DrmReader reads scanout directly via the dlopen loader; an
  IpcDrmCapturer serves _drm consumers with a per-connection capture worker;
  durable availability cache + pre-warm to avoid enumerate/re-probe restarts
- capture: multi-display (targets the selected crtc), hardware cursor over
  _drm, transient-errno retry with a bounded stall, rejects non-32bpp scanouts
  before the frame copy
- robustness: only active, crtc-bound outputs are offered (an unbound
  crtc_id=0 connector is filtered and a client-selected 0 is refused, both
  fall back to pipewire); a per-display rapid-rebuild guard demotes a flapping
  display to pipewire; per-display (not global) zero-frame failure tracking
- root-service hardening: bounded frame allocation and a concurrent-connection
  cap so a malformed scanout or a buggy consumer cannot OOM or thread-exhaust
  the service; a negative availability verdict expires so displays that appear
  after startup recover without a --server restart; exactly-one .so selection
  in the packaging so a stale object is never silently shipped
- build: libdrmtap.so cloned at build time from rustdesk-org/libdrmtap main
  and bundled only for the --drm deb; ci builds a separate
  rustdesk-unattended-wayland deb (incl. an ubuntu 18.04 container)
- DRM_CAPTURE_SECURITY.md: threat model and hardening notes
2026-07-24 08:50:16 -03:00
CHarris
b4af82157b fix: refresh wayland uinput range on compositor layout change (#15628)
* fix: refresh wayland uinput range on compositor layout change

The uinput absolute range is computed once at session init. If the
compositor layout changes mid-session (monitor scale or position
change, or a portal virtual output appearing once capture starts),
injected coordinates are rescaled by the stale range and land offset.

Poll the live desktop bounding box from the display service loop while
subscribed (one wayland roundtrip, throttled to 1.5s, no subprocesses)
and re-apply the uinput resolution when it changes. Also read a fresh
layout when computing the initial range in check_init, since the cache
is not cleared when a session closes through the restore-token path.

This is the X component of #15601. The stale advertised origins (the Y
component) are not touched here: re-advertising DisplayInfo mid-session
trips the portal re-negotiation and can drop displays.

Signed-off-by: Cody Harris <codyharris7188@gmail.com>

* fix: bound the mouse resolution IPC wait during session init

Wrap update_mouse_resolution in the same 3s timeout the periodic
refresh uses, so a hung IPC response can't stall check_init.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix: build timeout future inside runtime, split linux lazy_static

Constructing the timeout future eagerly as the block_on argument panics
with 'there is no reactor running'; move it into the async block so it is
built inside the runtime context. Also move WAYLAND_UINPUT_RECT into its
own cfg-gated lazy_static block, an attribute on a single item inside the
shared block does not compile.

* fix: confirm uinput mouse device adopted new range before caching rect

send_refresh() now waits for the mouse service to ack that it recreated the
device with the new range instead of firing and forgetting, and
update_mouse_resolution() propagates that result. The layout poller only
caches the rect after the device actually adopts the range, so a failed
refresh errors and retries on the next check. The ack read is bounded by
IPC_REQUEST_TIMEOUT, matching the keyboard get-key-state path.

* fix: propagate refresh failures instead of caching a stale range

- input_service: error when the custom-mouse downcast fails so the poller
  retries instead of caching an unconfirmed refresh
- uinput: on device recreation failure, keep the current device and the
  IPC connection and withhold the ack so the client retries, instead of
  killing the mouse handler

* fix: remap injected wayland coords onto the live layout after a monitor moves

The range refresh corrects the uinput ABS bounds, but a single-display client
sends whole-desktop coordinates offset by the origin of the display it follows,
taken from the layout advertised at session init. When another monitor is
rescaled or moved that origin shifts, so the coordinate lands offset before it
reaches uinput and the range refresh cannot recover it.

Snapshot the per-display layout at init, poll the live layout on the existing
1.5s throttle, and when they differ remap each injected move into the followed
display's current rectangle (matched by connector name, index fallback when the
compositor reports none). No-op and lock-free while the layout is unchanged.

---------

Signed-off-by: Cody Harris <codyharris7188@gmail.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 18:35:49 +08:00
21pages
beaa754299 fix stale primary display selection (#15460)
* fix stale primary display selection

Signed-off-by: 21pages <sunboeasy@gmail.com>

* fix stale display selection during login and switching

  - resolve the primary display from the refreshed login snapshot
  - defer display enumeration until authentication succeeds
  - read Wayland displays and primary index from the same cache snapshot
  - reject stale monitor and camera indices during display switching

Signed-off-by: 21pages <sunboeasy@gmail.com>

* fix inconsistent display snapshots during login

  - return displays from the same enumeration used to select the primary
  - avoid re-reading the shared display cache after updating it
  - use the same converted snapshot during Wayland initialization

Signed-off-by: 21pages <sunboeasy@gmail.com>

* avoid cloning unchanged display snapshots

Signed-off-by: 21pages <sunboeasy@gmail.com>

* fix invalid display subset handling

Signed-off-by: 21pages <sunboeasy@gmail.com>

* minimize code churn in switch_display_to

Signed-off-by: 21pages <sunboeasy@gmail.com>

---------

Signed-off-by: 21pages <sunboeasy@gmail.com>
2026-07-23 17:17:01 +08:00
bmmh1
929e989f17 feat(macos): silent auto-update with security hardening (#15550)
Co-authored-by: bmmh1 <bmmh1@users.noreply.github.com>
2026-07-23 00:22:10 +08:00
hatterp
1c2dd71891 Translate 'Continue' to 'Kontynuuj' in Polish (#15641) 2026-07-21 22:43:09 +08:00
Kuksgauzen
5b4d6baf47 fix: wrap BackingScaleFactor in autoreleasepool to stop NSDictionary accumulation on macOS (#15623)
Signed-off-by: Viktor Kuksgauzen <vkpiar@gmail.com>
2026-07-21 05:42:43 +08:00
gateslu
7696b0ee51 fix(linux): forward forced display server to user server (#15627)
Signed-off-by: Gateslu <lyjbbq@163.com>
2026-07-20 13:24:48 +08:00
CHarris
20ab5ab0ad fix(deploy): don't wipe local id when --deploy gets an empty --id (#15633)
`rustdesk --deploy --id ""` (e.g. an unset variable in a deployment
script) deploys a blank id, then wipes the local id and unconfirms the
key through the IPC config write. The Android deploy flow already guards
an empty id (#15146); apply the same guard to the CLI, and reject an
empty id at the IPC write boundary the same way the read path was fixed
in #15626.
2026-07-20 10:58:28 +08:00
CHarris
c01300be20 fix(ipc): never adopt an empty id from the main IPC (#15626) 2026-07-20 10:22:46 +08:00
cui fliter
5f015c9da1 Translate Continue into Simplified Chinese (#15621)
Signed-off-by: cuishuang <imcusg@gmail.com>
2026-07-18 17:56:27 +08:00
21pages
96e2a330b8 restrict switch sides to remote desktop sessions (#15610)
* fix: restrict switch sides to remote desktop sessions

 Reject switch sides requests outside authenticated remote desktop sessions, and reject switch sides responses that try to carry non-remote login types.

 Add scope coverage so file transfer, terminal, view camera, and port forward sessions cannot use switch sides.

Signed-off-by: 21pages <sunboeasy@gmail.com>

* fix review: consume switch sides UUID before rejecting response

Signed-off-by: 21pages <sunboeasy@gmail.com>

---------

Signed-off-by: 21pages <sunboeasy@gmail.com>
2026-07-17 15:52:49 +08:00
21pages
5abf4e9724 Fix disabled installation bypass (#15598)
* Fix disabled installation bypass

Prevent install.exe and --install from opening the install flow when disable-installation is set.

Signed-off-by: 21pages <sunboeasy@gmail.com>

* Refine disabled installation handling for portable clients

Document why --install must be filtered from both Rust and Flutter runner arguments for portable wrappers such as no-install.exe. Remove redundant UI-
    layer installation checks because the install entry points are already gated upstream.

---------

Signed-off-by: 21pages <sunboeasy@gmail.com>
2026-07-16 16:00:38 +08:00
fufesou
bdb38c4730 fix: check valid id (#15535)
Signed-off-by: fufesou <linlong1266@gmail.com>
2026-07-14 15:35:52 +08:00
Maison da Silva
fa418cace6 Translate 'Continue' to 'Continuar' in ptbr.rs (#15567)
Translate 'Continue' to 'Continuar' in ptbr.rs
2026-07-13 18:56:41 +08:00
Maison da Silva
865fe71c46 Update Portuguese translations for clarity (#15534) 2026-07-11 17:18:12 +08:00
fufesou
8314335b31 fix: update download, force tls (#15529)
Signed-off-by: fufesou <linlong1266@gmail.com>
2026-07-09 15:05:49 +08:00
bovirus
acb9f63e1d Update it.rs (#15531) 2026-07-08 16:35:34 +08:00
VenusGirl❤
005a8b4a04 Update Korean (#15525)
Updated Korean translations for clarity and accuracy.
2026-07-08 11:09:39 +08:00
fufesou
28930c0463 fix: non-E2EE show dialog (#15514)
* fix: non-E2EE show dialog

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: build web, bridge

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: direct IP access, do not snow non-E2EE dialog

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: non E2EE dialog, update contents

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: non-E2EE, show dialog, port forward

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: non-E2EE dialog, port forward, ignore direct IP access

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: non-E2EE is_direct_ip_access()

Signed-off-by: fufesou <linlong1266@gmail.com>

* Simple refactor

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: non-E2EE dialog, port forward, close socket on disconnect

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: non-E2EE dialog, incorrect reuse of Data::Close

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2026-07-06 17:05:11 +08:00
fufesou
37141afece refact: remove feature cli (#15524)
Signed-off-by: fufesou <linlong1266@gmail.com>
2026-07-06 16:17:32 +08:00
fufesou
493b14ba78 Fix/session scope permission audit (#15469)
* fix: enforce session-scoped permissions

Restrict non-remote sessions to their allowed message types, filter
out-of-scope login options, and audit rejected or filtered messages.
Hide screenshot controls outside default remote sessions.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: typo

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: prevent privacy mode in view-camera sessions

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: switch display, check non-view-camera

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: avoid sending unsupported messages

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: session scope, add option to control close/alarm

Signed-off-by: fufesou <linlong1266@gmail.com>

* Fix: scoped session handling for view-camera compatibility

  - Skip view-camera auto-login and display-management side effects
  - Allow harmless render broadcasts without affecting non-video sessions
  - Keep legacy view-camera management messages compatible as no-ops
  - Preserve stricter scope violations for non-video session types

Signed-off-by: fufesou <linlong1266@gmail.com>

* update libs/hbb_common

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: ignore repeated login request

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: view camera, support "Take screenshot"

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: session scoped messages, check update options

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: session scope, check portforward before conn type voolations

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: scoped messages, reduce changes.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: session scope, comments

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: keep scoped sessions compatible with render broadcasts

Allow legacy render-broadcast no-op messages for file transfer and terminal
sessions while keeping port forward and mixed options scoped. Also avoid sending
new render updates to non-video Flutter sessions.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: scope screenshot requests by video source

Key screenshot requests by video source and display index so camera and
monitor sessions cannot consume each other's requests. Deduplicate the Flutter
render-target predicate while keeping render updates limited to video sessions.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: Harden scoped session message handling

Filter option updates by authenticated connection type,
keep legacy no-op messages compatible, and avoid noisy repeated
scope violation alarms.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: session scope, comments

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: Send close reason for scoped session violations

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: Enforce scoped session message filtering

  - filter out-of-scope messages for limited session types
  - scope option updates by authenticated connection type
  - keep render-broadcast no-op compatibility for non-video scoped sessions
  - restore view-camera screenshot handling
  - improve session scope violation audit labels
  - avoid cloning option messages on the remote hot path

Signed-off-by: fufesou <linlong1266@gmail.com>

* Fix scoped session clipboard broadcast compatibility

Treat text clipboard broadcasts as no-op compatibility messages for FileTransfer and Terminal sessions, matching existing
handler behavior and preventing optional scope-violation close from disconnecting those sessions. Keep ViewCamera and
PortForward clipboard messages subject to normal scope enforcement.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: log warn

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: restrict Flutter clipboard sync to default sessions

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: session scope, comments and tests

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: session scope, reset sessions in login handle

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: session scope, view camera, allow clipboard noop

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2026-07-05 23:28:32 +08:00
alonginwind
a2b79462ab fix: auto-close terminal tab/window when shell exits (#15448) 2026-07-02 16:43:27 +08:00
rustdesk
435f6ec61d update copyright 2026-06-30 11:02:28 +08:00
21pages
0497814004 Add authentication details to connection audit (#15456)
* Add authentication details to connection audit

Signed-off-by: 21pages <sunboeasy@gmail.com>

* rename normalize_conn_audit_primary_auth to normalize_conn_audit_auth_fields

Signed-off-by: 21pages <sunboeasy@gmail.com>

* Merge permanent password audit methods

Signed-off-by: 21pages <sunboeasy@gmail.com>

* Simplify connection audit auth methods

Signed-off-by: 21pages <sunboeasy@gmail.com>

---------

Signed-off-by: 21pages <sunboeasy@gmail.com>
2026-06-29 16:04:24 +08:00
Maison da Silva
2ee580d49d Update translation for outdated installation message (#15427)
Update translation for outdated installation message
2026-06-28 12:11:03 +08:00
fufesou
4a54029cac fix(update): msi, norestart (#15440)
* fix(update): msi, norestart

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(update): escape path

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2026-06-27 16:45:27 +08:00
fufesou
001848bf2f fix(fuse): umount (#15426)
* fix(clipboard): clean up stale Linux FUSE mounts

Recover Linux file clipboard FUSE mount points before remounting and stop treating a cached
context as valid when the mount has already gone away.

This fixes the desktop file manager copy failure that shows dialogs such as
"Error while copying a" and "There was an error copying the file into xxx".

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(clipboard): fuse, reduce dups

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: clear Linux file clipboard before unmounting FUSE

Ensure Linux client teardown clears RustDesk file clipboard URLs while
the FUSE context is still available. Also prefer fusermount before
umount to avoid noisy unprivileged teardown attempts.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(clipboard): return and log errors

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2026-06-26 16:17:44 +08:00
21pages
989bf80fe8 Support controller user attribution in audit logs (#15407)
* Support controller user attribution in audit logs

This PR supports associating audit logs with the controller user.

  ## Implementation:
  - Add `ControlledContext { conn_audit_token }` to `PunchHole`, `RequestRelay`, and `FetchLocalAddr`.
  - The server sends a controller-user identity snapshot to the controlled client through rendezvous messages.
  - The controlled client sends the token back to the server when posting the `on_open` conn audit or IP whitelist alarm audit.
  - This lets the server attach the controller user to audit logs.

  ## How the controlled client helps identify the controller user:
  - Conn audit: sends the token to the server in `on_open`; the server creates the audit log and caches the user snapshot.
  - File audit: sends `id` and `conn_id`; the server uses them to find the cached user snapshot.
  - Alarm audit: IP whitelist sends the token directly; other alarm logs send `id` and `conn_id`, and the server uses them to find the cached user
  snapshot.

  ## Compatibility:
  - Supported only for logs created with a new server and a new controlled client.
  - Does not require upgrading the controller client.

  ## Test

  - [x] New/old clients connected to new/old servers, and conn/file/alarm audit logs worked normally.
  - [x] New client connected to new server generated searchable conn/file/alarm audit logs.
  - [x] Punch hole, local addr, and relay paths worked with audit logs and control role on new/old servers.
  - [x] Direct IP connections produced audit logs, but do not support user audit.

Signed-off-by: 21pages <sunboeasy@gmail.com>

* rename conn_audit_token to conn_audit_ref

Signed-off-by: 21pages <sunboeasy@gmail.com>

---------

Signed-off-by: 21pages <sunboeasy@gmail.com>
2026-06-26 15:07:27 +08:00
VenusGirl❤
78b5f47668 Update ko.rs (#15395) 2026-06-26 13:51:08 +08:00
jkh0kr
97e9e44faa Update ko.rs (#15390)
Incorrect translation
2026-06-26 11:19:18 +08:00
Daniel Marschall
0cbdb6ffb3 Fix tray icon click (regression due to breaking change in tray-icon 0.17) (#15413) 2026-06-25 09:43:04 +08:00
Maison da Silva
a69614d464 Update translation for 'Control Actions' in ptbr.rs (#15386) 2026-06-24 12:26:36 +08:00
fufesou
09bc9056c9 fix(update): win aarch64 (#15389)
Signed-off-by: fufesou <linlong1266@gmail.com>
2026-06-23 17:59:29 +08:00
fufesou
0c6df924d1 refact: file transfer, do this for all conflicts(tasks) (#15385)
Signed-off-by: fufesou <linlong1266@gmail.com>
2026-06-23 11:23:42 +08:00
rustdesk
16570ee34f fix https://github.com/rustdesk/rustdesk/discussions/15355 2026-06-22 22:57:08 +08:00