socks5_impl/server/connection/mod.rs
1use self::{associate::UdpAssociate, bind::Bind, connect::Connect};
2use crate::{
3 protocol::{self, Address, AsyncStreamOperation, AuthMethod, Command, handshake},
4 server::AuthAdaptor,
5};
6use std::{net::SocketAddr, time::Duration};
7use tokio::{io::AsyncWriteExt, net::TcpStream};
8
9pub mod associate;
10pub mod bind;
11pub mod connect;
12
13/// An incoming connection. This may not be a valid socks5 connection. You need to call [`authenticate()`](#method.authenticate)
14/// to perform the socks5 handshake. It will be converted to a proper socks5 connection after the handshake succeeds.
15pub struct IncomingConnection<O> {
16 stream: TcpStream,
17 auth: AuthAdaptor<O>,
18}
19
20impl<O: 'static> IncomingConnection<O> {
21 #[inline]
22 pub(crate) fn new(stream: TcpStream, auth: AuthAdaptor<O>) -> Self {
23 IncomingConnection { stream, auth }
24 }
25
26 /// Returns the local address that this stream is bound to.
27 #[inline]
28 pub fn local_addr(&self) -> std::io::Result<SocketAddr> {
29 self.stream.local_addr()
30 }
31
32 /// Returns the remote address that this stream is connected to.
33 #[inline]
34 pub fn peer_addr(&self) -> std::io::Result<SocketAddr> {
35 self.stream.peer_addr()
36 }
37
38 /// Shutdown the TCP stream.
39 #[inline]
40 pub async fn shutdown(&mut self) -> std::io::Result<()> {
41 self.stream.shutdown().await
42 }
43
44 /// Gets the value of the `TCP_NODELAY` option on this socket.
45 ///
46 /// For more information about this option, see
47 /// [`set_nodelay`](#method.set_nodelay).
48 #[inline]
49 pub fn nodelay(&self) -> std::io::Result<bool> {
50 self.stream.nodelay()
51 }
52
53 /// Sets the value of the `TCP_NODELAY` option on this socket.
54 ///
55 /// If set, this option disables the Nagle algorithm. This means that segments are always sent as soon as possible,
56 /// even if there is only a small amount of data. When not set, data is buffered until there is a sufficient amount
57 /// to send out, thereby avoiding the frequent sending of small packets.
58 pub fn set_nodelay(&self, nodelay: bool) -> std::io::Result<()> {
59 self.stream.set_nodelay(nodelay)
60 }
61
62 /// Gets the value of the `IP_TTL` option for this socket.
63 ///
64 /// For more information about this option, see
65 /// [`set_ttl`](#method.set_ttl).
66 pub fn ttl(&self) -> std::io::Result<u32> {
67 self.stream.ttl()
68 }
69
70 /// Sets the value for the `IP_TTL` option on this socket.
71 ///
72 /// This value sets the time-to-live field that is used in every packet sent from this socket.
73 pub fn set_ttl(&self, ttl: u32) -> std::io::Result<()> {
74 self.stream.set_ttl(ttl)
75 }
76
77 /// Set a timeout for the SOCKS5 handshake.
78 pub async fn authenticate_with_timeout(self, timeout: Duration) -> crate::Result<(Authenticated, O)> {
79 tokio::time::timeout(timeout, self.authenticate())
80 .await
81 .map_err(|_| crate::Error::String("handshake timeout".into()))?
82 }
83
84 /// Perform a SOCKS5 authentication handshake using the given
85 /// [`AuthExecutor`](crate::server::auth::AuthExecutor) adapter.
86 ///
87 /// If the handshake succeeds, an [`Authenticated`]
88 /// alongs with the output of the [`AuthExecutor`](crate::server::auth::AuthExecutor) adapter is returned.
89 /// Otherwise, the error and the original [`TcpStream`](https://docs.rs/tokio/latest/tokio/net/struct.TcpStream.html) is returned.
90 ///
91 /// Note that this method will not implicitly close the connection even if the handshake failed.
92 pub async fn authenticate(mut self) -> crate::Result<(Authenticated, O)> {
93 let request = handshake::Request::retrieve_from_async_stream(&mut self.stream).await?;
94 if let Some(method) = self.evaluate_request(&request) {
95 let response = handshake::Response::new(method);
96 response.write_to_async_stream(&mut self.stream).await?;
97 let output = self.auth.execute(&mut self.stream).await;
98 Ok((Authenticated::new(self.stream), output))
99 } else {
100 let response = handshake::Response::new(AuthMethod::NoAcceptableMethods);
101 response.write_to_async_stream(&mut self.stream).await?;
102 let err = "No available handshake method provided by client";
103 Err(crate::Error::Io(std::io::Error::new(std::io::ErrorKind::Unsupported, err)))
104 }
105 }
106
107 fn evaluate_request(&self, req: &handshake::Request) -> Option<AuthMethod> {
108 let method = self.auth.auth_method();
109 if req.evaluate_method(method) { Some(method) } else { None }
110 }
111}
112
113impl<O> std::fmt::Debug for IncomingConnection<O> {
114 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
115 f.debug_struct("IncomingConnection").field("stream", &self.stream).finish()
116 }
117}
118
119impl<O> From<IncomingConnection<O>> for TcpStream {
120 #[inline]
121 fn from(conn: IncomingConnection<O>) -> Self {
122 conn.stream
123 }
124}
125
126/// A TCP stream that has been authenticated.
127///
128/// To get the command from the SOCKS5 client, use
129/// [`wait_request`](crate::server::connection::Authenticated::wait_request).
130///
131/// It can also be converted back into a raw [`tokio::TcpStream`](https://docs.rs/tokio/latest/tokio/net/struct.TcpStream.html) with `From` trait.
132pub struct Authenticated(TcpStream);
133
134impl Authenticated {
135 #[inline]
136 fn new(stream: TcpStream) -> Self {
137 Self(stream)
138 }
139
140 /// Waits the SOCKS5 client to send a request.
141 ///
142 /// This method will return a [`Command`] if the client sends a valid command.
143 ///
144 /// When encountering an error, the stream will be returned alongside the error.
145 ///
146 /// Note that this method will not implicitly close the connection even if the client sends an invalid request.
147 pub async fn wait_request(mut self) -> crate::Result<ClientConnection> {
148 let req = protocol::Request::retrieve_from_async_stream(&mut self.0).await?;
149
150 match req.command {
151 Command::UdpAssociate => Ok(ClientConnection::UdpAssociate(
152 UdpAssociate::<associate::NeedReply>::new(self.0),
153 req.address,
154 )),
155 Command::Bind => Ok(ClientConnection::Bind(Bind::<bind::NeedFirstReply>::new(self.0), req.address)),
156 Command::Connect => Ok(ClientConnection::Connect(Connect::<connect::NeedReply>::new(self.0), req.address)),
157 }
158 }
159
160 /// Causes the other peer to receive a read of length 0, indicating that no more data will be sent. This only closes the stream in one direction.
161 #[inline]
162 pub async fn shutdown(&mut self) -> std::io::Result<()> {
163 self.0.shutdown().await
164 }
165
166 /// Returns the local address that this stream is bound to.
167 #[inline]
168 pub fn local_addr(&self) -> std::io::Result<SocketAddr> {
169 self.0.local_addr()
170 }
171
172 /// Returns the remote address that this stream is connected to.
173 #[inline]
174 pub fn peer_addr(&self) -> std::io::Result<SocketAddr> {
175 self.0.peer_addr()
176 }
177
178 /// Gets the value of the `TCP_NODELAY` option on this socket.
179 ///
180 /// For more information about this option, see
181 /// [`set_nodelay`](crate::server::connection::Authenticated::set_nodelay).
182 #[inline]
183 pub fn nodelay(&self) -> std::io::Result<bool> {
184 self.0.nodelay()
185 }
186
187 /// Sets the value of the `TCP_NODELAY` option on this socket.
188 ///
189 /// If set, this option disables the Nagle algorithm. This means that segments are always sent as soon as possible,
190 /// even if there is only a small amount of data. When not set, data is buffered until there is a sufficient amount to send out,
191 /// thereby avoiding the frequent sending of small packets.
192 pub fn set_nodelay(&self, nodelay: bool) -> std::io::Result<()> {
193 self.0.set_nodelay(nodelay)
194 }
195
196 /// Gets the value of the `IP_TTL` option for this socket.
197 ///
198 /// For more information about this option, see
199 /// [`set_ttl`](crate::server::connection::Authenticated::set_ttl).
200 pub fn ttl(&self) -> std::io::Result<u32> {
201 self.0.ttl()
202 }
203
204 /// Sets the value for the `IP_TTL` option on this socket.
205 ///
206 /// This value sets the time-to-live field that is used in every packet sent from this socket.
207 pub fn set_ttl(&self, ttl: u32) -> std::io::Result<()> {
208 self.0.set_ttl(ttl)
209 }
210}
211
212impl From<Authenticated> for TcpStream {
213 #[inline]
214 fn from(conn: Authenticated) -> Self {
215 conn.0
216 }
217}
218
219/// After the socks5 handshake succeeds, the connection may become:
220///
221/// - Associate
222/// - Bind
223/// - Connect
224#[derive(Debug)]
225pub enum ClientConnection {
226 UdpAssociate(UdpAssociate<associate::NeedReply>, Address),
227 Bind(Bind<bind::NeedFirstReply>, Address),
228 Connect(Connect<connect::NeedReply>, Address),
229}