11use core:: net:: SocketAddr ;
2+ use core:: time:: Duration ;
23use std:: rc:: Rc ;
34use std:: sync:: Arc ;
5+ use std:: time:: Instant ;
46
57use anyhow:: { Context as _, Result , bail} ;
68use ironrdp_acceptor:: { Acceptor , AcceptorResult , BeginResult , DesktopSize } ;
@@ -22,13 +24,47 @@ use ironrdp_svc::{ChannelFlags, StaticChannelId, StaticChannelSet, SvcProcessor,
2224use ironrdp_tokio:: { FramedRead , FramedWrite , TokioFramed , split_tokio_framed, unsplit_tokio_framed} ;
2325use rdpsnd:: server:: { RdpsndServer , RdpsndServerMessage } ;
2426use tokio:: io:: { AsyncRead , AsyncWrite , AsyncWriteExt as _} ;
25- use tokio:: net:: TcpListener ;
27+ use tokio:: net:: TcpSocket ;
2628use tokio:: sync:: { Mutex , mpsc, oneshot} ;
2729use tokio:: task;
2830use tokio_rustls:: TlsAcceptor ;
2931use tracing:: { debug, error, trace, warn} ;
3032use { ironrdp_dvc as dvc, ironrdp_rdpsnd as rdpsnd} ;
3133
34+ /// Connection lifecycle handler for `RdpServer::run()`.
35+ ///
36+ /// Implement this trait to hook into the TCP accept loop. The default
37+ /// implementation is a no-op, so existing callers are unaffected.
38+ ///
39+ /// # Example
40+ ///
41+ /// ```rust,ignore
42+ /// use ironrdp_server::ConnectionHandler;
43+ /// use std::net::SocketAddr;
44+ /// use std::time::Duration;
45+ ///
46+ /// struct MyHandler;
47+ ///
48+ /// impl ConnectionHandler for MyHandler {
49+ /// fn on_accept(&self, peer: SocketAddr) -> bool {
50+ /// println!("Connection from {peer}");
51+ /// true // accept all connections
52+ /// }
53+ /// }
54+ /// ```
55+ pub trait ConnectionHandler : Send + Sync {
56+ /// Called after TCP accept, before the RDP handshake begins.
57+ ///
58+ /// Return `true` to proceed with the connection, or `false` to
59+ /// drop it immediately (e.g. for rate limiting).
60+ fn on_accept ( & self , _peer : SocketAddr ) -> bool {
61+ true
62+ }
63+
64+ /// Called after a connection ends, whether successfully or with an error.
65+ fn on_disconnected ( & self , _peer : SocketAddr , _duration : Duration , _error : Option < & anyhow:: Error > ) { }
66+ }
67+
3268use crate :: clipboard:: CliprdrServerFactory ;
3369use crate :: display:: { DisplayUpdate , RdpServerDisplay } ;
3470use crate :: echo:: { EchoDvcBridge , EchoServerHandle , EchoServerMessage , build_echo_request} ;
@@ -240,6 +276,7 @@ pub struct RdpServer {
240276 ev_receiver : Arc < Mutex < mpsc:: UnboundedReceiver < ServerEvent > > > ,
241277 creds : Option < Credentials > ,
242278 local_addr : Option < SocketAddr > ,
279+ connection_handler : Option < Box < dyn ConnectionHandler > > ,
243280}
244281
245282#[ derive( Debug ) ]
@@ -307,9 +344,18 @@ impl RdpServer {
307344 ev_receiver : Arc :: new ( Mutex :: new ( ev_receiver) ) ,
308345 creds : None ,
309346 local_addr : None ,
347+ connection_handler : None ,
310348 }
311349 }
312350
351+ /// Set an optional connection lifecycle handler.
352+ ///
353+ /// The handler is called on TCP accept and disconnect events during
354+ /// `run()`. Has no effect when using `run_connection()` directly.
355+ pub fn set_connection_handler ( & mut self , handler : Box < dyn ConnectionHandler > ) {
356+ self . connection_handler = Some ( handler) ;
357+ }
358+
313359 pub fn builder ( ) -> builder:: RdpServerBuilder < builder:: WantsAddr > {
314360 builder:: RdpServerBuilder :: new ( )
315361 }
@@ -448,7 +494,25 @@ impl RdpServer {
448494 }
449495
450496 pub async fn run ( & mut self ) -> Result < ( ) > {
451- let listener = TcpListener :: bind ( self . opts . addr ) . await ?;
497+ // Create socket with SO_REUSEADDR and IPv6 dual-stack support.
498+ // Using TcpSocket instead of TcpListener::bind() gives control
499+ // over socket options before binding.
500+ let socket = match self . opts . addr {
501+ SocketAddr :: V4 ( _) => TcpSocket :: new_v4 ( ) . context ( "Failed to create IPv4 socket" ) ?,
502+ SocketAddr :: V6 ( _) => {
503+ // IPv6 socket with dual-stack: accepts both IPv4 and IPv6
504+ // on a single socket. IPv4 clients appear as IPv4-mapped
505+ // addresses (::ffff:x.x.x.x). Dual-stack is the Linux
506+ // default (net.ipv6.bindv6only=0).
507+ TcpSocket :: new_v6 ( ) . context ( "Failed to create IPv6 socket" ) ?
508+ }
509+ } ;
510+
511+ socket. set_reuseaddr ( true ) . context ( "Failed to set SO_REUSEADDR" ) ?;
512+
513+ socket. bind ( self . opts . addr ) . context ( "Failed to bind listen address" ) ?;
514+
515+ let listener = socket. listen ( 1024 ) . context ( "Failed to start listener" ) ?;
452516 let local_addr = listener. local_addr ( ) ?;
453517
454518 debug ! ( "Listening for connections on {local_addr}" ) ;
@@ -476,11 +540,36 @@ impl RdpServer {
476540 }
477541 } ,
478542 Ok ( ( stream, peer) ) = listener. accept( ) => {
543+ // Connection lifecycle: on_accept hook
544+ if let Some ( ref handler) = self . connection_handler {
545+ if !handler. on_accept( peer) {
546+ debug!( ?peer, "Connection rejected by handler" ) ;
547+ continue ;
548+ }
549+ }
550+
479551 debug!( ?peer, "Received connection" ) ;
480552 drop( ev_receiver) ;
481- if let Err ( error) = self . run_connection( stream) . await {
482- error!( ?error, "Connection error" ) ;
553+
554+ let start = Instant :: now( ) ;
555+ let result = self . run_connection( stream) . await ;
556+ let duration = start. elapsed( ) ;
557+
558+ // Connection lifecycle: on_disconnected hook
559+ match & result {
560+ Ok ( ( ) ) => {
561+ if let Some ( ref handler) = self . connection_handler {
562+ handler. on_disconnected( peer, duration, None ) ;
563+ }
564+ }
565+ Err ( error) => {
566+ if let Some ( ref handler) = self . connection_handler {
567+ handler. on_disconnected( peer, duration, Some ( error) ) ;
568+ }
569+ error!( ?error, "Connection error" ) ;
570+ }
483571 }
572+
484573 self . static_channels = StaticChannelSet :: new( ) ;
485574 }
486575 else => break ,
0 commit comments