1+ use ckb_async_runtime:: Handle ;
12use ckb_error:: { Error , InternalErrorKind } ;
23use ckb_logger:: { debug, error, info, warn} ;
34use futures:: future:: BoxFuture ;
45use std:: borrow:: Cow ;
56use std:: net:: SocketAddr ;
67use std:: time:: Duration ;
78use tokio:: fs:: File ;
8- use tokio:: io:: AsyncReadExt ;
9+ use tokio:: io:: { duplex , AsyncRead , AsyncReadExt , AsyncWrite , DuplexStream } ;
910use tokio:: net:: TcpStream ;
11+ use tokio:: sync:: mpsc;
1012use torut:: control:: { TorAuthData , TorAuthMethod , UnauthenticatedConn , COOKIE_LENGTH } ;
1113use torut:: {
1214 control:: { AsyncEvent , AuthenticatedConn , ConnError } ,
@@ -15,12 +17,18 @@ use torut::{
1517
1618use crate :: TorEventHandlerFn ;
1719
18- type TorAuthenticatedConn =
19- AuthenticatedConn < TcpStream , fn ( AsyncEvent < ' _ > ) -> BoxFuture < ' static , Result < ( ) , ConnError > > > ;
20+ type TorAuthenticatedConn = AuthenticatedConn <
21+ DuplexStream ,
22+ fn ( AsyncEvent < ' _ > ) -> BoxFuture < ' static , Result < ( ) , ConnError > > ,
23+ > ;
2024
2125/// A controller for a Tor server.
2226pub struct TorController {
2327 inner : TorAuthenticatedConn ,
28+ /// Notified when the underlying TCP connection to the Tor control port is
29+ /// closed or encounters a fatal I/O error. Receiving a value indicates
30+ /// the connection is dead.
31+ _disconnect_rx : mpsc:: UnboundedReceiver < ( ) > ,
2432}
2533
2634impl TorController {
@@ -30,8 +38,13 @@ impl TorController {
3038 tor_controller_url : String ,
3139 tor_password : Option < String > ,
3240 event_handler : Option < TorEventHandlerFn > ,
41+ handle : Handle ,
3342 ) -> Result < Self , Error > {
34- let s = TcpStream :: connect ( tor_controller_url. clone ( ) )
43+ let ( client, server) = duplex ( 1024 ) ;
44+
45+ let ( disconnect_tx, disconnect_rx) = mpsc:: unbounded_channel ( ) ;
46+
47+ let raw_stream = TcpStream :: connect ( tor_controller_url. clone ( ) )
3548 . await
3649 . map_err ( |err| {
3750 InternalErrorKind :: Other . other ( format ! (
@@ -40,15 +53,45 @@ impl TorController {
4053 ) )
4154 } ) ?;
4255
43- let mut utc: UnauthenticatedConn < TcpStream > = UnauthenticatedConn :: new ( s) ;
56+ handle. spawn ( async move {
57+ let ( mut tcp_read, mut tcp_write) = raw_stream. into_split ( ) ;
58+ let ( mut server_read, mut server_write) = tokio:: io:: split ( server) ;
59+
60+ tokio:: select! {
61+ // TCP -> Duplex
62+ res = tokio:: io:: copy( & mut tcp_read, & mut server_write) => {
63+ match res {
64+ Ok ( n) => debug!( "Tor TCP connection closed after {} bytes" , n) ,
65+ Err ( e) => error!( "Tor TCP read error: {}" , e) ,
66+ }
67+ _ = disconnect_tx. send( ( ) ) ;
68+ }
69+
70+ // Duplex -> TCP
71+ res = tokio:: io:: copy( & mut server_read, & mut tcp_write) => {
72+ match res {
73+ Ok ( n) => debug!( "Tor Duplex closed after {} bytes" , n) ,
74+ Err ( e) => error!( "Tor TCP write error: {}" , e) ,
75+ }
76+ _ = disconnect_tx. send( ( ) ) ;
77+ }
78+ }
79+
80+ debug ! ( "Tor DuplexStream exited." ) ;
81+ } ) ;
82+
83+ let mut utc: UnauthenticatedConn < _ > = UnauthenticatedConn :: new ( client) ;
4484
4585 authenticate ( tor_password, & mut utc) . await ?;
4686
4787 let mut ac = utc. into_authenticated ( ) . await ;
4888
4989 ac. set_async_event_handler ( event_handler) ;
5090
51- Ok ( TorController { inner : ac } )
91+ Ok ( TorController {
92+ inner : ac,
93+ _disconnect_rx : disconnect_rx,
94+ } )
5295 }
5396
5497 /// get tor server's status
@@ -112,6 +155,16 @@ impl TorController {
112155 Ok ( ( ) )
113156 }
114157
158+ /// Waits asynchronously until the underlying TCP connection to the Tor
159+ /// controller is severed (either cleanly closed or due to an I/O error).
160+ ///
161+ /// Returns `Some(())` when the connection is lost, or `None` if the internal
162+ /// notification channel was closed unexpectedly (which should not occur during
163+ /// normal operation).
164+ pub async fn wait_for_disconnect ( & mut self ) -> Option < ( ) > {
165+ self . _disconnect_rx . recv ( ) . await
166+ }
167+
115168 /// Add a new v3 onion service to the Tor server.
116169 pub async fn add_onion_v3 (
117170 & mut self ,
@@ -125,10 +178,13 @@ impl TorController {
125178}
126179
127180/// Authenticates with the Tor controller using the given password or cookie.
128- pub async fn authenticate (
181+ pub async fn authenticate < S > (
129182 tor_password : Option < String > ,
130- utc : & mut UnauthenticatedConn < TcpStream > ,
131- ) -> Result < ( ) , Error > {
183+ utc : & mut UnauthenticatedConn < S > ,
184+ ) -> Result < ( ) , Error >
185+ where
186+ S : AsyncRead + AsyncWrite + Unpin ,
187+ {
132188 let proto_info = utc. load_protocol_info ( ) . await . map_err ( |err| {
133189 InternalErrorKind :: Other . other ( format ! ( "Failed to load protocol info: {:?}" , err) )
134190 } ) ?;
0 commit comments