use std::{
convert::TryFrom,
io::{Cursor, Write},
};
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use crate::{auth::AuthFlavor, Error};
const REPLY_SUCCESS: u32 = 0;
const REPLY_PROG_UNAVAIL: u32 = 1;
const REPLY_PROG_MISMATCH: u32 = 2;
const REPLY_PROC_UNAVAIL: u32 = 3;
const REPLY_GARBAGE_ARGS: u32 = 4;
const REPLY_SYSTEM_ERR: u32 = 5;
#[derive(Debug, PartialEq)]
pub struct AcceptedReply<T, P>
where
T: AsRef<[u8]>,
P: AsRef<[u8]>,
{
auth_verifier: AuthFlavor<T>,
status: AcceptedStatus<P>,
}
impl<'a> AcceptedReply<&'a [u8], &'a [u8]> {
pub(crate) fn from_cursor(r: &mut Cursor<&'a [u8]>) -> Result<Self, Error> {
Ok(AcceptedReply {
auth_verifier: AuthFlavor::from_cursor(r)?,
status: AcceptedStatus::from_cursor(r)?,
})
}
}
impl<T, P> AcceptedReply<T, P>
where
T: AsRef<[u8]>,
P: AsRef<[u8]>,
{
pub fn new(auth_verifier: AuthFlavor<T>, status: AcceptedStatus<P>) -> Self {
Self {
auth_verifier,
status,
}
}
pub fn serialise_into<W: Write>(&self, mut buf: W) -> Result<(), std::io::Error> {
self.auth_verifier.serialise_into(&mut buf)?;
self.status.serialise_into(&mut buf)
}
pub fn serialised_len(&self) -> u32 {
self.auth_verifier.serialised_len() + self.status.serialised_len()
}
pub fn auth_verifier(&self) -> &AuthFlavor<T> {
&self.auth_verifier
}
pub fn status(&self) -> &AcceptedStatus<P> {
&self.status
}
}
impl<'a> TryFrom<&'a [u8]> for AcceptedReply<&'a [u8], &'a [u8]> {
type Error = Error;
fn try_from(v: &'a [u8]) -> Result<Self, Self::Error> {
let mut c = Cursor::new(v);
AcceptedReply::from_cursor(&mut c)
}
}
#[cfg(feature = "bytes")]
impl TryFrom<crate::Bytes> for AcceptedReply<crate::Bytes, crate::Bytes> {
type Error = Error;
fn try_from(mut v: crate::Bytes) -> Result<Self, Self::Error> {
use crate::Buf;
let auth_verifier = AuthFlavor::try_from(v.clone())?;
v.advance(auth_verifier.serialised_len() as usize);
Ok(Self {
auth_verifier,
status: AcceptedStatus::try_from(v)?,
})
}
}
#[derive(Debug, PartialEq, Clone)]
pub enum AcceptedStatus<P>
where
P: AsRef<[u8]>,
{
Success(P),
ProgramUnavailable,
ProgramMismatch {
low: u32,
high: u32,
},
ProcedureUnavailable,
GarbageArgs,
SystemError,
}
impl<'a> AcceptedStatus<&'a [u8]> {
pub(crate) fn from_cursor(r: &mut Cursor<&'a [u8]>) -> Result<Self, Error> {
let reply = match r.read_u32::<BigEndian>()? {
REPLY_SUCCESS => AcceptedStatus::new_success(r),
REPLY_PROG_UNAVAIL => AcceptedStatus::ProgramUnavailable,
REPLY_PROG_MISMATCH => AcceptedStatus::ProgramMismatch {
low: r.read_u32::<BigEndian>()?,
high: r.read_u32::<BigEndian>()?,
},
REPLY_PROC_UNAVAIL => AcceptedStatus::ProcedureUnavailable,
REPLY_GARBAGE_ARGS => AcceptedStatus::GarbageArgs,
REPLY_SYSTEM_ERR => AcceptedStatus::SystemError,
v => return Err(Error::InvalidReplyStatus(v)),
};
Ok(reply)
}
fn new_success(r: &mut Cursor<&'a [u8]>) -> Self {
let data = *r.get_ref();
let start = r.position() as usize;
let payload = &data[start..];
AcceptedStatus::Success(payload)
}
}
impl<P> AcceptedStatus<P>
where
P: AsRef<[u8]>,
{
pub fn serialise_into<W: Write>(&self, mut buf: W) -> Result<(), std::io::Error> {
match self {
Self::Success(p) => {
buf.write_u32::<BigEndian>(REPLY_SUCCESS)?;
buf.write_all(p.as_ref())
}
Self::ProgramUnavailable => buf.write_u32::<BigEndian>(REPLY_PROG_UNAVAIL),
Self::ProgramMismatch { low: l, high: h } => {
buf.write_u32::<BigEndian>(REPLY_PROG_MISMATCH)?;
buf.write_u32::<BigEndian>(*l)?;
buf.write_u32::<BigEndian>(*h)
}
Self::ProcedureUnavailable => buf.write_u32::<BigEndian>(REPLY_PROC_UNAVAIL),
Self::GarbageArgs => buf.write_u32::<BigEndian>(REPLY_GARBAGE_ARGS),
Self::SystemError => buf.write_u32::<BigEndian>(REPLY_SYSTEM_ERR),
}
}
pub fn serialised_len(&self) -> u32 {
let mut len = 0;
len += 4;
len += match self {
Self::Success(p) => p.as_ref().len() as u32,
Self::ProgramUnavailable => 0,
Self::ProgramMismatch { low: _l, high: _h } => 8,
Self::ProcedureUnavailable => 0,
Self::GarbageArgs => 0,
Self::SystemError => 0,
};
len
}
}
impl<'a> TryFrom<&'a [u8]> for AcceptedStatus<&'a [u8]> {
type Error = Error;
fn try_from(v: &'a [u8]) -> Result<Self, Self::Error> {
let mut c = Cursor::new(v);
AcceptedStatus::from_cursor(&mut c)
}
}
#[cfg(feature = "bytes")]
impl TryFrom<crate::Bytes> for AcceptedStatus<crate::Bytes> {
type Error = Error;
fn try_from(mut v: crate::Bytes) -> Result<Self, Self::Error> {
use crate::bytes_ext::BytesReaderExt;
let reply = match v.try_u32()? {
REPLY_SUCCESS => Self::Success(v),
REPLY_PROG_UNAVAIL => Self::ProgramUnavailable,
REPLY_PROG_MISMATCH => Self::ProgramMismatch {
low: v.try_u32()?,
high: v.try_u32()?,
},
REPLY_PROC_UNAVAIL => Self::ProcedureUnavailable,
REPLY_GARBAGE_ARGS => Self::GarbageArgs,
REPLY_SYSTEM_ERR => Self::SystemError,
v => return Err(Error::InvalidReplyStatus(v)),
};
Ok(reply)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_differing_payload_type() {
let binding = vec![42];
let auth = AuthFlavor::AuthNone(Some(binding.as_slice()));
let payload = [42, 42, 42, 42];
let _reply: AcceptedReply<&[u8], [u8; 4]> =
AcceptedReply::new(auth, AcceptedStatus::Success(payload));
}
}