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;
pub struct TcpNoAuth {
destination: socks5::Destination,
port: u16,
timeouts: ConnectionTimeouts
}
pub enum ErrorKind {
OperationTimeoutReached,
IOError(std::io::Error),
BadBuffer,
DomainNameTooLong,
SocksServerFailure,
RequestDenied,
NetworkUnreachable,
HostUnreachable,
ConnectionRefused,
TTLExpired,
NotSupported,
DestinationNotSupported
}
#[derive(Debug)]
pub enum StrParsingError {
SyntaxError,
InvalidDestination,
InvalidPort,
InvalidTimeouts
}
pub struct TcpNoAuthStream {
wrapped_stream: TcpStream
}
impl TcpNoAuth {
pub fn new(destination: socks5::Destination, port: u16, timeouts: ConnectionTimeouts)
-> TcpNoAuth
{
TcpNoAuth { destination, port, timeouts }
}
}
impl FromStr for TcpNoAuth {
type Err = StrParsingError;
fn from_str(s: &str) -> Result<TcpNoAuth, Self::Err> {
let mut s = s.split(" ");
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>
{
const BUF_LEN: usize = 3;
let mut buf = Vec::<u8>::with_capacity(BUF_LEN);
buf.push(5);
buf.push(1);
buf.push(0);
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))?;
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))?;
if read_bytes != 2 {
return Err(ErrorKind::BadBuffer)
}
if buf[0] != 0x05 || buf[1] == 0xFF {
return Err(ErrorKind::BadBuffer)
}
let dest_buf_len = self.destination.len_as_buffer();
let buf_len = 1 + 1 + 1 + dest_buf_len + 2;
buf.resize(buf_len, 0);
buf[0] = 5;
buf[1] = socks5::Command::TcpConnectionEstablishment as u8;
self.destination.extend_buffer(&mut buf[3..]).unwrap();
BigEndian::write_u16(&mut buf[3 + dest_buf_len .. 3 + dest_buf_len + 2], self.port);
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))?;
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))?;
if read_bytes < 2 {
return Err(ErrorKind::BadBuffer)
}
match buf[1] {
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)
}
}
impl Into<TcpStream> for TcpNoAuthStream {
fn into(self) -> TcpStream {
self.wrapped_stream
}
}