socksx/socks5/
mod.rs

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
use anyhow::Result;
use num_traits::FromPrimitive;
use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};

pub use s5_client::Socks5Client;
pub use s5_handler::Socks5Handler;

use crate::addresses::{self, Address};
use crate::constants::*;

mod s5_client;
mod s5_handler;

/// Represents the different commands for SOCKS5 protocol.
#[repr(u8)]
#[derive(Clone, Debug, FromPrimitive, PartialEq)]
pub enum Socks5Command {
    Connect = 0x01,
    Bind = 0x02,
    UdpAssociate = 0x03,
}

/// Represents a SOCKS5 request.
#[derive(Clone, Debug)]
pub struct Socks5Request {
    pub command: Socks5Command,
    pub destination: Address,
}

impl Socks5Request {
    /// Creates a new SOCKS5 request.
    ///
    /// # Arguments
    ///
    /// * `command` - The command type (e.g., Connect).
    /// * `destination` - The target address and port to connect to.
    ///
    /// # Returns
    ///
    /// A new `Socks5Request` instance.
    pub fn new(
        command: u8,
        destination: Address,
    ) -> Self {
        Socks5Request {
            command: Socks5Command::from_u8(command).unwrap(),
            destination,
        }
    }

    /// Converts the request into bytes suitable for transmission over a SOCKS5 connection.
    ///
    /// # Returns
    ///
    /// A vector of bytes representing the request.
    pub fn into_socks_bytes(self) -> Vec<u8> {
        let mut data = vec![SOCKS_VER_5, SOCKS_CMD_CONNECT, SOCKS_RSV];
        data.extend(self.destination.as_socks_bytes());

        data
    }
}

/// Represents different reply codes for SOCKS5 protocol.
#[repr(u8)]
#[derive(Clone, Debug, FromPrimitive, PartialEq)]
pub enum Socks5Reply {
    Success = 0x00,
    GeneralFailure = 0x01,
    ConnectionNotAllowed = 0x02,
    NetworkUnreachable = 0x03,
    HostUnreachable = 0x04,
    ConnectionRefused = 0x05,
    TTLExpired = 0x06,
    CommandNotSupported = 0x07,
    AddressTypeNotSupported = 0x08,
    ConnectionAttemptTimeOut = 0x09,
}

/// Writes a SOCKS5 reply to the provided stream.
///
/// # Arguments
///
/// * `stream` - The output stream where the reply will be written.
/// * `reply` - The SOCKS5 reply code to be written.
///
/// # Returns
///
/// A `Result` indicating success or an error.
pub async fn write_reply<S>(
    stream: &mut S,
    reply: Socks5Reply,
) -> Result<()>
    where
        S: AsyncWrite + Unpin,
{
    let reply = [
        SOCKS_VER_5,
        reply as u8,
        SOCKS_RSV,
        SOCKS_ATYP_IPV4,
        0x00,
        0x00,
        0x00,
        0x00,
        0x00,
        0x00,
    ];

    stream.write(&reply).await?;

    Ok(())
}

/// Reads a SOCKS5 reply from the provided stream and returns the associated address.
///
/// # Arguments
///
/// * `stream` - The input stream where the reply will be read from.
///
/// # Returns
///
/// A `Result` containing the address associated with the reply if successful, or an error if the reply indicates failure.
pub async fn read_reply<S>(stream: &mut S) -> Result<Address>
    where
        S: AsyncRead + Unpin,
{
    let mut operation_reply = [0; 3];
    stream.read_exact(&mut operation_reply).await?;

    let reply_code = operation_reply[1];
    ensure!(
        reply_code == SOCKS_REP_SUCCEEDED,
        "CONNECT operation failed: {}",
        reply_code
    );

    let binding = addresses::read_address(stream).await?;

    Ok(binding)
}