Socks5

Struct Socks5 

Source
pub struct Socks5 { /* private fields */ }
Expand description

The main SOCKS5 server struct.

Handles incoming TCP connections, negotiates authentication, and manages SOCKS5 commands (CONNECT, BIND, UDP ASSOCIATE).

⚠️ UDP ASSOCIATE is partially implemented. The server currently only supports binding a UDP socket and sending the reply to the client. Actual UDP packet forwarding is not implemented yet.

Implementations§

Source§

impl Socks5

Source

pub async fn bind(addr: &str) -> Result<Self, SocksError>

Bind a new SOCKS5 server to an address.

§Arguments
  • addr - The address to bind to, e.g., "127.0.0.1:1080".
§Errors

Returns a SocksError::Io if binding fails.

Examples found in repository?
examples/simple_server.rs (line 19)
11async fn main() -> Result<(), SocksError> {
12    tracing_subscriber::fmt()
13        .with_target(false)
14        .with_level(true)
15        .compact()
16        .init();
17
18    // Both IPv4 and IPv6 work
19    let mut server = Socks5::bind("127.0.0.1:1080").await?;
20    server.allow_no_auth();
21
22    // Example with a username and password if you need authentication
23    // server.allow_userpass(|u, p| u == "admin" && p == "admin");
24
25    let server = Arc::new(server);
26
27    info!("SOCKS5 proxy listening on {}", server.local_addr()?);
28
29    loop {
30        let (client, addr) = server.accept().await?;
31        let server_ref = Arc::clone(&server);
32
33        tokio::spawn(async move {
34            if let Err(e) = handle_client(server_ref, client, addr).await {
35                error!("Client {addr} error: {e}");
36            }
37        });
38    }
39}
Source

pub fn allow_no_auth(&mut self)

Enable the NO AUTH authentication method.

Examples found in repository?
examples/simple_server.rs (line 20)
11async fn main() -> Result<(), SocksError> {
12    tracing_subscriber::fmt()
13        .with_target(false)
14        .with_level(true)
15        .compact()
16        .init();
17
18    // Both IPv4 and IPv6 work
19    let mut server = Socks5::bind("127.0.0.1:1080").await?;
20    server.allow_no_auth();
21
22    // Example with a username and password if you need authentication
23    // server.allow_userpass(|u, p| u == "admin" && p == "admin");
24
25    let server = Arc::new(server);
26
27    info!("SOCKS5 proxy listening on {}", server.local_addr()?);
28
29    loop {
30        let (client, addr) = server.accept().await?;
31        let server_ref = Arc::clone(&server);
32
33        tokio::spawn(async move {
34            if let Err(e) = handle_client(server_ref, client, addr).await {
35                error!("Client {addr} error: {e}");
36            }
37        });
38    }
39}
Source

pub fn allow_userpass<F>(&mut self, validator: F)
where F: Fn(&str, &str) -> bool + Send + Sync + 'static,

Enable username/password authentication with a custom validator closure.

§Arguments
  • validator - A closure that receives username and password and returns true if valid.
Source

pub async fn accept(&self) -> Result<(TcpStream, SocketAddr), SocksError>

Accept a client TCP connection.

§Returns

A tuple of (TcpStream, SocketAddr) representing the connected client.

Examples found in repository?
examples/simple_server.rs (line 30)
11async fn main() -> Result<(), SocksError> {
12    tracing_subscriber::fmt()
13        .with_target(false)
14        .with_level(true)
15        .compact()
16        .init();
17
18    // Both IPv4 and IPv6 work
19    let mut server = Socks5::bind("127.0.0.1:1080").await?;
20    server.allow_no_auth();
21
22    // Example with a username and password if you need authentication
23    // server.allow_userpass(|u, p| u == "admin" && p == "admin");
24
25    let server = Arc::new(server);
26
27    info!("SOCKS5 proxy listening on {}", server.local_addr()?);
28
29    loop {
30        let (client, addr) = server.accept().await?;
31        let server_ref = Arc::clone(&server);
32
33        tokio::spawn(async move {
34            if let Err(e) = handle_client(server_ref, client, addr).await {
35                error!("Client {addr} error: {e}");
36            }
37        });
38    }
39}
Source

pub fn local_addr(&self) -> Result<SocketAddr, SocksError>

Returns the local address of the server.

Examples found in repository?
examples/simple_server.rs (line 27)
11async fn main() -> Result<(), SocksError> {
12    tracing_subscriber::fmt()
13        .with_target(false)
14        .with_level(true)
15        .compact()
16        .init();
17
18    // Both IPv4 and IPv6 work
19    let mut server = Socks5::bind("127.0.0.1:1080").await?;
20    server.allow_no_auth();
21
22    // Example with a username and password if you need authentication
23    // server.allow_userpass(|u, p| u == "admin" && p == "admin");
24
25    let server = Arc::new(server);
26
27    info!("SOCKS5 proxy listening on {}", server.local_addr()?);
28
29    loop {
30        let (client, addr) = server.accept().await?;
31        let server_ref = Arc::clone(&server);
32
33        tokio::spawn(async move {
34            if let Err(e) = handle_client(server_ref, client, addr).await {
35                error!("Client {addr} error: {e}");
36            }
37        });
38    }
39}
Source

