async_proxy/clients/socks4/
general.rs

1use crate::general::ConnectionTimeouts;
2use crate::proxy::ProxyConstructor;
3use crate::clients::socks4::{ErrorKind, Command};
4use byteorder::{ByteOrder, BigEndian};
5use tokio::net::TcpStream;
6use tokio::io::{AsyncRead, AsyncWrite};
7use tokio::io::{AsyncReadExt, AsyncWriteExt};
8use tokio::time::timeout;
9use std::pin::Pin;
10use core::task::{Poll, Context};
11use std::net::SocketAddrV4;
12use std::str::FromStr;
13use std::borrow::Cow;
14use std::io;
15
16/// Represents the proxy constructor
17/// that creates a `S4GeneralStream`
18/// proxy stream when connected
19pub struct Socks4General {
20    /// the IPv4 address of a service
21    /// we are connecting through proxy
22    dest_addr: SocketAddrV4,
23    /// An ident (see Socks4 protocol wiki
24    ///  for more information)
25    ident: Cow<'static, str>,
26    /// The timeout set
27    timeouts: ConnectionTimeouts
28}
29
30/// Represents an error that
31/// can occur during `from_str`
32/// parsing
33#[derive(Debug)]
34pub enum StrParsingError {
35    /// Indicates that the string is not
36    /// formatted appropriately for parsing
37    /// process
38    SyntaxError,
39    /// Indicates that a destination
40    /// address cannot be parsed
41    InvalidAddr,
42    /// Indicates that timeouts
43    /// cannot be parsed
44    InvalidTimeouts,
45}
46
47/// The actual type that represents
48/// the Socks4 proxy client stream.
49/// Contains a tcp stream that operates on
50pub struct S4GeneralStream {
51    /// The tcp stream on which
52    /// the client operates on
53    wrapped_stream: TcpStream
54}
55
56impl Socks4General {
57    pub fn new(dest_addr: SocketAddrV4, ident: Cow<'static, str>,
58    timeouts: ConnectionTimeouts)
59    -> Socks4General
60    {
61        Socks4General { dest_addr, ident, timeouts }
62    }
63}
64
65/// Impl for parsing a `Socks4General`
66/// from a string
67impl FromStr for Socks4General {
68    type Err = StrParsingError;
69
70    /// Parses a `Socks4General` from a
71    /// string in format:
72    ///   ipv4:port ident timeouts
73    fn from_str(s: &str) -> Result<Socks4General, Self::Err> {
74        // Splitting the string on spaces
75        let mut s = s.split(" ");
76
77        // Parsing an address and timeouts
78        let (address, ident, timeouts) = (s.next()
79                                           .ok_or(StrParsingError::SyntaxError)?
80                                           .parse::<SocketAddrV4>()
81                                           .map_err(|_| StrParsingError::InvalidAddr)?,
82                                          s.next()
83                                           .ok_or(StrParsingError::SyntaxError)?,
84                                          s.next()
85                                           .ok_or(StrParsingError::SyntaxError)?
86                                           .parse::<ConnectionTimeouts>()
87                                           .map_err(|_| StrParsingError::InvalidTimeouts)?);
88
89        Ok(Socks4General::new(address, Cow::Owned(ident.to_owned()), timeouts))
90    }
91}
92
93#[async_trait::async_trait]
94impl ProxyConstructor for Socks4General {
95    type ProxyStream = S4GeneralStream;
96    type Stream = TcpStream;
97    type ErrorKind = ErrorKind;
98
99    async fn connect(&mut self, mut stream: Self::Stream)
100        -> Result<Self::ProxyStream, Self::ErrorKind>
101    {
102        // Computing the Socks4 buffer length.
103        // The buffer length is computed this way:
104        //  (+1) for the number of the version of the socks protocol (4 in this case)
105        //  (+1) for the command number (1 or 2)
106        //  (+2) for port (in the network byte order)
107        //  (+4) for the IPv4 address
108        //  (+n) where `n` is the length of the given ident
109        //  (+1) for the NULL-termination byte (0x00)
110        let buf_len = 1 + 1 + 2 + 4 + self.ident.len() + 1;
111        // Creating the payload buffer
112        let mut buf = Vec::with_capacity(buf_len);
113
114        // Pushing the version of the socks protocol
115        // being used in the payload buffer
116        buf.push(4);
117
118        // Pusing the tcp connection establishment command
119        buf.push(Command::TcpConnectionEstablishment as u8);
120        
121        // Filling the port buffer with zeroes
122        // due to that fact that it is permitted
123        // to access an initialized memory
124        buf.push(0);
125        buf.push(0);
126
127        // Writing the port to the buffer
128        BigEndian::write_u16(&mut buf[2..4], self.dest_addr.port());
129
130        // Filling the IPv4 buffer with zeroes
131        // due to that fact that it is permitted
132        // to access an initialized memory
133        buf.push(0);
134        buf.push(0);
135        buf.push(0);
136        buf.push(0);
137
138        // Writing the IPv4 in the buffer
139        BigEndian::write_u32(&mut buf[4..8], (*self.dest_addr.ip()).into());
140
141        // And, finally, pushing the
142        // NULL-termination (0x00) byte
143        buf.push(0);
144
145        // Sending our generated payload
146        // to the Socks4 server
147        let future = stream.write_all(&buf);
148        let future = timeout(self.timeouts.write_timeout, future);
149        let _ = future.await.map_err(|_| ErrorKind::OperationTimeoutReached)?
150                            .map_err(|e| ErrorKind::IOError(e))?;
151
152        // Reading a reply from the server
153        let future = stream.read(&mut buf);
154        let future = timeout(self.timeouts.read_timeout, future);
155        let read_bytes = future.await.map_err(|_| ErrorKind::OperationTimeoutReached)?
156                                     .map_err(|e| ErrorKind::IOError(e))?;
157
158        // We should receive exatly 8 bytes from the server,
159        // unless there is something wrong with the
160        // received reply
161        if read_bytes != 8 {
162            return Err(ErrorKind::BadBuffer)
163        }
164
165        // Analyzing the received reply
166        // and returning a socks4 general proxy client
167        // instance if everything was successful
168        match buf[1] {
169            // Means that request accepted
170            0x5a => Ok(S4GeneralStream { wrapped_stream: stream }),
171            // Means that our request was denied
172            0x5b => Err(ErrorKind::RequestDenied),
173            // Means that ident is currently unavailable
174            0x5c => Err(ErrorKind::IdentIsUnavailable),
175            // Means that the user passed a wrong ident string
176            0x5d => Err(ErrorKind::BadIdent),
177            // Does not match anything, means that
178            // we got a bad buffer
179            _ => Err(ErrorKind::BadBuffer)
180        }
181    }
182}
183
184impl AsyncRead for S4GeneralStream {
185    fn poll_read(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8])
186        -> Poll<io::Result<usize>>
187    {
188        let pinned = &mut Pin::into_inner(self).wrapped_stream;
189        Pin::new(pinned).poll_read(cx, buf)
190    }
191}
192
193impl AsyncWrite for S4GeneralStream {
194    fn poll_write(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8])
195        -> Poll<Result<usize, io::Error>>
196    { 
197        let stream = &mut Pin::into_inner(self).wrapped_stream;
198        Pin::new(stream).poll_write(cx, buf)
199    }
200
201    fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>)
202        -> Poll<Result<(), io::Error>>
203    {
204        let stream = &mut Pin::into_inner(self).wrapped_stream;
205        Pin::new(stream).poll_flush(cx)
206    }
207
208    fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>)
209        -> Poll<Result<(), io::Error>>
210    {
211        let stream = &mut Pin::into_inner(self).wrapped_stream;
212        Pin::new(stream).poll_shutdown(cx)
213    }
214}
215
216impl Into<TcpStream> for S4GeneralStream {
217    fn into(self) -> TcpStream {
218        self.wrapped_stream
219    }
220}