1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
use crate::proxy::ProxyConstructor;
use crate::clients::socks5;
use crate::general::ConnectionTimeouts;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::io::{AsyncRead, AsyncWrite};
use tokio::net::TcpStream;
use tokio::time::timeout;
use core::task::{Poll, Context};
use byteorder::{ByteOrder, BigEndian};
use std::str::FromStr;
use std::pin::Pin;
use std::fmt;
use std::io;

/// Represents the proxy constructor
/// that builds a stream when the function
/// `connect` is invoked
pub struct TcpNoAuth {
    /// Represents an address of 
    /// a service to what user
    /// wants to connect through a proxy
    destination: socks5::Destination,
    /// The port of the destination service
    port: u16,
    /// Timeouts for the connection
    timeouts: ConnectionTimeouts
}

/// An error that can occur when connecting
/// to a service through a socks5 proxy client
pub enum ErrorKind {
    /// Indicates that an operation
    /// took too much time so that
    /// timeouts has been reached
    OperationTimeoutReached,
    /// Indicates an I/O error
    IOError(std::io::Error),
    /// Indicates that a socks5-proxy
    /// server has replied with a bad buffer
    BadBuffer,
    /// May occur only if a destination
    /// is a domain name and indicates
    /// that the domain name is too long.
    /// The maximal length is 255
    DomainNameTooLong,
    /// Indicates that it is unable
    /// to establish a connection
    /// due to that fact that a socks5-proxy
    /// server is currently unavailable
    SocksServerFailure,
    /// Indicates that the connection request
    /// was denied due to the server rules
    RequestDenied,
    /// Indicates that it is unable to
    /// establish a connection due to that fact
    /// that the network is unreachable
    NetworkUnreachable,
    /// Indicates that a host is unreachable
    /// so that it is unable to establish a connection
    HostUnreachable,
    /// Indicates that a connection is refused
    /// so that it is unable to establish a connection
    ConnectionRefused,
    /// Indicates that it is unable to establish a connection
    /// due to that fact that a TTL is expired
    TTLExpired,
    /// Indicates that the command sent on the server
    /// is not currently supported, or the protocol
    /// is broken on a server side
    NotSupported,
    /// Indicates that the type of a destination
    /// address is not supported
    DestinationNotSupported
}

/// Represents an error that
/// can occur during `from_str`
/// parsing
#[derive(Debug)]
pub enum StrParsingError {
    /// Indicates that the string is not
    /// formatted appropriately for parsing
    /// process
    SyntaxError,
    /// Indicates that a destination
    /// address cannot be parsed
    InvalidDestination,
    /// Indicates that a port (u16)
    /// is invalid and it is unable to parse it
    InvalidPort,
    /// Indicates that timeouts
    /// cannot be parsed
    InvalidTimeouts
}

/// Represents the socks5-tcp 
/// proxy client stream implementation
pub struct TcpNoAuthStream {
    /// The tcp stream on which
    /// the client operates on
    wrapped_stream: TcpStream
}

impl TcpNoAuth {
    pub fn new(destination: socks5::Destination, port: u16, timeouts: ConnectionTimeouts)
        -> TcpNoAuth
    {
        TcpNoAuth { destination, port, timeouts }
    }
}

/// Impl for parsing a `Socks4General`
/// from a string
impl FromStr for TcpNoAuth {
    type Err = StrParsingError;

    /// Parses a `Socks4General` from a
    /// string in format:
    ///   (ipv4 or ipv6 or domain.com) port timeouts 
    fn from_str(s: &str) -> Result<TcpNoAuth, Self::Err> {
        // Splitting the string on spaces
        let mut s = s.split(" ");

        // Parsing an address and timeouts
        let (destination, port, timeouts) = (s.next()
                                                .ok_or(StrParsingError::SyntaxError)?
                                                .parse::<socks5::Destination>()
                                                .map_err(|_| StrParsingError::InvalidDestination)?,
                                             s.next()
                                                .ok_or(StrParsingError::SyntaxError)?
                                                .parse::<u16>()
                                                .map_err(|_| StrParsingError::InvalidPort)?,
                                             s.next()
                                                .ok_or(StrParsingError::SyntaxError)?
                                                .parse::<ConnectionTimeouts>()
                                                .map_err(|_| StrParsingError::InvalidTimeouts)?);

        Ok(TcpNoAuth::new(destination, port, timeouts))
    }
}

impl fmt::Display for ErrorKind {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        match self {
            ErrorKind::DomainNameTooLong => f.write_str("domain name is too long"),
            ErrorKind::IOError(e) 
                => f.write_str(&format!("I/O error: {}", e)),
            ErrorKind::BadBuffer => f.write_str("bad buffer has been received"),
            ErrorKind::RequestDenied => f.write_str("request denied"),
            ErrorKind::SocksServerFailure => f.write_str("SOCKS5 server is unavailable"),
            ErrorKind::NetworkUnreachable => f.write_str("network is unreachable"),
            ErrorKind::HostUnreachable => f.write_str("destination host is unreachable"),
            ErrorKind::ConnectionRefused => f.write_str("connection refused"),
            ErrorKind::TTLExpired => f.write_str("TTL is expired"),
            ErrorKind::NotSupported => f.write_str("operation is not supported by the SOCKS server"),
            ErrorKind::DestinationNotSupported => f.write_str("the type of passed destination is not supported"),
            ErrorKind::OperationTimeoutReached => f.write_str("operation timeout reached")
        }
    }
}

#[async_trait::async_trait]
impl ProxyConstructor for TcpNoAuth {
    type Stream = TcpStream;
    type ProxyStream = TcpNoAuthStream;
    type ErrorKind = ErrorKind;

