use super::security;
use crate::{VncError, VncVersion};
use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
#[allow(dead_code)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub(super) enum SecurityType {
Invalid = 0,
None = 1,
VncAuth = 2,
RA2 = 5,
RA2ne = 6,
Tight = 16,
Ultra = 17,
Tls = 18,
VeNCrypt = 19,
GtkVncSasl = 20,
Md5Hash = 21,
ColinDeanXvp = 22,
}
impl TryFrom<u8> for SecurityType {
type Error = VncError;
fn try_from(num: u8) -> Result<Self, Self::Error> {
match num {
0 | 1 | 2 | 5 | 6 | 16 | 17 | 18 | 19 | 20 | 21 | 22 => {
Ok(unsafe { std::mem::transmute::<u8, SecurityType>(num) })
}
invalid => Err(VncError::InvalidSecurityTyep(invalid)),
}
}
}
impl From<SecurityType> for u8 {
fn from(e: SecurityType) -> Self {
e as u8
}
}
impl SecurityType {
pub(super) async fn read<S>(reader: &mut S, version: &VncVersion) -> Result<Vec<Self>, VncError>
where
S: AsyncRead + Unpin,
{
match version {
VncVersion::RFB33 => {
let security_type = reader.read_u32().await?;
let security_type = (security_type as u8).try_into()?;
if let SecurityType::Invalid = security_type {
let _ = reader.read_u32().await?;
let mut err_msg = String::new();
reader.read_to_string(&mut err_msg).await?;
return Err(VncError::General(err_msg));
}
Ok(vec![security_type])
}
_ => {
let num = reader.read_u8().await?;
if num == 0 {
let _ = reader.read_u32().await?;
let mut err_msg = String::new();
reader.read_to_string(&mut err_msg).await?;
return Err(VncError::General(err_msg));
}
let mut sec_types = vec![];
for _ in 0..num {
sec_types.push(reader.read_u8().await?.try_into()?);
}
tracing::trace!("Server supported security type: {:?}", sec_types);
Ok(sec_types)
}
}
}
pub(super) async fn write<S>(&self, writer: &mut S) -> Result<(), VncError>
where
S: AsyncWrite + Unpin,
{
writer.write_all(&[(*self).into()]).await?;
Ok(())
}
}
#[allow(dead_code)]
#[repr(u32)]
pub(super) enum AuthResult {
Ok = 0,
Failed = 1,
}
impl From<u32> for AuthResult {
fn from(num: u32) -> Self {
unsafe { std::mem::transmute(num) }
}
}
impl From<AuthResult> for u32 {
fn from(e: AuthResult) -> Self {
e as u32
}
}
pub(super) struct AuthHelper {
challenge: [u8; 16],
key: [u8; 8],
}
impl AuthHelper {
pub(super) async fn read<S>(reader: &mut S, credential: &str) -> Result<Self, VncError>
where
S: AsyncRead + Unpin,
{
let mut challenge = [0; 16];
reader.read_exact(&mut challenge).await?;
let credential_len = credential.len();
let mut key = [0u8; 8];
for (i, key_i) in key.iter_mut().enumerate() {
let c = if i < credential_len {
credential.as_bytes()[i]
} else {
0
};
let mut cs = 0u8;
for j in 0..8 {
cs |= ((c >> j) & 1) << (7 - j)
}
*key_i = cs;
}
Ok(Self { challenge, key })
}
pub(super) async fn write<S>(&self, writer: &mut S) -> Result<(), VncError>
where
S: AsyncWrite + Unpin,
{
let encrypted = security::des::encrypt(&self.challenge, &self.key);
writer.write_all(&encrypted).await?;
Ok(())
}
pub(super) async fn finish<S>(self, reader: &mut S) -> Result<AuthResult, VncError>
where
S: AsyncRead + AsyncWrite + Unpin,
{
let result = reader.read_u32().await?;
Ok(result.into())
}
}