use crate::auth::AuthUnixParams;
use crate::bytes_ext::BytesReaderExt;
use crate::read_slice_bytes;
use crate::Error;
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use bytes::Bytes;
use std::convert::TryFrom;
use std::io::{Cursor, Write};
const AUTH_NONE: u32 = 0;
const AUTH_UNIX: u32 = 1;
const AUTH_SHORT: u32 = 2;
#[non_exhaustive]
#[derive(Debug, PartialEq)]
pub enum AuthFlavor<T>
where
T: AsRef<[u8]>,
{
AuthNone(Option<T>),
AuthUnix(AuthUnixParams<T>),
AuthShort(T),
Unknown {
id: u32,
data: T,
},
}
impl<'a> AuthFlavor<&'a [u8]> {
pub(crate) fn from_cursor(r: &mut Cursor<&'a [u8]>) -> Result<Self, Error> {
let flavor = r.read_u32::<BigEndian>()?;
let len = r.read_u32::<BigEndian>()?;
if len > 200 {
return Err(Error::InvalidLength);
}
let flavor = match flavor {
AUTH_NONE => AuthFlavor::new_none(r, len)?,
AUTH_UNIX => AuthFlavor::new_unix(r, len)?,
AUTH_SHORT => AuthFlavor::new_short(r, len)?,
v => AuthFlavor::Unknown {
id: v,
data: read_slice_bytes(r, len)?,
},
};
Ok(flavor)
}
fn new_none(r: &mut Cursor<&'a [u8]>, len: u32) -> Result<Self, Error> {
if len == 0 {
return Ok(AuthFlavor::AuthNone(None));
}
Ok(AuthFlavor::AuthNone(Some(read_slice_bytes(r, len)?)))
}
fn new_unix(r: &mut Cursor<&'a [u8]>, len: u32) -> Result<Self, Error> {
Ok(AuthFlavor::AuthUnix(AuthUnixParams::from_cursor(r, len)?))
}
fn new_short(r: &mut Cursor<&'a [u8]>, len: u32) -> Result<Self, Error> {
Ok(AuthFlavor::AuthShort(read_slice_bytes(r, len)?))
}
}
impl<T> AuthFlavor<T>
where
T: AsRef<[u8]>,
{
pub fn serialise_into(&self, buf: &mut Cursor<Vec<u8>>) -> Result<(), std::io::Error> {
buf.write_u32::<BigEndian>(self.id())?;
buf.write_u32::<BigEndian>(self.associated_data_len())?;
match self {
AuthFlavor::AuthNone(Some(d)) => buf.write_all(d.as_ref()),
AuthFlavor::AuthNone(None) => Ok(()),
AuthFlavor::AuthUnix(p) => p.serialise_into(buf),
AuthFlavor::AuthShort(d) => buf.write_all(d.as_ref()),
AuthFlavor::Unknown { id: _id, data } => buf.write_all(data.as_ref()),
}
}
pub fn id(&self) -> u32 {
match self {
AuthFlavor::AuthNone(_) => AUTH_NONE,
AuthFlavor::AuthUnix(_) => AUTH_UNIX,
AuthFlavor::AuthShort(_) => AUTH_SHORT,
AuthFlavor::Unknown { id, data: _ } => *id,
}
}
pub fn associated_data_len(&self) -> u32 {
match self {
AuthFlavor::AuthNone(Some(d)) => d.as_ref().len() as u32,
AuthFlavor::AuthNone(None) => 0,
AuthFlavor::AuthUnix(p) => p.serialised_len(),
AuthFlavor::AuthShort(d) => d.as_ref().len() as u32,
AuthFlavor::Unknown { id: _id, data } => data.as_ref().len() as u32,
}
}
pub fn serialised_len(&self) -> u32 {
let mut l = 0;
l += 4;
l += 4;
l += match self {
AuthFlavor::AuthNone(ref data) => {
data.as_ref().map_or(0, |d| d.as_ref().len())
}
AuthFlavor::AuthUnix(ref p) => p.serialised_len() as usize,
AuthFlavor::AuthShort(data) => {
data.as_ref().len()
}
AuthFlavor::Unknown { id: _id, data } => {
data.as_ref().len()
}
};
l as u32
}
}
impl<'a> TryFrom<&'a [u8]> for AuthFlavor<&'a [u8]> {
type Error = Error;
fn try_from(v: &'a [u8]) -> Result<Self, Self::Error> {
let mut c = Cursor::new(v);
AuthFlavor::from_cursor(&mut c)
}
}
impl TryFrom<Bytes> for AuthFlavor<Bytes> {
type Error = Error;
fn try_from(mut v: Bytes) -> Result<Self, Self::Error> {
let flavor = v.try_u32()?;
let auth_data = v.try_array(200)?;
let flavor = match flavor {
AUTH_NONE if auth_data.is_empty() => AuthFlavor::AuthNone(None),
AUTH_NONE => AuthFlavor::AuthNone(Some(auth_data)),
AUTH_UNIX => {
let should_consume = auth_data.len();
let params = AuthUnixParams::try_from(auth_data)?;
if params.serialised_len() as usize != should_consume {
return Err(Error::InvalidAuthData);
}
AuthFlavor::AuthUnix(params)
}
AUTH_SHORT => AuthFlavor::AuthShort(auth_data),
id => AuthFlavor::Unknown {
id,
data: auth_data,
},
};
Ok(flavor)
}
}
#[cfg(test)]
mod tests {
use super::*;
use hex_literal::hex;
use std::convert::TryInto;
#[test]
fn test_auth_unix<'a>() {
const RAW: [u8; 92] = hex!(
"00000001000000540000000000000000000001f50000001400000010000001f500
00000c000000140000003d0000004f000000500000005100000062000002bd00000
02100000064000000cc000000fa0000018b0000018e0000018f"
);
let f: AuthFlavor<&'a [u8]> = RAW.as_ref().try_into().expect("failed to parse message");
assert_eq!(f.serialised_len(), 92);
assert_eq!(f.id(), AUTH_UNIX);
assert_eq!(f.associated_data_len(), 92 - 4 - 4);
let params = match f {
AuthFlavor::AuthUnix(ref p) => p,
_ => panic!("wrong auth"),
};
assert_eq!(params.uid(), 501);
let mut c = Cursor::new(Vec::new());
f.serialise_into(&mut c).expect("serialise failed");
let buf = c.into_inner();
assert_eq!(buf.as_slice(), RAW.as_ref());
}
#[test]
fn test_auth_none<'a>() {
const RAW: [u8; 92] = hex!(
"
00 00 00 00
00 00 00 54
0000000000000000000001f50000001400000010000001f50000000c00000014000
0003d0000004f000000500000005100000062000002bd0000002100000064000000
cc000000fa0000018b0000018e0000018f"
);
let f: AuthFlavor<&'a [u8]> = RAW.as_ref().try_into().expect("failed to parse message");
assert_eq!(f.serialised_len(), 92);
assert_eq!(f.id(), AUTH_NONE);
assert_eq!(f.associated_data_len(), 92 - 4 - 4);
let data = match f {
AuthFlavor::AuthNone(Some(ref p)) => p,
_ => panic!("wrong auth"),
};
assert_eq!(data.len(), f.associated_data_len() as usize);
}
#[test]
fn test_auth_short<'a>() {
const RAW: [u8; 92] = hex!(
"
00 00 00 02
00 00 00 54
0000000000000000000001f50000001400000010000001f50000000c00000014000
0003d0000004f000000500000005100000062000002bd0000002100000064000000
cc000000fa0000018b0000018e0000018f"
);
let f: AuthFlavor<&'a [u8]> = RAW.as_ref().try_into().expect("failed to parse message");
assert_eq!(f.serialised_len(), 92);
assert_eq!(f.id(), AUTH_SHORT);
assert_eq!(f.associated_data_len(), 92 - 4 - 4);
let data = match f {
AuthFlavor::AuthShort(ref p) => p,
_ => panic!("wrong auth"),
};
assert_eq!(data.len(), f.associated_data_len() as usize);
}
#[test]
fn test_auth_unknown<'a>() {
const RAW: [u8; 92] = hex!(
"
00 00 00 FF
00 00 00 54
0000000000000000000001f50000001400000010000001f50000000c00000014000
0003d0000004f000000500000005100000062000002bd0000002100000064000000
cc000000fa0000018b0000018e0000018f"
);
let f: AuthFlavor<&'a [u8]> = RAW.as_ref().try_into().expect("failed to parse message");
assert_eq!(f.serialised_len(), 92);
assert_eq!(f.id(), 255);
assert_eq!(f.associated_data_len(), 92 - 4 - 4);
let (id, data) = match f {
AuthFlavor::Unknown { id, data } => (id, data),
_ => panic!("wrong auth"),
};
assert_eq!(id, f.id());
assert_eq!(data.len(), f.associated_data_len() as usize);
}
}