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