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
impl Socks5
Sourcepub async fn bind(addr: &str) -> Result<Self, SocksError>
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?
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}Sourcepub fn allow_no_auth(&mut self)
pub fn allow_no_auth(&mut self)
Enable the NO AUTH authentication method.
Examples found in repository?
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}Sourcepub fn allow_userpass<F>(&mut self, validator: F)
pub fn allow_userpass<F>(&mut self, validator: F)
Enable username/password authentication with a custom validator closure.
§Arguments
validator- A closure that receives username and password and returnstrueif valid.
Sourcepub async fn accept(&self) -> Result<(TcpStream, SocketAddr), SocksError>
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?
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}Sourcepub fn local_addr(&self) -> Result<SocketAddr, SocksError>
pub fn local_addr(&self) -> Result<SocketAddr, SocksError>
Returns the local address of the server.
Examples found in repository?
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}Sourcepub async fn read_version_message(
stream: &mut TcpStream,
) -> Result<VersionMessage, SocksError>
pub async fn read_version_message( stream: &mut TcpStream, ) -> Result<VersionMessage, SocksError>
Read a SOCKS5 version/method message from the client.
Sourcepub async fn send_method_selection(
stream: &mut TcpStream,
method: Method,
) -> Result<(), SocksError>
pub async fn send_method_selection( stream: &mut TcpStream, method: Method, ) -> Result<(), SocksError>
Send the server’s method selection message.
Sourcepub async fn read_auth_request(
stream: &mut TcpStream,
) -> Result<AuthRequest, SocksError>
pub async fn read_auth_request( stream: &mut TcpStream, ) -> Result<AuthRequest, SocksError>
Read a username/password authentication request from the client.
Sourcepub async fn send_auth_reply(
stream: &mut TcpStream,
status: AuthStatus,
) -> Result<(), SocksError>
pub async fn send_auth_reply( stream: &mut TcpStream, status: AuthStatus, ) -> Result<(), SocksError>
Send an authentication reply to the client.
Sourcepub async fn read_conn_request(
stream: &mut TcpStream,
) -> Result<ConnRequest, SocksError>
pub async fn read_conn_request( stream: &mut TcpStream, ) -> Result<ConnRequest, SocksError>
Read a SOCKS5 connection request from the client.
Examples found in repository?
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}Sourcepub async fn send_conn_reply(
stream: &mut TcpStream,
rep: Rep,
atyp: ATYP,
addr: AddrPort,
) -> Result<(), SocksError>
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?
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}Sourcepub async fn bind_udp(addr: &str) -> Result<UdpSocket, SocksError>
pub async fn bind_udp(addr: &str) -> Result<UdpSocket, SocksError>
Bind a UDP socket for UDP ASSOCIATE.
Actual UDP relay is not implemented yet.
Sourcepub async fn authenticate(
&self,
stream: &mut TcpStream,
) -> Result<(), SocksError>
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?
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}