dust_devil_core/sandstorm/
handshake.rs

1use std::io::{Error, ErrorKind};
2
3use tokio::io::{AsyncRead, AsyncWrite};
4
5use crate::{
6    serialize::{ByteRead, ByteWrite, SmallReadString, SmallWriteString},
7    u8_repr_enum::U8ReprEnum,
8};
9
10/// A Sandstorm handshake client request, implicitly version 1.
11pub struct SandstormHandshake {
12    /// The username of the user to log in as.
13    pub username: String,
14
15    /// The password of the user to log in as.
16    pub password: String,
17}
18
19/// A borrowed version of [`SandstormHandshake`].
20pub struct SandstormHandshakeRef<'a> {
21    /// The username of the user to log in as.
22    pub username: &'a str,
23
24    /// The password of the user to log in as.
25    pub password: &'a str,
26}
27
28/// An error from reading a [`SandstormHandshake`].
29pub enum ParseHandshakeError {
30    /// Indicates that the client requested an invalid or unsupported Sandstorm version.
31    InvalidVersion(u8),
32
33    /// Indicates that an IO error ocurred while reading the handshake.
34    IO(Error),
35}
36
37impl From<Error> for ParseHandshakeError {
38    fn from(value: Error) -> Self {
39        ParseHandshakeError::IO(value)
40    }
41}
42
43impl SandstormHandshake {
44    pub fn new(username: String, password: String) -> Self {
45        Self { username, password }
46    }
47
48    pub fn as_ref(&self) -> SandstormHandshakeRef {
49        SandstormHandshakeRef {
50            username: &self.username,
51            password: &self.password,
52        }
53    }
54
55    pub async fn read_with_version_check<R: AsyncRead + Unpin + ?Sized>(reader: &mut R) -> Result<Self, ParseHandshakeError> {
56        let version = u8::read(reader).await?;
57        if version != 1 {
58            Err(ParseHandshakeError::InvalidVersion(version))
59        } else {
60            Ok(Self {
61                username: SmallReadString::read(reader).await?.0,
62                password: SmallReadString::read(reader).await?.0,
63            })
64        }
65    }
66}
67
68impl<'a> SandstormHandshakeRef<'a> {
69    pub fn new(username: &'a str, password: &'a str) -> Self {
70        Self { username, password }
71    }
72}
73
74impl ByteRead for SandstormHandshake {
75    async fn read<R: AsyncRead + Unpin + ?Sized>(reader: &mut R) -> Result<Self, Error> {
76        match Self::read_with_version_check(reader).await {
77            Ok(value) => Ok(value),
78            Err(ParseHandshakeError::InvalidVersion(version)) => {
79                Err(Error::new(ErrorKind::InvalidInput, format!("Invalid Sandstorm version: {version}")))
80            }
81            Err(ParseHandshakeError::IO(error)) => Err(error),
82        }
83    }
84}
85
86impl ByteWrite for SandstormHandshake {
87    async fn write<W: AsyncWrite + Unpin + ?Sized>(&self, writer: &mut W) -> Result<(), Error> {
88        self.as_ref().write(writer).await
89    }
90}
91
92impl<'a> ByteWrite for SandstormHandshakeRef<'a> {
93    async fn write<W: AsyncWrite + Unpin + ?Sized>(&self, writer: &mut W) -> Result<(), Error> {
94        (0x01u8, SmallWriteString(self.username), SmallWriteString(self.password))
95            .write(writer)
96            .await
97    }
98}
99
100/// A Sandstorm handshake server response, indicating the result of the handshake.
101#[repr(u8)]
102#[derive(Debug, Clone, Copy, PartialEq, Eq)]
103pub enum SandstormHandshakeStatus {
104    /// The handshake succeeded and access was granted.
105    Ok = 0x00,
106
107    /// The client requested an invalid or unsupported Sandstorm version.
108    UnsupportedVersion = 0x01,
109
110    /// The client specified invalid username or password.
111    InvalidUsernameOrPassword = 0x02,
112
113    /// The client successfully logged in, but the user doesn't have monitoring permissions.
114    PermissionDenied = 0x03,
115
116    /// Unspecified error.
117    UnspecifiedError = 0xFF,
118}
119
120impl U8ReprEnum for SandstormHandshakeStatus {
121    fn from_u8(value: u8) -> Option<SandstormHandshakeStatus> {
122        match value {
123            0x00 => Some(SandstormHandshakeStatus::Ok),
124            0x01 => Some(SandstormHandshakeStatus::UnsupportedVersion),
125            0x02 => Some(SandstormHandshakeStatus::InvalidUsernameOrPassword),
126            0x03 => Some(SandstormHandshakeStatus::PermissionDenied),
127            _ => None,
128        }
129    }
130
131    fn into_u8(self) -> u8 {
132        self as u8
133    }
134}
135
136impl ByteRead for SandstormHandshakeStatus {
137    async fn read<R: AsyncRead + Unpin + ?Sized>(reader: &mut R) -> Result<Self, Error> {
138        match Self::from_u8(u8::read(reader).await?) {
139            Some(value) => Ok(value),
140            None => Err(Error::new(ErrorKind::InvalidData, "Invalid SandstormHandshakeStatus type byte")),
141        }
142    }
143}
144impl ByteWrite for SandstormHandshakeStatus {
145    async fn write<W: AsyncWrite + Unpin + ?Sized>(&self, writer: &mut W) -> Result<(), Error> {
146        self.into_u8().write(writer).await
147    }
148}