hyper_util/client/legacy/connect/proxy/socks/v4/
mod.rs

1mod errors;
2pub use errors::*;
3
4mod messages;
5use messages::*;
6
7use std::net::{IpAddr, SocketAddr, SocketAddrV4, ToSocketAddrs};
8use std::task::{Context, Poll};
9
10use http::Uri;
11use hyper::rt::{Read, Write};
12use tower_service::Service;
13
14use bytes::BytesMut;
15
16use super::{Handshaking, SocksError};
17
18/// Tunnel Proxy via SOCKSv4
19///
20/// This is a connector that can be used by the `legacy::Client`. It wraps
21/// another connector, and after getting an underlying connection, it established
22/// a TCP tunnel over it using SOCKSv4.
23#[derive(Debug, Clone)]
24pub struct SocksV4<C> {
25    inner: C,
26    config: SocksConfig,
27}
28
29#[derive(Debug, Clone)]
30struct SocksConfig {
31    proxy: Uri,
32    local_dns: bool,
33}
34
35impl<C> SocksV4<C> {
36    /// Create a new SOCKSv4 handshake service
37    ///
38    /// Wraps an underlying connector and stores the address of a tunneling
39    /// proxying server.
40    ///
41    /// A `SocksV4` can then be called with any destination. The `dst` passed to
42    /// `call` will not be used to create the underlying connection, but will
43    /// be used in a SOCKS handshake with the proxy destination.
44    pub fn new(proxy_dst: Uri, connector: C) -> Self {
45        Self {
46            inner: connector,
47            config: SocksConfig::new(proxy_dst),
48        }
49    }
50
51    /// Resolve domain names locally on the client, rather than on the proxy server.
52    ///
53    /// Disabled by default as local resolution of domain names can be detected as a
54    /// DNS leak.
55    pub fn local_dns(mut self, local_dns: bool) -> Self {
56        self.config.local_dns = local_dns;
57        self
58    }
59}
60
61impl SocksConfig {
62    pub fn new(proxy: Uri) -> Self {
63        Self {
64            proxy,
65            local_dns: false,
66        }
67    }
68
69    async fn execute<T, E>(self, mut conn: T, host: String, port: u16) -> Result<T, SocksError<E>>
70    where
71        T: Read + Write + Unpin,
72    {
73        let address = match host.parse::<IpAddr>() {
74            Ok(IpAddr::V6(_)) => return Err(SocksV4Error::IpV6.into()),
75            Ok(IpAddr::V4(ip)) => Address::Socket(SocketAddrV4::new(ip, port)),
76            Err(_) => {
77                if self.local_dns {
78                    (host, port)
79                        .to_socket_addrs()?
80                        .find_map(|s| {
81                            if let SocketAddr::V4(v4) = s {
82                                Some(Address::Socket(v4))
83                            } else {
84                                None
85                            }
86                        })
87                        .ok_or(SocksError::DnsFailure)?
88                } else {
89                    Address::Domain(host, port)
90                }
91            }
92        };
93
94        let mut send_buf = BytesMut::with_capacity(1024);
95        let mut recv_buf = BytesMut::with_capacity(1024);
96
97        // Send Request
98        let req = Request(&address);
99        let n = req.write_to_buf(&mut send_buf)?;
100        crate::rt::write_all(&mut conn, &send_buf[..n]).await?;
101
102        // Read Response
103        let res: Response = super::read_message(&mut conn, &mut recv_buf).await?;
104        if res.0 == Status::Success {
105            Ok(conn)
106        } else {
107            Err(SocksV4Error::Command(res.0).into())
108        }
109    }
110}
111
112impl<C> Service<Uri> for SocksV4<C>
113where
114    C: Service<Uri>,
115    C::Future: Send + 'static,
116    C::Response: Read + Write + Unpin + Send + 'static,
117    C::Error: Send + 'static,
118{
119    type Response = C::Response;
120    type Error = SocksError<C::Error>;
121    type Future = Handshaking<C::Future, C::Response, C::Error>;
122
123    fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
124        self.inner.poll_ready(cx).map_err(SocksError::Inner)
125    }
126
127    fn call(&mut self, dst: Uri) -> Self::Future {
128        let config = self.config.clone();
129        let connecting = self.inner.call(config.proxy.clone());
130
131        let fut = async move {
132            let port = dst.port().map(|p| p.as_u16()).unwrap_or(443);
133            let host = dst.host().ok_or(SocksError::MissingHost)?.to_string();
134
135            let conn = connecting.await.map_err(SocksError::Inner)?;
136            config.execute(conn, host, port).await
137        };
138
139        Handshaking {
140            fut: Box::pin(fut),
141            _marker: Default::default(),
142        }
143    }
144}