socks.rs

 1//! socks proxy
 2use anyhow::{Result, anyhow};
 3use http_client::Url;
 4use tokio_socks::tcp::{Socks4Stream, Socks5Stream};
 5
 6pub(crate) async fn connect_socks_proxy_stream(
 7    proxy: Option<&Url>,
 8    rpc_host: (&str, u16),
 9) -> Result<Box<dyn AsyncReadWrite>> {
10    let stream = match parse_socks_proxy(proxy) {
11        Some((socks_proxy, SocksVersion::V4)) => {
12            let stream = Socks4Stream::connect_with_socket(
13                tokio::net::TcpStream::connect(socks_proxy).await?,
14                rpc_host,
15            )
16            .await
17            .map_err(|err| anyhow!("error connecting to socks {}", err))?;
18            Box::new(stream) as Box<dyn AsyncReadWrite>
19        }
20        Some((socks_proxy, SocksVersion::V5)) => Box::new(
21            Socks5Stream::connect_with_socket(
22                tokio::net::TcpStream::connect(socks_proxy).await?,
23                rpc_host,
24            )
25            .await
26            .map_err(|err| anyhow!("error connecting to socks {}", err))?,
27        ) as Box<dyn AsyncReadWrite>,
28        None => {
29            Box::new(tokio::net::TcpStream::connect(rpc_host).await?) as Box<dyn AsyncReadWrite>
30        }
31    };
32    Ok(stream)
33}
34
35fn parse_socks_proxy(proxy: Option<&Url>) -> Option<((String, u16), SocksVersion)> {
36    let proxy_url = proxy?;
37    let scheme = proxy_url.scheme();
38    let socks_version = if scheme.starts_with("socks4") {
39        // socks4
40        SocksVersion::V4
41    } else if scheme.starts_with("socks") {
42        // socks, socks5
43        SocksVersion::V5
44    } else {
45        return None;
46    };
47    if let Some((host, port)) = proxy_url.host().zip(proxy_url.port_or_known_default()) {
48        Some(((host.to_string(), port), socks_version))
49    } else {
50        None
51    }
52}
53
54// private helper structs and traits
55
56enum SocksVersion {
57    V4,
58    V5,
59}
60
61pub(crate) trait AsyncReadWrite:
62    tokio::io::AsyncRead + tokio::io::AsyncWrite + Unpin + Send + 'static
63{
64}
65impl<T: tokio::io::AsyncRead + tokio::io::AsyncWrite + Unpin + Send + 'static> AsyncReadWrite
66    for T
67{
68}