Skip to content

feat(server): IPv6 dual-stack and SO_REUSEADDR for run()#1187

Merged
Benoît Cortier (CBenoit) merged 2 commits into
Devolutions:masterfrom
lamco-admin:feat/enhanced-server-listener
Apr 1, 2026
Merged

feat(server): IPv6 dual-stack and SO_REUSEADDR for run()#1187
Benoît Cortier (CBenoit) merged 2 commits into
Devolutions:masterfrom
lamco-admin:feat/enhanced-server-listener

Conversation

@glamberson
Copy link
Copy Markdown
Contributor

@glamberson Greg Lamberson (glamberson) commented Mar 28, 2026

Replace TcpListener::bind() with TcpSocket for control over socket
options before binding:

SO_REUSEADDR: Prevents EADDRINUSE on server restart. The previous
TcpListener::bind() doesn't set this, so the port stays in TIME_WAIT
for ~60 seconds after shutdown.

IPv6 dual-stack: Detects address family from the configured
SocketAddr. When IPv6 (e.g. [::]:3389), creates a v6 socket that
accepts both IPv4 and IPv6 via dual-stack (Linux default
bindv6only=0). When IPv4, uses new_v4() as before.

Backlog: Increased from the TcpListener default to 1024.

No new dependencies. Backward compatible.

Replace TcpListener::bind() with TcpSocket for control over socket
options before binding:

- SO_REUSEADDR: prevents EADDRINUSE on server restart (TIME_WAIT)
- IPv6 dual-stack: when bound to [::], accepts both IPv4 and IPv6
  on a single socket. Address family is detected from SocketAddr.
- Backlog increased from default to 1024
@glamberson Greg Lamberson (glamberson) changed the title feat(server): IPv6 dual-stack, SO_REUSEADDR, and ConnectionHandler for run() feat(server): IPv6 dual-stack and SO_REUSEADDR for run() Mar 28, 2026
Comment thread crates/ironrdp-server/src/server.rs Outdated
Copy link
Copy Markdown
Member

@CBenoit Benoît Cortier (CBenoit) left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the RDP server’s run() listener setup to use tokio::net::TcpSocket so socket options can be set prior to binding, aiming to improve restart behavior and support IPv6 dual-stack binding.

Changes:

  • Replace TcpListener::bind() with TcpSocket to configure socket options before bind().
  • Enable SO_REUSEADDR to reduce EADDRINUSE likelihood on restart.
  • Increase listen backlog to 1024.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread crates/ironrdp-server/src/server.rs Outdated
Comment thread crates/ironrdp-server/src/server.rs Outdated
Comment thread crates/ironrdp-server/src/server.rs
Replace TcpListener::bind() with TcpSocket for control over socket
options before binding:

SO_REUSEADDR (unix-only): Prevents EADDRINUSE on server restart. Gated
behind cfg(unix) because Windows SO_REUSEADDR has different semantics
that allow port hijacking.

IPv6 dual-stack: Detects address family from configured SocketAddr.
When IPv6, documents platform-specific IPV6_V6ONLY behavior (Linux
defaults to dual-stack, Windows/BSDs may not).

Backlog: Extracted to LISTENER_BACKLOG constant (1024).

No new dependencies. Backward compatible.
@CBenoit Benoît Cortier (CBenoit) merged commit f10625c into Devolutions:master Apr 1, 2026
10 checks passed
@glamberson Greg Lamberson (glamberson) deleted the feat/enhanced-server-listener branch May 15, 2026 18:00
Greg Lamberson (glamberson) added a commit to lamco-admin/IronRDP that referenced this pull request May 20, 2026
Reshape the public surface introduced earlier in this PR before it
lands, to avoid a future breaking touch when ironrdp-server's
anyhow-removal pass arrives. Mirrors the post-Devolutions#1264 hand-rolled
error pattern (Display + core::error::Error impls, no thiserror).

Trait signature was `Result<bool>` (i.e. anyhow::Result through the
file-level `use anyhow::Result`). Two issues:

- New public surface depending on anyhow at a moment when the
  workspace is moving the other way (Devolutions#1277 just removed anyhow from
  rdpsnd-native).
- `Ok(true)` vs `Ok(false)` is stringly typed at the call site: a
  bare bool with no semantic clue.

New shape:

- `CredentialDecision::{Accept, Reject}` for the binary outcome.
- `CredentialValidationError` wrapping any `core::error::Error +
  Send + Sync` for the case where the validator backend itself
  failed (LDAP unreachable, PAM transport error, etc.). Manual
  Display + Error impls; source chains through.
- `validate(&self, &Credentials) -> Result<CredentialDecision,
  CredentialValidationError>`.

The accept/reject/backend-error trichotomy is now explicit at every
call site. Rejection is no longer an error; backend failure is.
Log + bail strings updated to match the trichotomy.

Also addresses the API consistency note: every other configurable
on `RdpServerBuilder<BuilderDone>` goes through `with_*` on the
builder. Added `with_credential_validator(Option<Arc<dyn ...>>)`
following the same shape as `with_connection_handler`. The
post-construction `set_credential_validator` setter is kept (now
takes `Option` to match the field and the builder's setter shape)
for dynamic reconfiguration after `build()`.

Tests added in `server.rs` cover Accept / Reject / backend-error
propagation and `Arc<dyn CredentialValidator>` Send + Sync + 'static
bounds. Integration with `client_accepted` is exercised through
the existing acceptor-side tests once the validator is wired via
the builder; no new integration test in this commit (server-side
precedent per Devolutions#1181 / Devolutions#1187 / Devolutions#1281).

Verification: `cargo test -p ironrdp-server --lib` (14 passed),
`cargo clippy -p ironrdp-server --all-targets -- -D warnings` clean,
`cargo check -p ironrdp-server` clean.

Refs Devolutions#1154, Devolutions#1150, Devolutions#1155.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

4 participants