pub async fn read_version_message( stream: &mut TcpStream, ) -> Result<VersionMessage, SocksError>

Read a SOCKS5 version/method message from the client.

Source

pub async fn send_method_selection( stream: &mut TcpStream, method: Method, ) -> Result<(), SocksError>

Send the server’s method selection message.

Source

pub async fn read_auth_request( stream: &mut TcpStream, ) -> Result<AuthRequest, SocksError>

Read a username/password authentication request from the client.

Source

pub async fn send_auth_reply( stream: &mut TcpStream, status: AuthStatus, ) -> Result<(), SocksError>

Send an authentication reply to the client.

Source

pub async fn read_conn_request( stream: &mut TcpStream, ) -> Result<ConnRequest, SocksError>

Read a SOCKS5 connection request from the client.

Examples found in repository?
examples/simple_server.rs (line 55)
41async fn handle_client(
42    server: Arc<Socks5>,
43    mut stream: TcpStream,
44    addr: std::net::SocketAddr,
45) -> Result<(), SocksError> {
46    info!("New client connected from {addr}");
47
48    if let Err(e) = server.authenticate(&mut stream).await {
49        warn!("Authentication failed for {addr}: {e}");
50        let _ = stream.shutdown().await;
51        return Ok(());
52    }
53    info!("Authentication succeeded for {addr}");
54
55    let req = match Socks5::read_conn_request(&mut stream).await {
56        Ok(r) => {
57            info!(client=%addr, "Connection request");
58            info!(request=%r, "Request format");
59            r
60        }
61        Err(e) => {
62            error!("Failed to read connection request from {addr}: {e}");
63            let _ = stream.shutdown().await;
64            return Ok(());
65        }
66    };
67
68    match req.cmd {
69        CMD::Connect => {
70            info!(client=%addr, dest=%req.dst, "Connecting to destination");
71
72            let mut target = match req.dst {
73                AddrPort::V4(ip, port) => TcpStream::connect((ip, port)).await?,
74                AddrPort::V6(ip, port) => TcpStream::connect((ip, port)).await?,
75                AddrPort::Domain(ref host, port) => {
76                    TcpStream::connect((host.as_str(), port)).await?
77                }
78            };
79
80            let local_addr = target.local_addr()?;
81            let bnd = match local_addr.ip() {
82                IpAddr::V4(ip) => AddrPort::V4(ip, local_addr.port()),
83                IpAddr::V6(ip) => AddrPort::V6(ip, local_addr.port()),
84            };
85
86            let atyp = match bnd {
87                AddrPort::V4(_, _) => ATYP::V4,
88                AddrPort::V6(_, _) => ATYP::V6,
89                _ => ATYP::DomainName,
90            };
91
92            info!(client=%addr, bind=%bnd, atyp=%atyp, "Connection established");
93
94            Socks5::send_conn_reply(&mut stream, Rep::Succeeded, atyp, bnd).await?;
95
96            if let Err(e) = io::copy_bidirectional(&mut stream, &mut target).await {
97                warn!("TCP connection with {addr} closed with error: {e}");
98            } else {
99                info!("TCP connection with {addr} closed gracefully");
100            }
101        }
102
103        _ => {
104            warn!("Unsupported command from {addr}: {}", req.cmd);
105            Socks5::send_conn_reply(
106                &mut stream,
107                Rep::CommandNotSupported,
108                ATYP::V4,
109                AddrPort::V4(Ipv4Addr::UNSPECIFIED, 0),
110            )
111            .await?;
112        }
113    }
114
115    Ok(())
116}
Source

pub async fn send_conn_reply( stream: &mut TcpStream, rep: Rep, atyp: ATYP, addr: AddrPort, ) -> Result<(), SocksError>

Send a connection reply to the client.

