dust_devil_core/
socks5.rs

1//! Basic types for implementing the socks5 protocol as well as implementations of [`ByteRead`] and
2//! [`ByteWrite`] for them.
3
4use std::{
5    io::{Error, ErrorKind},
6    net::{Ipv4Addr, Ipv6Addr},
7};
8
9use tokio::io::{AsyncRead, AsyncWrite};
10
11use crate::{
12    serialize::{ByteRead, ByteWrite, SmallReadString, SmallWriteString},
13    u8_repr_enum::U8ReprEnum,
14};
15
16/// A socks5 request's address (without a port).
17#[derive(Debug, Clone, PartialEq, Eq)]
18pub enum SocksRequestAddress {
19    IPv4(Ipv4Addr),
20    IPv6(Ipv6Addr),
21    Domainname(String),
22}
23
24impl ByteWrite for SocksRequestAddress {
25    async fn write<W: AsyncWrite + Unpin + ?Sized>(&self, writer: &mut W) -> Result<(), Error> {
26        match self {
27            Self::IPv4(v4) => (4u8, v4).write(writer).await,
28            Self::IPv6(v6) => (6u8, v6).write(writer).await,
29            Self::Domainname(domainname) => (200u8, SmallWriteString(domainname)).write(writer).await,
30        }
31    }
32}
33
34impl ByteRead for SocksRequestAddress {
35    async fn read<R: AsyncRead + Unpin + ?Sized>(reader: &mut R) -> Result<Self, Error> {
36        match u8::read(reader).await? {
37            4 => Ok(SocksRequestAddress::IPv4(Ipv4Addr::read(reader).await?)),
38            6 => Ok(SocksRequestAddress::IPv6(Ipv6Addr::read(reader).await?)),
39            200 => Ok(SocksRequestAddress::Domainname(SmallReadString::read(reader).await?.0)),
40            _ => Err(Error::new(ErrorKind::InvalidData, "Invalid SocksRequestAddress type byte")),
41        }
42    }
43}
44
45/// A socks5 request's destination, including the address and port.
46pub struct SocksRequest {
47    pub destination: SocksRequestAddress,
48    pub port: u16,
49}
50
51impl SocksRequest {
52    pub fn new(destination: SocksRequestAddress, port: u16) -> Self {
53        Self { destination, port }
54    }
55
56    pub fn from_ipv4(ipv4: Ipv4Addr, port: u16) -> Self {
57        SocksRequest {
58            destination: SocksRequestAddress::IPv4(ipv4),
59            port,
60        }
61    }
62
63    pub fn from_ipv6(ipv6: Ipv6Addr, port: u16) -> Self {
64        SocksRequest {
65            destination: SocksRequestAddress::IPv6(ipv6),
66            port,
67        }
68    }
69
70    pub fn from_domainname(domainname: String, port: u16) -> Self {
71        SocksRequest {
72            destination: SocksRequestAddress::Domainname(domainname),
73            port,
74        }
75    }
76}
77
78impl ByteWrite for SocksRequest {
79    async fn write<W: AsyncWrite + Unpin + ?Sized>(&self, writer: &mut W) -> Result<(), Error> {
80        (&self.destination, self.port).write(writer).await
81    }
82}
83
84impl ByteRead for SocksRequest {
85    async fn read<R: AsyncRead + Unpin + ?Sized>(reader: &mut R) -> Result<Self, Error> {
86        Ok(SocksRequest::new(
87            SocksRequestAddress::read(reader).await?,
88            u16::read(reader).await?,
89        ))
90    }
91}
92
93/// A socks5 authentication method.
94#[repr(u8)]
95#[derive(Debug, Clone, Copy, PartialEq, Eq)]
96pub enum AuthMethod {
97    NoAuth = 0x00,
98    // GSSAPI = 0x01,
99    UsernameAndPassword = 0x02,
100}
101
102impl U8ReprEnum for AuthMethod {
103    fn from_u8(value: u8) -> Option<Self> {
104        match value {
105            0x00 => Some(AuthMethod::NoAuth),
106            // 0x01 => Some(AuthMethod::GSSAPI),
107            0x02 => Some(AuthMethod::UsernameAndPassword),
108            _ => None,
109        }
110    }
111
112    fn into_u8(self) -> u8 {
113        self as u8
114    }
115}
116
117impl ByteWrite for AuthMethod {
118    async fn write<W: AsyncWrite + Unpin + ?Sized>(&self, writer: &mut W) -> Result<(), Error> {
119        self.into_u8().write(writer).await
120    }
121}
122
123impl ByteRead for AuthMethod {
124    async fn read<R: AsyncRead + Unpin + ?Sized>(reader: &mut R) -> Result<Self, Error> {
125        match Self::from_u8(u8::read(reader).await?) {
126            Some(value) => Ok(value),
127            None => Err(Error::new(ErrorKind::InvalidData, "Invalid AuthMethod type byte")),
128        }
129    }
130}
131
132impl AuthMethod {
133    /// Gets this `AuthMethod` represented by a `&'static str`.
134    pub const fn to_str(&self) -> &'static str {
135        match self {
136            Self::NoAuth => "noauth",
137            Self::UsernameAndPassword => "userpass",
138        }
139    }
140}
141
142impl std::fmt::Display for AuthMethod {
143    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
144        write!(f, "{}", self.to_str())
145    }
146}