33use anyhow:: { Context as _, Result } ;
44use http_client:: Url ;
55use tokio:: net:: TcpStream ;
6- use tokio_socks:: tcp:: { Socks4Stream , Socks5Stream } ;
6+ use tokio_socks:: {
7+ IntoTargetAddr , TargetAddr ,
8+ tcp:: { Socks4Stream , Socks5Stream } ,
9+ } ;
710
811use super :: AsyncReadWrite ;
912
@@ -23,8 +26,14 @@ pub(super) struct Socks5Authorization<'a> {
2326/// V4 allows idenfication using a user_id
2427/// V5 allows authorization using a username and password
2528pub ( super ) enum SocksVersion < ' a > {
26- V4 ( Option < Socks4Identification < ' a > > ) ,
27- V5 ( Option < Socks5Authorization < ' a > > ) ,
29+ V4 {
30+ local_dns : bool ,
31+ identification : Option < Socks4Identification < ' a > > ,
32+ } ,
33+ V5 {
34+ local_dns : bool ,
35+ authorization : Option < Socks5Authorization < ' a > > ,
36+ } ,
2837}
2938
3039pub ( super ) fn parse_socks_proxy < ' t > ( scheme : & str , proxy : & ' t Url ) -> SocksVersion < ' t > {
@@ -33,13 +42,19 @@ pub(super) fn parse_socks_proxy<'t>(scheme: &str, proxy: &'t Url) -> SocksVersio
3342 "" => None ,
3443 username => Some ( Socks4Identification { user_id : username } ) ,
3544 } ;
36- SocksVersion :: V4 ( identification)
45+ SocksVersion :: V4 {
46+ local_dns : scheme != "socks4a" ,
47+ identification,
48+ }
3749 } else {
3850 let authorization = proxy. password ( ) . map ( |password| Socks5Authorization {
3951 username : proxy. username ( ) ,
4052 password,
4153 } ) ;
42- SocksVersion :: V5 ( authorization)
54+ SocksVersion :: V5 {
55+ local_dns : scheme != "socks5h" ,
56+ authorization,
57+ }
4358 }
4459}
4560
@@ -48,26 +63,58 @@ pub(super) async fn connect_socks_proxy_stream(
4863 socks_version : SocksVersion < ' _ > ,
4964 rpc_host : ( & str , u16 ) ,
5065) -> Result < Box < dyn AsyncReadWrite > > {
66+ let rpc_host = rpc_host
67+ . into_target_addr ( )
68+ . context ( "Failed to parse target addr" ) ?;
69+
70+ let local_dns = match & socks_version {
71+ SocksVersion :: V4 { local_dns, .. } => local_dns,
72+ SocksVersion :: V5 { local_dns, .. } => local_dns,
73+ } ;
74+ let rpc_host = match ( rpc_host, local_dns) {
75+ ( TargetAddr :: Domain ( domain, port) , true ) => {
76+ let ip_addr = tokio:: net:: lookup_host ( ( domain. as_ref ( ) , port) )
77+ . await
78+ . with_context ( || format ! ( "Failed to lookup domain {}" , domain) ) ?
79+ . next ( )
80+ . ok_or_else ( || anyhow:: anyhow!( "Failed to lookup domain {}" , domain) ) ?;
81+ TargetAddr :: Ip ( ip_addr)
82+ }
83+ ( rpc_host, _) => rpc_host,
84+ } ;
85+
5186 match socks_version {
52- SocksVersion :: V4 ( None ) => {
87+ SocksVersion :: V4 {
88+ identification : None ,
89+ ..
90+ } => {
5391 let socks = Socks4Stream :: connect_with_socket ( stream, rpc_host)
5492 . await
5593 . context ( "error connecting to socks" ) ?;
5694 Ok ( Box :: new ( socks) )
5795 }
58- SocksVersion :: V4 ( Some ( Socks4Identification { user_id } ) ) => {
96+ SocksVersion :: V4 {
97+ identification : Some ( Socks4Identification { user_id } ) ,
98+ ..
99+ } => {
59100 let socks = Socks4Stream :: connect_with_userid_and_socket ( stream, rpc_host, user_id)
60101 . await
61102 . context ( "error connecting to socks" ) ?;
62103 Ok ( Box :: new ( socks) )
63104 }
64- SocksVersion :: V5 ( None ) => {
105+ SocksVersion :: V5 {
106+ authorization : None ,
107+ ..
108+ } => {
65109 let socks = Socks5Stream :: connect_with_socket ( stream, rpc_host)
66110 . await
67111 . context ( "error connecting to socks" ) ?;
68112 Ok ( Box :: new ( socks) )
69113 }
70- SocksVersion :: V5 ( Some ( Socks5Authorization { username, password } ) ) => {
114+ SocksVersion :: V5 {
115+ authorization : Some ( Socks5Authorization { username, password } ) ,
116+ ..
117+ } => {
71118 let socks = Socks5Stream :: connect_with_password_and_socket (
72119 stream, rpc_host, username, password,
73120 )
@@ -90,7 +137,13 @@ mod tests {
90137 let scheme = proxy. scheme ( ) ;
91138
92139 let version = parse_socks_proxy ( scheme, & proxy) ;
93- assert ! ( matches!( version, SocksVersion :: V4 ( None ) ) )
140+ assert ! ( matches!(
141+ version,
142+ SocksVersion :: V4 {
143+ local_dns: true ,
144+ identification: None
145+ }
146+ ) )
94147 }
95148
96149 #[ test]
@@ -101,7 +154,25 @@ mod tests {
101154 let version = parse_socks_proxy ( scheme, & proxy) ;
102155 assert ! ( matches!(
103156 version,
104- SocksVersion :: V4 ( Some ( Socks4Identification { user_id: "userid" } ) )
157+ SocksVersion :: V4 {
158+ local_dns: true ,
159+ identification: Some ( Socks4Identification { user_id: "userid" } )
160+ }
161+ ) )
162+ }
163+
164+ #[ test]
165+ fn parse_socks4_with_remote_dns ( ) {
166+ let proxy = Url :: parse ( "socks4a://proxy.example.com:1080" ) . unwrap ( ) ;
167+ let scheme = proxy. scheme ( ) ;
168+
169+ let version = parse_socks_proxy ( scheme, & proxy) ;
170+ assert ! ( matches!(
171+ version,
172+ SocksVersion :: V4 {
173+ local_dns: false ,
174+ identification: None
175+ }
105176 ) )
106177 }
107178
@@ -111,7 +182,13 @@ mod tests {
111182 let scheme = proxy. scheme ( ) ;
112183
113184 let version = parse_socks_proxy ( scheme, & proxy) ;
114- assert ! ( matches!( version, SocksVersion :: V5 ( None ) ) )
185+ assert ! ( matches!(
186+ version,
187+ SocksVersion :: V5 {
188+ local_dns: true ,
189+ authorization: None
190+ }
191+ ) )
115192 }
116193
117194 #[ test]
@@ -122,10 +199,28 @@ mod tests {
122199 let version = parse_socks_proxy ( scheme, & proxy) ;
123200 assert ! ( matches!(
124201 version,
125- SocksVersion :: V5 ( Some ( Socks5Authorization {
126- username: "username" ,
127- password: "password"
128- } ) )
202+ SocksVersion :: V5 {
203+ local_dns: true ,
204+ authorization: Some ( Socks5Authorization {
205+ username: "username" ,
206+ password: "password"
207+ } )
208+ }
209+ ) )
210+ }
211+
212+ #[ test]
213+ fn parse_socks5_with_remote_dns ( ) {
214+ let proxy = Url :: parse ( "socks5h://proxy.example.com:1080" ) . unwrap ( ) ;
215+ let scheme = proxy. scheme ( ) ;
216+
217+ let version = parse_socks_proxy ( scheme, & proxy) ;
218+ assert ! ( matches!(
219+ version,
220+ SocksVersion :: V5 {
221+ local_dns: false ,
222+ authorization: None
223+ }
129224 ) )
130225 }
131226}
0 commit comments