Examples found in repository?
examples/simple_server.rs (line 94)
41async fn handle_client(
42    server: Arc<Socks5>,
43    mut stream: TcpStream,
44    addr: std::net::SocketAddr,
45) -> Result<(), SocksError> {
46    info!("New client connected from {addr}");
47
48    if let Err(e) = server.authenticate(&mut stream).await {
49        warn!("Authentication failed for {addr}: {e}");
50        let _ = stream.shutdown().await;
51        return Ok(());
52    }
53    info!("Authentication succeeded for {addr}");
54
55    let req = match Socks5::read_conn_request(&mut stream).await {
56        Ok(r) => {
57            info!(client=%addr, "Connection request");
58            info!(request=%r, "Request format");
59            r
60        }
61        Err(e) => {
62            error!("Failed to read connection request from {addr}: {e}");
63            let _ = stream.shutdown().await;
64            return Ok(());
65        }
66    };
67
68    match req.cmd {
69        CMD::Connect => {
70            info!(client=%addr, dest=%req.dst, "Connecting to destination");
71
72            let mut target = match req.dst {
73                AddrPort::V4(ip, port) => TcpStream::connect((ip, port)).await?,
74                AddrPort::V6(ip, port) => TcpStream::connect((ip, port)).await?,
75                AddrPort::Domain(ref host, port) => {
76                    TcpStream::connect((host.as_str(), port)).await?
77                }
78            };
79
80            let local_addr = target.local_addr()?;
81            let bnd = match local_addr.ip() {
82                IpAddr::V4(ip) => AddrPort::V4(ip, local_addr.port()),
83                IpAddr::V6(ip) => AddrPort::V6(ip, local_addr.port()),
84            };
85
86            let atyp = match bnd {
87                AddrPort::V4(_, _) => ATYP::V4,
88                AddrPort::V6(_, _) => ATYP::V6,
89                _ => ATYP::DomainName,
90            };
91
92            info!(client=%addr, bind=%bnd, atyp=%atyp, "Connection established");
93
94            Socks5::send_conn_reply(&mut stream, Rep::Succeeded, atyp, bnd).await?;
95
96            if let Err(e) = io::copy_bidirectional(&mut stream, &mut target).await {
97                warn!("TCP connection with {addr} closed with error: {e}");
98            } else {
99                info!("TCP connection with {addr} closed gracefully");
100            }
101        }
102
103        _ => {
104            warn!("Unsupported command from {addr}: {}", req.cmd);
105            Socks5::send_conn_reply(
106                &mut stream,
107                Rep::CommandNotSupported,
108                ATYP::V4,
109                AddrPort::V4(Ipv4Addr::UNSPECIFIED, 0),
110            )
111            .await?;
112        }
113    }
114
115    Ok(())
116}
Source

pub async fn bind_udp(addr: &str) -> Result<UdpSocket, SocksError>

Bind a UDP socket for UDP ASSOCIATE.

Actual UDP relay is not implemented yet.

Source

pub async fn authenticate( &self, stream: &mut TcpStream, ) -> Result<(), SocksError>

Perform authentication according to the configured methods.

Negotiates between NO AUTH and USERNAME/PASSWORD methods if enabled.

Examples found in repository?
examples/simple_server.rs (line 48)
41async fn handle_client(
42    server: Arc<Socks5>,
43    mut stream: TcpStream,
44    addr: std::net::SocketAddr,
45) -> Result<(), SocksError> {
46    info!("New client connected from {addr}");
47
48    if let Err(e) = server.authenticate(&mut stream).await {
49        warn!("Authentication failed for {addr}: {e}");
50        let _ = stream.shutdown().await;
51        return Ok(());
52    }
53    info!("Authentication succeeded for {addr}");
54
55    let req = match Socks5::read_conn_request(&mut stream).await {
56        Ok(r) => {
57            info!(client=%addr, "Connection request");
58            info!(request=%r, "Request format");
59            r
60        }
61        Err(e) => {
62            error!("Failed to read connection request from {addr}: {e}");
63            let _ = stream.shutdown().await;
64            return Ok(());
65        }
66    };
67
68    match req.cmd {
69        CMD::Connect => {
70            info!(client=%addr, dest=%req.dst, "Connecting to destination");
71
72            let mut target = match req.dst {
73                AddrPort::V4(ip, port) => TcpStream::connect((ip, port)).await?,
74                AddrPort::V6(ip, port) => TcpStream::connect((ip, port)).await?,
75                AddrPort::Domain(ref host, port) => {
76                    TcpStream::connect((host.as_str(), port)).await?
77                }
78            };
79
80            let local_addr = target.local_addr()?;
81            let bnd = match local_addr.ip() {
82                IpAddr::V4(ip) => AddrPort::V4(ip, local_addr.port()),
83                IpAddr::V6(ip) => AddrPort::V6(ip, local_addr.port()),
84            };
85
86            let atyp = match bnd {
87                AddrPort::V4(_, _) => ATYP::V4,
88                AddrPort::V6(_, _) => ATYP::V6,
89                _ => ATYP::DomainName,
90            };
91
92            info!(client=%addr, bind=%bnd, atyp=%atyp, "Connection established");
93
94            Socks5::send_conn_reply(&mut stream, Rep::Succeeded, atyp, bnd).await?;
95
96            if let Err(e) = io::copy_bidirectional(&mut stream, &mut target).await {
97                warn!("TCP connection with {addr} closed with error: {e}");
98            } else {
99                info!("TCP connection with {addr} closed gracefully");
100            }
101        }
102
103        _ => {
104            warn!("Unsupported command from {addr}: {}", req.cmd);
105            Socks5::send_conn_reply(
106                &mut stream,
107                Rep::CommandNotSupported,
108                ATYP::V4,
109                AddrPort::V4(Ipv4Addr::UNSPECIFIED, 0),
110            )
111            .await?;
112        }
113    }
114
115    Ok(())
116}

Auto Trait Implementations§

§

impl !Freeze for Socks5

§

impl !RefUnwindSafe for Socks5

§

impl Send for Socks5

§

impl Sync for Socks5

§

impl Unpin for Socks5

§

impl !UnwindSafe for Socks5

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.