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
pub mod address;
mod error;
mod socks;

use address::ToSocketDestination;
use error::ProxyStreamError;
use socks::{AuthMethod, InterruptedSocks5, Socks5};
use tokio::io::{AsyncRead, AsyncWrite};

pub trait AsyncSocket: AsyncRead + AsyncWrite + Unpin + Send + 'static {}
impl<T> AsyncSocket for T where T: AsyncRead + AsyncWrite + Unpin + Send + 'static {}

pub enum ProxyType {
    SOCKS5,
}
pub struct ProxyStream {
    proxy_type: ProxyType,
}

pub enum InterruptedStream {
    Socks5(InterruptedSocks5),
}

impl InterruptedStream {
    pub async fn serve(self, stream: impl AsyncSocket) -> Result<(), ProxyStreamError> {
        match self {
            InterruptedStream::Socks5(socks) => socks.serve(stream).await,
        }
    }
    pub fn addr(&self) -> &address::DestinationAddress {
        match self {
            InterruptedStream::Socks5(socks) => &socks.addr,
        }
    }
    pub async fn replay_error(self, replay: ReplayError) -> Result<(), ProxyStreamError> {
        match self {
            InterruptedStream::Socks5(socks) => socks.replay_error(replay.into()).await,
        }
    }
}

pub enum ReplayError {
    GeneralSocksServerFailure,
    ConnectionNotAllowedByRuleset,
    NetworkUnreachable,
    HostUnreachable,
    ConnectionRefused,
    TtlExpired,
    CommandNotSupported,
    AddressTypeNotSupported,
}

impl ProxyStream {
    pub fn new(proxy_type: ProxyType) -> Self {
        ProxyStream { proxy_type }
    }
    pub async fn accept(
        &self,
        stream: impl AsyncSocket,
    ) -> Result<InterruptedStream, ProxyStreamError> {
        match self.proxy_type {
            ProxyType::SOCKS5 => {
                let socks = Socks5::new(vec![AuthMethod::Noauth])?;
                Ok(InterruptedStream::Socks5(socks.accept(stream).await?))
            }
        }
    }
    pub async fn connect(
        &self,
        stream: impl AsyncSocket,
        addr: impl ToSocketDestination,
    ) -> Result<impl AsyncSocket, ProxyStreamError> {
        match self.proxy_type {
            ProxyType::SOCKS5 => {
                let socks = Socks5::new(vec![AuthMethod::Noauth])?;
                socks.connect(stream, socks::Command::Connect, addr).await
            }
        }
    }
}