11use arti_client:: TorClient ;
22use libp2p:: core:: multiaddr:: Protocol ;
33use libp2p:: core:: transport:: { ListenerId , TransportEvent } ;
4+ use libp2p:: { Multiaddr , Transport , TransportError } ;
5+ use std:: future:: Future ;
6+ use std:: net:: SocketAddr ;
7+ use std:: pin:: Pin ;
48use std:: sync:: Arc ;
9+ use std:: task:: { Context , Poll } ;
10+ use tokio:: net:: TcpStream ;
11+ use tokio_socks:: tcp:: Socks5Stream ;
12+ use tokio_socks:: TargetAddr ;
513use tor_rtcompat:: tokio:: TokioRustlsRuntime ;
614
15+ fn onion3_to_dotonion ( service : & [ u8 ; 35 ] ) -> String {
16+ let mut domain = data_encoding:: BASE32 . encode ( service) . to_lowercase ( ) ;
17+ domain. push_str ( ".onion" ) ;
18+ domain
19+ }
20+ fn multi_to_socks ( addr : & Multiaddr ) -> Option < TargetAddr < ' static > > {
21+ let mut addr = addr. iter ( ) ;
22+ match ( addr. next ( ) ?, addr. next ( ) ) {
23+ (
24+ Protocol :: Dns ( domain) | Protocol :: Dns4 ( domain) | Protocol :: Dns6 ( domain) ,
25+ Some ( Protocol :: Tcp ( port) ) ,
26+ ) => Some ( TargetAddr :: Domain ( domain. into_owned ( ) . into ( ) , port) ) ,
27+ ( Protocol :: Onion3 ( service) , _) => Some ( TargetAddr :: Domain (
28+ onion3_to_dotonion ( service. hash ( ) ) . into ( ) ,
29+ service. port ( ) ,
30+ ) ) ,
31+ ( Protocol :: Ip4 ( ip) , Some ( Protocol :: Tcp ( port) ) ) => {
32+ Some ( TargetAddr :: Ip ( SocketAddr :: from ( ( ip, port) ) ) )
33+ }
34+ ( Protocol :: Ip6 ( ip) , Some ( Protocol :: Tcp ( port) ) ) => {
35+ Some ( TargetAddr :: Ip ( SocketAddr :: from ( ( ip, port) ) ) )
36+ }
37+ _ => None ,
38+ }
39+ }
40+ #[ cfg( test) ]
41+ mod tests {
42+ use std:: net:: { Ipv4Addr , Ipv6Addr , SocketAddr } ;
43+ use tokio_socks:: TargetAddr ;
44+
45+ const MULTIS : [ & str ; 6 ] = [
46+ "/dns/ip.tld/tcp/10" ,
47+ "/dns4/dns.ip4.tld/tcp/11" ,
48+ "/dns6/dns.ip6.tld/tcp/12" ,
49+ "/onion3/cebulka7uxchnbpvmqapg5pfos4ngaxglsktzvha7a5rigndghvadeyd:13" ,
50+ "/ip4/127.0.0.1/tcp/10" ,
51+ "/ip6/::1/tcp/10" ,
52+ ] ;
53+
54+ #[ test]
55+ fn multi_to_socks ( ) {
56+ assert_eq ! (
57+ MULTIS . map( |ma| super :: multi_to_socks( & ma. parse( ) . unwrap( ) ) . unwrap( ) ) ,
58+ [
59+ TargetAddr :: Domain ( "ip.tld" . into( ) , 10 ) ,
60+ TargetAddr :: Domain ( "dns.ip4.tld" . into( ) , 11 ) ,
61+ TargetAddr :: Domain ( "dns.ip6.tld" . into( ) , 12 ) ,
62+ TargetAddr :: Domain (
63+ "cebulka7uxchnbpvmqapg5pfos4ngaxglsktzvha7a5rigndghvadeyd.onion" . into( ) ,
64+ 13 ,
65+ ) ,
66+ TargetAddr :: Ip ( SocketAddr :: new( Ipv4Addr :: LOCALHOST . into( ) , 10 ) ) ,
67+ TargetAddr :: Ip ( SocketAddr :: new( Ipv6Addr :: LOCALHOST . into( ) , 10 ) ) ,
68+ ] ,
69+ ) ;
70+ }
71+ }
72+
73+ pub struct Socks5Transport ( SocksServerAddress ) ;
74+ impl Transport for Socks5Transport {
75+ type Output = tokio_util:: compat:: Compat < TcpStream > ;
76+ type Error = tokio_socks:: Error ;
77+ type ListenerUpgrade = std:: future:: Pending < Result < Self :: Output , Self :: Error > > ;
78+ type Dial = Pin < Box < dyn Future < Output = Result < Self :: Output , Self :: Error > > + Send + ' static > > ;
79+
80+ fn listen_on (
81+ & mut self ,
82+ _: ListenerId ,
83+ addr : Multiaddr ,
84+ ) -> Result < ( ) , TransportError < Self :: Error > > {
85+ Err ( TransportError :: MultiaddrNotSupported ( addr) )
86+ }
87+
88+ fn remove_listener ( & mut self , _: ListenerId ) -> bool {
89+ false
90+ }
91+
92+ fn dial ( & mut self , addr : Multiaddr ) -> Result < Self :: Dial , TransportError < Self :: Error > > {
93+ let target = multi_to_socks ( & addr) . ok_or ( TransportError :: MultiaddrNotSupported ( addr) ) ?;
94+ let proxy = self . 0 ;
95+
96+ Ok ( Box :: pin ( async move {
97+ Ok ( tokio_util:: compat:: TokioAsyncReadCompatExt :: compat (
98+ proxy. proxy ( target) . await ?,
99+ ) )
100+ } ) )
101+ }
102+
103+ fn dial_as_listener (
104+ & mut self ,
105+ addr : Multiaddr ,
106+ ) -> Result < Self :: Dial , TransportError < Self :: Error > > {
107+ self . dial ( addr)
108+ }
109+
110+ fn poll (
111+ self : Pin < & mut Self > ,
112+ _: & mut Context < ' _ > ,
113+ ) -> Poll < TransportEvent < Self :: ListenerUpgrade , Self :: Error > > {
114+ Poll :: Pending
115+ }
116+
117+ fn address_translation ( & self , _: & Multiaddr , _: & Multiaddr ) -> Option < Multiaddr > {
118+ None
119+ }
120+ }
121+
122+ #[ derive( Debug , Copy , Clone ) ]
123+ pub struct SocksServerAddress ( pub SocketAddr ) ;
124+
125+ impl SocksServerAddress {
126+ pub fn transport ( self ) -> Socks5Transport {
127+ tracing:: debug!( "Using SOCKS5 proxy at {:?}" , self . 0 ) ;
128+ Socks5Transport ( self )
129+ }
130+
131+ pub async fn connect ( & self ) -> std:: io:: Result < TcpStream > {
132+ TcpStream :: connect ( self . 0 ) . await
133+ }
134+
135+ pub async fn proxy ( & self , target : TargetAddr < ' _ > ) -> Result < TcpStream , tokio_socks:: Error > {
136+ Socks5Stream :: connect_with_socket ( self . connect ( ) . await ?, target)
137+ . await
138+ . map ( Socks5Stream :: into_inner)
139+ }
140+ }
141+
7142pub type TcpTransport = libp2p:: dns:: tokio:: Transport < libp2p:: tcp:: tokio:: Transport > ;
8143
9144#[ derive( Clone ) ]
10145pub enum TorBackend {
11146 /// Private Tor client
12147 Arti ( Arc < TorClient < TokioRustlsRuntime > > ) ,
148+ /// Talking through a Tor SOCKS5 proxy
149+ Socks ( SocksServerAddress ) ,
13150 /// No Tor at all
14151 None ,
15152}
@@ -18,6 +155,7 @@ impl std::fmt::Debug for TorBackend {
18155 fn fmt ( & self , f : & mut std:: fmt:: Formatter ) -> Result < ( ) , std:: fmt:: Error > {
19156 f. write_str ( match self {
20157 TorBackend :: Arti ( ..) => "Arti" ,
158+ TorBackend :: Socks ( ..) => "Socks" ,
21159 TorBackend :: None => "None" ,
22160 } )
23161 }
0 commit comments