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");
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");
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");
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.