Commit Graph

15 Commits

Author SHA1 Message Date
rustdesk
80cd800375 port_forward_mux: a window violation drops the channel on the spot
Both demultiplexers only queued a `Violation` and left the entry until
the channel task woke and exited, so a peer that kept sending past the
window queued one more entry per frame in the meantime, bounded by
nothing. The entry now goes the moment `accept` fails; later frames for
that id are unknown-channel noise.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EZ49AbZJYfm8NTp5yDPMab
2026-09-04 19:51:38 +08:00
rustdesk
945dc30b94 port forward: one tunnel per mapping, bound to the authenticated target
The login latches `PortForward.host`/`port` into the session scope and
approval is shown that target, but a window-wide tunnel let any later
`open` name another target with only `enable-tunnel` rechecked. A
tunnel now belongs to one listener and serves the one target its login
authenticated: the controlled side refuses an `open` for any other
target, and a window with several targets uses one connection each,
approved on its own.

With one owner per tunnel the claim needs no waiters: `Establishing`,
`Claim::Wait` and `wait_ready` go, and `try_claim` becomes a plain
read. The CM label that followed a tunnel's targets goes with them; a
row shows its mapping's target, as before.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EZ49AbZJYfm8NTp5yDPMab
2026-09-04 19:51:38 +08:00
rustdesk
6a490ed109 port_forward_mux: a tunnel ends when its window drops the Tunnel
The loop held its own handle and state sender, so once the window
closed nothing was left to stop it: it kept answering TestDelay and the
peer connection, CM row included, lived on until the peer went away.
`Tunnel` now owns a watch sender nobody sends on; the loop's receiver
errors when the last `Tunnel` drops, and the loop ends.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EZ49AbZJYfm8NTp5yDPMab
2026-09-04 19:51:38 +08:00
rustdesk
adda994a42 server: admit only INITIAL_WINDOW on a channel before opened
The demultiplexer accepted CHANNEL_WINDOW into a pending channel's
unbounded queue, four times the bound the channel task enforces once
it polls. The window now starts at INITIAL_WINDOW and is widened right
before `opened` advertises the rest.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EZ49AbZJYfm8NTp5yDPMab
2026-09-04 19:51:38 +08:00
rustdesk
7f2f4c41a8 port forward: apply the whole-branch review
Correctness:
- listen(): the Legacy arm is merged with the Claimed arm. On its own it
  ignored outcome.local_eof, so a client that hung up during login still
  got a target connect, an audit record and a CM row on the controlled
  side, and ignored outcome.mux, so a peer upgraded while a legacy window
  stayed open answered as a tunnel while the controller went raw.
- Refusal dialogs are deduplicated per quiet spell (10 s) rather than per
  tunnel lifetime; the lifetime set went silent for the rest of a
  long-lived window after the first burst.
- Android's CM listener handles UpdatePortForward; it fell into `_ => {}`.
- relay_socket_to_tunnel reads into one scratch buffer per channel and
  sends an exact-size copy. A frame owning its 64 KiB read allocation
  pinned it until sent, once per byte on interactive traffic.

Consistency and cleanups:
- The controlled side's refusal text is the raw pipe's wording, RDP
  substitution included.
- connection.rs: the PortForwardChannel arm is a one-line hook, the CM
  label is pushed from the 1 s tick alone, and the unreachable inner.tx
  fall-through is gone.
