socksx/socks6/s6_client.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
use std::{convert::TryInto, net::SocketAddr};
use anyhow::{ensure, Result};
use tokio::io::AsyncWriteExt;
use tokio::net::TcpStream;
use crate::{Address, constants::*, Credentials};
use crate::socks6::{self, Socks6Request};
use crate::socks6::{
AuthMethod,
options::{AuthMethodAdvertisementOption, SocksOption},
};
/// Represents a SOCKS6 client.
#[derive(Clone)]
pub struct Socks6Client {
proxy_addr: SocketAddr,
credentials: Option<Credentials>,
}
impl Socks6Client {
/// Creates a new Socks6Client.
///
/// # Parameters
/// - `proxy_addr`: The address of the SOCKS6 proxy.
/// - `credentials`: Optional credentials for authentication.
///
/// # Returns
/// A `Result` containing a new `Socks6Client` or an error.
pub async fn new<A: Into<String>>(
proxy_addr: A,
credentials: Option<Credentials>,
) -> Result<Self> {
let proxy_addr = crate::resolve_addr(proxy_addr).await?;
Ok(Socks6Client {
proxy_addr,
credentials,
})
}
/// Connects to a given destination through the SOCKS6 proxy.
///
/// # Parameters
/// - `destination`: The destination to connect to.
/// - `initial_data`: Optional initial data to send.
/// - `options`: Optional SOCKS options.
///
/// # Returns
/// A `Result` containing a tuple of the `TcpStream` and the bound `Address`, or an error.
pub async fn connect<A>(
&self,
destination: A,
initial_data: Option<Vec<u8>>,
options: Option<Vec<SocksOption>>,
) -> Result<(TcpStream, Address)>
where
A: TryInto<Address, Error = anyhow::Error>,
{
let mut stream = TcpStream::connect(&self.proxy_addr).await?;
let binding = self.handshake(destination, initial_data, options, &mut stream).await?;
Ok((stream, binding))
}
/// Conducts the handshake process with the SOCKS6 proxy.
///
/// This method implements the handshake protocol as per [socks6-draft11].
/// [socks6-draft11]: https://tools.ietf.org/html/draft-olteanu-intarea-socks-6-11
///
/// # Parameters
/// - `destination`: The destination to connect to.
/// - `initial_data`: Optional initial data to send.
/// - `options`: Optional SOCKS options.
/// - `stream`: The mutable reference to the `TcpStream`.
///
/// # Returns
/// A `Result` containing the bound `Address` or an error.
pub async fn handshake<A>(
&self,
destination: A,
initial_data: Option<Vec<u8>>,
options: Option<Vec<SocksOption>>,
stream: &mut TcpStream,
) -> Result<Address>
where
A: TryInto<Address, Error = anyhow::Error>,
{
if let Some(Credentials { username, password }) = &self.credentials {
ensure!(username.len() > 255, "Username MUST NOT be larger than 255 bytes.");
ensure!(password.len() > 255, "Password MUST NOT be larger than 255 bytes.");
}
// Prepare initial data.
let initial_data = initial_data.unwrap_or_default();
ensure!(
initial_data.len() <= 2 ^ 14,
"Initial data MUST NOT be larger than 16384 bytes."
);
let initial_data_length = initial_data.len() as u16;
// Prepare SOCKS options.
let mut auth_methods = vec![];
if self.credentials.is_some() {
auth_methods.push(AuthMethod::UsernamePassword);
}
let auth_methods_adv = AuthMethodAdvertisementOption::new(initial_data_length, vec![]);
let mut options = options.unwrap_or_default();
options.push(auth_methods_adv.wrap());
// Create SOCKS6 CONNECT request.
let request = Socks6Request::new(
SOCKS_CMD_CONNECT,
destination.try_into()?,
initial_data_length,
options,
None,
);
// Send SOCKS request information.
let request_bytes = request.into_socks_bytes();
stream.write(&request_bytes).await?;
// Wait for authentication and operation reply.
let _ = socks6::read_no_authentication(stream).await?;
let (binding, _) = socks6::read_reply(stream).await?;
Ok(binding)
}
}