use std::fmt;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
use std::str::FromStr;
use base64::Engine as _;
use base64::engine::general_purpose::STANDARD_NO_PAD;
use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
use crate::Error;
const IPV4_ADDRESS: u8 = 0x01;
const DOMAIN_ADDRESS: u8 = 0x03;
const IPV6_ADDRESS: u8 = 0x04;
const CUSTOM_SUFFIX: &str = ".custom";
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub enum SocksAddress {
Ip(SocketAddr),
Domain {
host: String,
port: u16,
},
Custom {
network: String,
address: String,
},
}
impl SocksAddress {
pub fn domain(host: impl Into<String>, port: u16) -> Result<Self, Error> {
let host = host.into();
validate_wire_string(&host)?;
if host.contains(':')
|| host
.chars()
.any(|character| character.is_whitespace() || character.is_control())
{
return Err(Error::InvalidSocksAddress);
}
Ok(Self::Domain { host, port })
}
pub fn custom(network: impl Into<String>, address: impl Into<String>) -> Result<Self, Error> {
let network = network.into();
let address = address.into();
if network.is_empty() {
return Err(Error::InvalidSocksAddress);
}
let encoded = encode_custom_domain(&network, &address);
validate_wire_string(&encoded)?;
Ok(Self::Custom { network, address })
}
pub fn encode(&self) -> Result<Vec<u8>, Error> {
let mut encoded = Vec::with_capacity(19);
match self {
Self::Ip(SocketAddr::V4(address)) => {
encoded.push(IPV4_ADDRESS);
encoded.extend_from_slice(&address.ip().octets());
encoded.extend_from_slice(&address.port().to_be_bytes());
}
Self::Ip(SocketAddr::V6(address)) => {
encoded.push(IPV6_ADDRESS);
encoded.extend_from_slice(&address.ip().octets());
encoded.extend_from_slice(&address.port().to_be_bytes());
}
Self::Domain { host, port } => {
encode_domain(&mut encoded, host, *port)?;
}
Self::Custom { network, address } => {
encode_domain(&mut encoded, &encode_custom_domain(network, address), 0)?;
}
}
Ok(encoded)
}
pub fn decode(encoded: &[u8]) -> Result<Self, Error> {
let (address, consumed) = Self::decode_prefix(encoded)?;
if consumed != encoded.len() {
return Err(Error::InvalidSocksAddress);
}
Ok(address)
}
pub(crate) fn decode_prefix(encoded: &[u8]) -> Result<(Self, usize), Error> {
let (&address_type, remainder) = encoded.split_first().ok_or(Error::InvalidSocksAddress)?;
match address_type {
IPV4_ADDRESS => {
if remainder.len() < 6 {
return Err(Error::InvalidSocksAddress);
}
let ip = Ipv4Addr::new(remainder[0], remainder[1], remainder[2], remainder[3]);
let port = u16::from_be_bytes([remainder[4], remainder[5]]);
Ok((Self::Ip(SocketAddr::new(IpAddr::V4(ip), port)), 7))
}
IPV6_ADDRESS => {
if remainder.len() < 18 {
return Err(Error::InvalidSocksAddress);
}
let octets: [u8; 16] = remainder[..16]
.try_into()
.map_err(|_| Error::InvalidSocksAddress)?;
let port = u16::from_be_bytes([remainder[16], remainder[17]]);
Ok((
Self::Ip(SocketAddr::new(IpAddr::V6(Ipv6Addr::from(octets)), port)),
19,
))
}
DOMAIN_ADDRESS => {
let (&length, _) = remainder.split_first().ok_or(Error::InvalidSocksAddress)?;
let consumed = usize::from(length) + 4;
if encoded.len() < consumed {
return Err(Error::InvalidSocksAddress);
}
Ok((decode_domain(&remainder[..consumed - 1])?, consumed))
}
address_type => Err(Error::UnsupportedSocksAddressType { address_type }),
}
}
#[must_use]
pub fn network(&self) -> &str {
match self {
Self::Custom { network, .. } => network,
Self::Ip(_) | Self::Domain { .. } => "socks5",
}
}
#[must_use]
pub const fn port(&self) -> u16 {
match self {
Self::Ip(address) => address.port(),
Self::Domain { port, .. } => *port,
Self::Custom { .. } => 0,
}
}
pub(crate) async fn read_from(reader: &mut (impl AsyncRead + Unpin)) -> Result<Self, Error> {
let address_type = reader.read_u8().await?;
let mut encoded = vec![address_type];
match address_type {
IPV4_ADDRESS => {
let mut remainder = [0_u8; 6];
reader.read_exact(&mut remainder).await?;
encoded.extend_from_slice(&remainder);
}
IPV6_ADDRESS => {
let mut remainder = [0_u8; 18];
reader.read_exact(&mut remainder).await?;
encoded.extend_from_slice(&remainder);
}
DOMAIN_ADDRESS => {
let length = reader.read_u8().await?;
encoded.push(length);
let mut remainder = vec![0_u8; usize::from(length) + 2];
reader.read_exact(&mut remainder).await?;
encoded.extend_from_slice(&remainder);
}
address_type => return Err(Error::UnsupportedSocksAddressType { address_type }),
}
Self::decode(&encoded)
}
pub(crate) async fn write_to(
&self,
writer: &mut (impl AsyncWrite + Unpin),
) -> Result<(), Error> {
writer.write_all(&self.encode()?).await?;
Ok(())
}
}
impl From<SocketAddr> for SocksAddress {
fn from(address: SocketAddr) -> Self {
Self::Ip(address)
}
}
impl FromStr for SocksAddress {
type Err = Error;
fn from_str(value: &str) -> Result<Self, Self::Err> {
if let Ok(address) = value.parse::<SocketAddr>() {
return Ok(Self::Ip(address));
}
let (host, port) = value.rsplit_once(':').ok_or(Error::InvalidSocksAddress)?;
if host.is_empty() || host.starts_with('[') || host.ends_with(']') {
return Err(Error::InvalidSocksAddress);
}
let port = port
.parse::<u16>()
.map_err(|_| Error::InvalidSocksAddress)?;
Self::domain(host, port)
}
}
impl fmt::Display for SocksAddress {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Ip(address) => address.fmt(formatter),
Self::Domain { host, port } => write!(formatter, "{host}:{port}"),
Self::Custom { address, .. } => address.fmt(formatter),
}
}
}
fn validate_wire_string(value: &str) -> Result<(), Error> {
if value.is_empty() {
return Err(Error::InvalidSocksAddress);
}
if value.len() > u8::MAX.into() {
return Err(Error::SocksAddressTooLong);
}
Ok(())
}
fn encode_domain(encoded: &mut Vec<u8>, domain: &str, port: u16) -> Result<(), Error> {
validate_wire_string(domain)?;
encoded.push(DOMAIN_ADDRESS);
encoded.push(u8::try_from(domain.len()).map_err(|_| Error::SocksAddressTooLong)?);
encoded.extend_from_slice(domain.as_bytes());
encoded.extend_from_slice(&port.to_be_bytes());
Ok(())
}
fn decode_domain(remainder: &[u8]) -> Result<SocksAddress, Error> {
let (&length, remainder) = remainder.split_first().ok_or(Error::InvalidSocksAddress)?;
let length = usize::from(length);
if length == 0 || remainder.len() != length + 2 {
return Err(Error::InvalidSocksAddress);
}
let host = std::str::from_utf8(&remainder[..length]).map_err(|_| Error::InvalidSocksAddress)?;
let port = u16::from_be_bytes([remainder[length], remainder[length + 1]]);
if port == 0 && host.ends_with(CUSTOM_SUFFIX) {
let (network, address) = decode_custom_domain(host)?;
SocksAddress::custom(network, address)
} else {
SocksAddress::domain(host, port)
}
}
fn encode_custom_domain(network: &str, address: &str) -> String {
format!(
"{}.{}{}",
STANDARD_NO_PAD.encode(network),
STANDARD_NO_PAD.encode(address),
CUSTOM_SUFFIX
)
}
fn decode_custom_domain(domain: &str) -> Result<(String, String), Error> {
let encoded = domain
.strip_suffix(CUSTOM_SUFFIX)
.ok_or(Error::InvalidSocksAddress)?;
let mut fields = encoded.split('.');
let network = fields.next().ok_or(Error::InvalidSocksAddress)?;
let address = fields.next().ok_or(Error::InvalidSocksAddress)?;
if network.is_empty() || fields.next().is_some() {
return Err(Error::InvalidSocksAddress);
}
let network = STANDARD_NO_PAD
.decode(network)
.map_err(|_| Error::InvalidSocksAddress)?;
let address = STANDARD_NO_PAD
.decode(address)
.map_err(|_| Error::InvalidSocksAddress)?;
let network = String::from_utf8(network).map_err(|_| Error::InvalidSocksAddress)?;
let address = String::from_utf8(address).map_err(|_| Error::InvalidSocksAddress)?;
Ok((network, address))
}