- The Ready enum is removed; wait_ready() returns Option<Claim>.
- SendCredit::add wakes with notify_one alone.
- on_ui_command() replaces the two ui_receiver handlers in listen().
- TunnelHandle is no longer re-exported (unused-import warning).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EZ49AbZJYfm8NTp5yDPMab
2026-09-04 19:51:38 +08:00
rustdesk
22a6abbeec Use on_error for refused-channel dialog in tunnel_loop
Redirect the refused-channel error through the standard on_error path
instead of calling msgbox directly, for consistency with other errors
in the port-forward flow.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EZ49AbZJYfm8NTp5yDPMab
2026-09-04 19:51:38 +08:00
rustdesk
cc83c636a4 port_forward_mux: report a refused channel's reason as an error dialog
The controlled side already answers a refused port-forward channel with
opened { success: false, message }; on the multiplexed path TunnelHandle::
on_frame only logged that message at debug and closed the channel, so the
user saw a closed connection with no explanation, worst on the RDP path
where only the RDP client's own error remained. on_frame now returns the
message the window should show, deduplicated per distinct reason (capped
at MAX_REPORTED_OPEN_ERRORS) so one page load's dozen refused connections
surface one dialog per reason instead of a dozen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EZ49AbZJYfm8NTp5yDPMab
2026-09-04 19:51:38 +08:00
rustdesk
dc50873db6 port_forward_mux: cap send credit and other final review fixes
Fix 1 (critical): clamp SendCredit to MAX_SEND_CREDIT (= CHANNEL_WINDOW)
in both new() and add(), so a peer with tunnel permission can no longer
advertise an unbounded window and force the controlled side's unbounded
FrameSink::Direct sink to buffer unlimited target data per channel.

Fix 2: rename the "starve" test to many_channels_echo_concurrently and
drop its (untrue) starvation claim, since it opens every channel before
the bulk transfer starts. Add a_channel_opened_during_a_bulk_transfer_
is_served_promptly, which opens the small channel while the bulk one is
demonstrably mid-flight.

Fix 3: only look up the tunnel permission for `open` frames in the
PortForwardChannel arm of on_message, instead of once per data frame.

Fix 4: two rustfmt deviations in connection.rs (matches! wrapping and a
tuple literal), fixed by hand without a blanket cargo fmt run.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EZ49AbZJYfm8NTp5yDPMab
2026-09-04 19:51:38 +08:00
rustdesk
edd107099a port_forward_mux: fix bulk test's premature half-close, pin the half-close limitation
many_channels_echo_concurrently_and_a_bulk_one_does_not_starve_them dropped
its bulk write half as soon as writing finished, which shuts down the write
side of the socket and, by design (see the design doc's TCP half-close
non-goal; today's run_forward does the same), ends the whole channel. Keep
the write half alive until the reader is done so the test measures
starvation, not half-close. Add a_local_half_close_ends_the_whole_channel to
pin that limitation in code.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EZ49AbZJYfm8NTp5yDPMab
2026-09-04 19:51:38 +08:00
rustdesk
91ed741704 port_forward_mux: end-to-end tests over a loopback tunnel
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EZ49AbZJYfm8NTp5yDPMab
2026-09-04 19:51:38 +08:00
rustdesk
97b7d161cf port_forward_mux: publish Muxed before spawning the tunnel loop
Publishing after spawn let a loop that dies immediately reset the state
first, so the later publish pinned it at Muxed with a dead handle
forever. Also adds a test pinning open-before-data ordering across many
concurrently opened channels, and drops an unused Clone derive.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EZ49AbZJYfm8NTp5yDPMab
2026-09-04 19:51:38 +08:00
rustdesk
a5c2b58a51 port_forward_mux: controller tunnel with a single-writer stream loop
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EZ49AbZJYfm8NTp5yDPMab
2026-09-04 19:51:38 +08:00
rustdesk
975ab973c5 port_forward_mux: credit-windowed relay halves and channel coordinator
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EZ49AbZJYfm8NTp5yDPMab
2026-09-04 19:51:38 +08:00
rustdesk
6a68bb5db7 port_forward_mux: fix RecvWindow counter overflow on long transfers
Replace cumulative accounting (granted/received) with remaining credit
tracking to prevent u32 overflow after 4 GiB of data on a single channel.
Wire behavior is identical, but the fix allows large file transfers
without mid-stream channel closure.

Add regression test for 8 GiB transfer to verify fix.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EZ49AbZJYfm8NTp5yDPMab
2026-09-04 19:51:38 +08:00
rustdesk
ff41979a7a port_forward_mux: window accounting and channel frame builders
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EZ49AbZJYfm8NTp5yDPMab
2026-09-04 19:51:38 +08:00