socks5_impl/protocol/handshake/password_method/
request.rs

1#[cfg(feature = "tokio")]
2use crate::protocol::AsyncStreamOperation;
3use crate::protocol::{StreamOperation, UserKey};
4#[cfg(feature = "tokio")]
5use tokio::io::{AsyncRead, AsyncReadExt};
6
7/// SOCKS5 password handshake request
8///
9/// ```plain
10/// +-----+------+----------+------+----------+
11/// | VER | ULEN |  UNAME   | PLEN |  PASSWD  |
12/// +-----+------+----------+------+----------+
13/// |  1  |  1   | 1 to 255 |  1   | 1 to 255 |
14/// +-----+------+----------+------+----------+
15/// ```
16
17#[derive(Clone, Debug)]
18pub struct Request {
19    pub user_key: UserKey,
20}
21
22impl Request {
23    pub fn new(username: &str, password: &str) -> Self {
24        let user_key = UserKey::new(username, password);
25        Self { user_key }
26    }
27}
28
29impl StreamOperation for Request {
30    fn retrieve_from_stream<R: std::io::Read>(r: &mut R) -> std::io::Result<Self> {
31        let mut ver = [0; 1];
32        r.read_exact(&mut ver)?;
33        let ver = ver[0];
34
35        if ver != super::SUBNEGOTIATION_VERSION {
36            let err = format!("Unsupported sub-negotiation version {ver:#x}");
37            return Err(std::io::Error::new(std::io::ErrorKind::Unsupported, err));
38        }
39
40        let mut ulen = [0; 1];
41        r.read_exact(&mut ulen)?;
42        let ulen = ulen[0];
43        let mut buf = vec![0; ulen as usize + 1];
44        r.read_exact(&mut buf)?;
45
46        let plen = buf[ulen as usize];
47        buf.truncate(ulen as usize);
48        let username = String::from_utf8(buf).map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))?;
49
50        let mut password = vec![0; plen as usize];
51        r.read_exact(&mut password)?;
52        let pwd = String::from_utf8(password).map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))?;
53
54        let user_key = UserKey::new(username, pwd);
55        Ok(Self { user_key })
56    }
57
58    fn write_to_buf<B: bytes::BufMut>(&self, buf: &mut B) {
59        buf.put_u8(super::SUBNEGOTIATION_VERSION);
60
61        let username = self.user_key.username_arr();
62        buf.put_u8(username.len() as u8);
63        buf.put_slice(&username);
64
65        let password = self.user_key.password_arr();
66        buf.put_u8(password.len() as u8);
67        buf.put_slice(&password);
68    }
69
70    fn len(&self) -> usize {
71        3 + self.user_key.username_arr().len() + self.user_key.password_arr().len()
72    }
73}
74
75#[cfg(feature = "tokio")]
76#[async_trait::async_trait]
77impl AsyncStreamOperation for Request {
78    async fn retrieve_from_async_stream<R>(r: &mut R) -> std::io::Result<Self>
79    where
80        R: AsyncRead + Unpin + Send + ?Sized,
81    {
82        let ver = r.read_u8().await?;
83
84        if ver != super::SUBNEGOTIATION_VERSION {
85            let err = format!("Unsupported sub-negotiation version {ver:#x}");
86            return Err(std::io::Error::new(std::io::ErrorKind::Unsupported, err));
87        }
88
89        let ulen = r.read_u8().await?;
90        let mut buf = vec![0; ulen as usize + 1];
91        r.read_exact(&mut buf).await?;
92
93        let plen = buf[ulen as usize];
94        buf.truncate(ulen as usize);
95        let username = String::from_utf8(buf).map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))?;
96
97        let mut password = vec![0; plen as usize];
98        r.read_exact(&mut password).await?;
99        let pwd = String::from_utf8(password).map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))?;
100
101        let user_key = UserKey::new(username, pwd);
102        Ok(Self { user_key })
103    }
104}