    async fn connect(&mut self, mut stream: Self::Stream)
        -> Result<Self::ProxyStream, Self::ErrorKind>
    {
        // The length of the initial Socks5 request's buffer
        const BUF_LEN: usize = 3;

        // Creating the payload buffer
        let mut buf = Vec::<u8>::with_capacity(BUF_LEN);

        // The number of the Socks protocol version
        // (0x05 or just 5 in this case)
        buf.push(5);

        // The number of supported authentication methods
        // (1 in this case)
        buf.push(1);

        // The only one value of the supported
        // authentication method.
        // (0x00 or just 0 — No authentication)
        buf.push(0);

        // Writing the initial payload to the server
        let future = stream.write_all(&buf);
        let future = timeout(self.timeouts.write_timeout, future);
        let _ = future.await.map_err(|_| ErrorKind::OperationTimeoutReached)?
                            .map_err(|e| ErrorKind::IOError(e))?;

        // Reading a reply from the server
        let future = stream.read(&mut buf);
        let future = timeout(self.timeouts.read_timeout, future);
        let read_bytes = future.await.map_err(|_| ErrorKind::OperationTimeoutReached)?
                                      .map_err(|e| ErrorKind::IOError(e))?;

        // The server must send reply
        // with the length of 2 bytes.
        // Anything else is a sense of an error
        if read_bytes != 2 {
            return Err(ErrorKind::BadBuffer)
        }

        // The former read byte must be 0x05,
        // while the latter must not be 0xFF
        if buf[0] != 0x05 || buf[1] == 0xFF {
            return Err(ErrorKind::BadBuffer)
        }

        // Computing the length of a Socks5 request
        // The buffer length is computed this way:
        //  (+1) for the number of the version of the socks protocol (4 in this case)
        //  (+1) for the command number (1 or 2)
        //  (+1) for the reserved byte, must be 0x00
        //  (+1) for the destination address type,
        //       must be 0x01, 0x03 or 0x04,
        //       where 0x01 stands for an IPv4 address,
        //       0x03 stands for a domain name and
        //       0x04 stands for an IPv6 address
        //  [+4]* if the type of the address is IPv4,
        //  [+n]* if the type of the address is domain
        //  [+16]* if the type of the address is IPv6 
        //  (+2) for port (in the network byte order)
        let dest_buf_len = self.destination.len_as_buffer();
        let buf_len = 1 + 1 + 1 + dest_buf_len + 2;

        // Reallocating the payload buffer
        buf.resize(buf_len, 0);

        // Setting the version of the socks protocol
        // being used in the payload buffer
        buf[0] = 5;

        // Setting the tcp connection establishment command
        buf[1] = socks5::Command::TcpConnectionEstablishment as u8;

        // Setting a 0x00 byte as it is
        // rule of the socks5 protocol
        // buf[2] = 0;

        // Filling the buffer with the destiation
        self.destination.extend_buffer(&mut buf[3..]).unwrap();

        // Writing port as a big endian short
        BigEndian::write_u16(&mut buf[3 + dest_buf_len .. 3 + dest_buf_len + 2], self.port);

        // println!("{:?}", buf);

        // Sending our generated payload
        // to the Socks5 server
        let future = stream.write_all(&buf);
        let future = timeout(self.timeouts.write_timeout, future);
        let _ = future.await.map_err(|_| ErrorKind::OperationTimeoutReached)?
                            .map_err(|e| ErrorKind::IOError(e))?;

        // Reading a reply from the server
        let future = stream.read(&mut buf);
        let future = timeout(self.timeouts.read_timeout, future);
        let read_bytes = future.await.map_err(|_| ErrorKind::OperationTimeoutReached)?
                                     .map_err(|e| ErrorKind::IOError(e))?;

        // We should receive exatly `buf_len` bytes from the server,
        // unless there is something wrong with the
        // received reply
        if read_bytes < 2 {
            return Err(ErrorKind::BadBuffer)
        }

        // Analyzing the received reply
        // and returning a socks4 general proxy client
        // instance if everything was successful
        match buf[1] {
            // Means that request accepted
            0x00 => Ok(TcpNoAuthStream { wrapped_stream: stream }),
            0x01 => Err(ErrorKind::SocksServerFailure),
            0x02 => Err(ErrorKind::RequestDenied),
            0x03 => Err(ErrorKind::NetworkUnreachable),
            0x04 => Err(ErrorKind::HostUnreachable),
            0x05 => Err(ErrorKind::ConnectionRefused),
            0x06 => Err(ErrorKind::TTLExpired),
            0x07 => Err(ErrorKind::NotSupported),
            0x08 => Err(ErrorKind::DestinationNotSupported),
            _ => Err(ErrorKind::BadBuffer)
        }
    }
}

impl AsyncRead for TcpNoAuthStream {
    fn poll_read(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8])
        -> Poll<io::Result<usize>>
    {
        let pinned = &mut Pin::into_inner(self).wrapped_stream;
        Pin::new(pinned).poll_read(cx, buf)
    }
}

impl AsyncWrite for TcpNoAuthStream {
    fn poll_write(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8])
        -> Poll<Result<usize, io::Error>>
    { 
        let stream = &mut Pin::into_inner(self).wrapped_stream;
        Pin::new(stream).poll_write(cx, buf)
    }

    fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>)
        -> Poll<Result<(), io::Error>>
    {
        let stream = &mut Pin::into_inner(self).wrapped_stream;
        Pin::new(stream).poll_flush(cx)
    }

    fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>)
        -> Poll<Result<(), io::Error>>
    {
        let stream = &mut Pin::into_inner(self).wrapped_stream;
        Pin::new(stream).poll_shutdown(cx)
    }
}