- the WebRTC offer now rides any punch request; only an offer-less request
may close and reuse the rendezvous socket for TCP punching
(request_allows_tcp_punch replaces the udp_port-based invariant), with a
separate offer-less request racing as the TCP fallback
- WebSocket mode no longer disables WebRTC — ws only tunnels the
signaling/relay legs while ICE stays the only P2P path there; SOCKS proxy
still disables it (ICE would bypass the proxy and leak the real IP)
- controlled side: WebRTC-only punch replies and trickled ICE candidates go
over dedicated TCP connections to the rendezvous server instead of the UDP
mediator channel, for ws/TCP-only hbbs deployments; drop the now-redundant
rz_sender plumbing and the 400ms candidate re-send on that leg
- guard is_udp handling against responses to requests that advertised no
udp_port; skip the IPv6 socket bind under force-relay
- test_udp_uat: drop the STUN port race — the punch port must come from the
rendezvous server's TestNatResponse observing this socket's mapping, a
STUN probe from another socket can advertise an unreachable port
- bump hbb_common (webrtc 0.13 MSRV pin rationale + upgrade checklist docs)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- prefer-P2P racing (race_transports_prefer_webrtc) across punch and RelayResponse; ICE bridge with 400ms candidate resend
- controlled-side answerer and ICE routing; sign local DTLS fingerprint into SignedId, controller verifies the binding fail-closed
- fix pc leaks: close_webrtc() on insecure-decline paths (io_loop, port_forward); compute direct before disarming the offerer guard
- point hbb_common to the WebRTC data-plane commit 9f5a296
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(client): allow switch-sides back-connection in incoming-only mode
"Switch sides" makes the controlled client run `--connect <peer>
--switch_uuid <uuid>`, which Client::_start rejected outright in
incoming-only custom clients, so the feature silently dropped the
session and never switched.
Exempt exactly that back-connection: a default-conn session carrying a
switch uuid may proceed. The uuid is then verified against the local
server process in handle_hash(); if it is missing there (forged or
expired), an incoming-only client now aborts with an error instead of
falling through to password login, so the outgoing-connection
restriction cannot be bypassed with a crafted --switch_uuid.
Fixesrustdesk/rustdesk#11200 (discussion)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(client): validate switch-back grants before connecting
- check pending peer/UUID grants before bypassing incoming-only mode
- close rejected switch-back connections and suppress retries
- keep grant consumption in handle_hash and test non-consuming checks
Signed-off-by: 21pages <sunboeasy@gmail.com>
* fix(client): prevent switch-back UUID reuse
- claim pending switch-back grants before connecting
- retain claimed grants to reject duplicate requests
- bind authorization to the peer ID and UUID
- use a shared TTL for switch-back grants
Signed-off-by: 21pages <sunboeasy@gmail.com>
* fix(client): defer switch UUID consumption until authentication
Signed-off-by: 21pages <sunboeasy@gmail.com>
* fix(client): reject repeated hash login in incoming-only mode
Signed-off-by: 21pages <sunboeasy@gmail.com>
---------
Signed-off-by: 21pages <sunboeasy@gmail.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: 21pages <sunboeasy@gmail.com>
* swtich_code for hbbs to bypass ACL
* improve register_switch_grant: skip public server, log at error level
Also document why registration is fire-and-forget with no retry: the
peer connects within seconds, so a late retry would land after its
punch request was already rejected; a failed switch is recovered by
the user triggering it again, which registers a fresh grant.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* add timestamp
Signed-off-by: 21pages <sunboeasy@gmail.com>
* fix(switch-sides): handle grant registration clock skew
- retry registration once with the server-provided timestamp
- require an explicit accepted response from hbbs
- report malformed or incomplete responses
Signed-off-by: 21pages <sunboeasy@gmail.com>
* fix(switch-sides): register grants with code verifiers
- send a derived verifier instead of the raw switch code
- use detached signatures for grant registration
- add verifier and signed-message tests
Signed-off-by: 21pages <sunboeasy@gmail.com>
---------
Signed-off-by: 21pages <sunboeasy@gmail.com>
Co-authored-by: 21pages <sunboeasy@gmail.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* 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>
Treat "Access to mobile devices is restricted in your country"
as a non-retriable connection error so the error dialog does not
trigger reconnect attempts.
Signed-off-by: 21pages <sunboeasy@gmail.com>
* Fix: Wayland requires higher version of linux distro. Please try X11 desktop or change your OS. #12897
* refactor(wayland): optimize translation keys for binary size and improve dbus matching
* avatar
* refactor avatar display: unify rendering and resolve at use time
- Extract buildAvatarWidget() in common.dart to share avatar rendering
logic across desktop settings, desktop CM and mobile CM
- Add resolve_avatar_url() in Rust, exposed via FFI (SyncReturn),
to resolve relative avatar paths (e.g. "/avatar/xxx") to absolute URLs
- Store avatar as-is in local config, only resolve when displaying
(settings page) or sending (LoginRequest)
- Resolve avatar in LoginRequest before sending to remote peer
- Add error handling for network image load failures
- Guard against empty client.name[0] crash
- Show avatar in mobile settings page account tile
Signed-off-by: 21pages <sunboeasy@gmail.com>
* web: implement mainResolveAvatarUrl via js getByName
Signed-off-by: 21pages <sunboeasy@gmail.com>
* increase ipc Data enum size limit to 120 bytes
Signed-off-by: 21pages <sunboeasy@gmail.com>
---------
Signed-off-by: 21pages <sunboeasy@gmail.com>
Co-authored-by: 21pages <sunboeasy@gmail.com>
* - UI display: display_name first
- Fallback: name
- Technical identity: still name
### What changed
- Added account display helpers and display_name state in user model:
- flutter/lib/models/user_model.dart:16
- Account/logout label now uses display_name (@name) when both exist:
- flutter/lib/mobile/pages/settings_page.dart:689
- flutter/lib/desktop/pages/desktop_setting_page.dart:2016
- flutter/lib/desktop/pages/desktop_setting_page.dart:2135
- Desktop Account info now shows both when applicable:
- Display Name: ...
- Username: ...
- flutter/lib/desktop/pages/desktop_setting_page.dart:2039
- Previously done group-list behavior remains:
- group user list displays display_name with name fallback
- flutter/lib/common/widgets/my_group.dart:187
- Persistence path for display_name remains enabled (including group cache/submodule field):
- libs/hbb_common/src/config.rs:2347
- src/client.rs:2630
- LoginRequest.my_name now resolves as:
1. OPTION_DISPLAY_NAME (manual override)
2. user_info.display_name
3. user_info.name
4. OS username fallback
* 1. GUID key (...Uninstall\{GUID}) is MSI-native metadata generated by Windows Installer.
2. Non-GUID key (...Uninstall\RustDesk) is explicitly written by RustDesk’s MSI compatibility component in res/msi/Package/Components/Regs.wxs:44, populated by preprocess.py --arp from .github/workflows/
flutter-build.yml:262.
So they were not using the same EstimatedSize logic:
- MSI GUID key: MSI-calculated size (KB).
- RustDesk key: custom script value from res/msi/preprocess.py:339 (previously bytes, now fixed to KB).
That mismatch is exactly why you saw different sizes.
* improve display name handling
- Append (@username) when multiple users share the same display name
- Trim whitespace from display_name before comparison and display
- Add missing translate() for Logout button on desktop
Signed-off-by: 21pages <sunboeasy@gmail.com>
* group peer filter match both user's display name and user's name
Signed-off-by: 21pages <sunboeasy@gmail.com>
* case-insensitive search in group peer filter
Signed-off-by: 21pages <sunboeasy@gmail.com>
---------
Signed-off-by: 21pages <sunboeasy@gmail.com>
Co-authored-by: 21pages <sunboeasy@gmail.com>
The retry logic was blocking retry buttons for errors containing "exist",
which incorrectly filtered out "An existing connection was forcibly closed"
network errors. Changed to "not exist" to only block "ID does not exist"
type errors while allowing legitimate network disconnection errors to show
retry buttons.
Fixes issue where users couldn't retry after network disconnections.
1. Enable the RustDesk client to use WebSocket for either controlling or being controlled.
2. Fix TCP sending `register_pk` frequently
Note:
1. Because hbb_common directly uses `use_ws` to read config directly, rustdesk also directly reads config
Signed-off-by: 21pages <sunboeasy@gmail.com>
* option `allow-d3d-render`, default false
Add this option because it fails on some machines
Signed-off-by: 21pages <sunboeasy@gmail.com>
* only add nokhwa to windows and linux dependencies
Signed-off-by: 21pages <sunboeasy@gmail.com>
---------
Signed-off-by: 21pages <sunboeasy@gmail.com>