1#![no_std]
6#![warn(missing_docs)]
7#![warn(clippy::cast_lossless)]
8#![warn(clippy::cast_possible_truncation)]
9#![cfg_attr(docsrs, feature(doc_auto_cfg))]
11
12#[cfg(feature = "std")]
13extern crate std;
14
15use core::{fmt, num::TryFromIntError};
16
17mod util;
18
19pub mod accounting;
20pub mod authentication;
21pub mod authorization;
22
23mod packet;
24use getset::CopyGetters;
25pub use packet::header::HeaderInfo;
26pub use packet::{Packet, PacketFlags, PacketType};
27
28mod arguments;
29pub use arguments::{Argument, Arguments, InvalidArgument};
30
31mod fields;
32pub use fields::*;
33
34mod text;
35pub use text::{FieldText, InvalidText};
36
37#[cfg(feature = "std")]
38mod owned;
39
40#[non_exhaustive]
42#[derive(Debug, PartialEq, Eq)]
43pub enum SerializeError {
44 NotEnoughSpace,
46
47 LengthOverflow,
49
50 LengthMismatch {
52 expected: usize,
54 actual: usize,
56 },
57}
58
59impl fmt::Display for SerializeError {
60 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
61 match self {
62 Self::NotEnoughSpace => write!(f, "not enough space in buffer"),
63 Self::LengthOverflow => write!(f, "field length overflowed"),
64 Self::LengthMismatch { expected, actual } => write!(
65 f,
66 "mismatch in number of bytes written: expected {expected}, actual {actual}"
67 ),
68 }
69 }
70}
71
72#[doc(hidden)]
73impl From<TryFromIntError> for SerializeError {
74 fn from(_value: TryFromIntError) -> Self {
75 Self::LengthOverflow
76 }
77}
78
79#[non_exhaustive]
81#[derive(Debug, PartialEq, Eq)]
82pub enum DeserializeError {
83 InvalidStatus(u8),
85
86 InvalidPacketType(u8),
88
89 InvalidHeaderFlags(u8),
91
92 InvalidBodyFlags(u8),
94
95 InvalidVersion(u8),
97
98 InvalidArgument(InvalidArgument),
100
101 PacketTypeMismatch {
103 expected: PacketType,
105
106 actual: PacketType,
108 },
109
110 BadText,
112
113 IncorrectUnencryptedFlag,
115
116 WrongBodyBufferSize {
118 expected: usize,
120 buffer_size: usize,
122 },
123
124 UnexpectedEnd,
126}
127
128impl fmt::Display for DeserializeError {
129 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
130 match self {
131 Self::InvalidStatus(num) => write!(f, "invalid status byte in raw packet: {num:#x}"),
132 Self::InvalidPacketType(num) => write!(f, "invalid packet type byte: {num:#x}"),
133 Self::InvalidHeaderFlags(num) => write!(f, "invalid header flags: {num:#x}"),
134 Self::InvalidBodyFlags(num) => write!(f, "invalid body flags: {num:#x}"),
135 Self::InvalidVersion(num) => write!(
136 f,
137 "invalid version number: major {:#x}, minor {:#x}",
138 num >> 4, num & 0b1111 ),
141 Self::InvalidArgument(reason) => write!(f, "invalid argument: {reason}"),
142 Self::BadText => write!(f, "text field was not printable ASCII"),
143 Self::IncorrectUnencryptedFlag => write!(f, "unencrypted flag had an incorrect value"),
144 Self::PacketTypeMismatch { expected, actual } => write!(f, "packet type mismatch: expected {expected:?} but got {actual:?}"),
145 Self::WrongBodyBufferSize { expected, buffer_size } => write!(f, "body buffer size didn't match length fields: expected {expected} bytes, but buffer was actually {buffer_size}"),
146 Self::UnexpectedEnd => write!(f, "unexpected end of buffer when deserializing object"),
147 }
148 }
149}
150
151#[cfg(feature = "std")]
153mod error_impls {
154 use std::error::Error;
155 use std::fmt;
156
157 use super::text::InvalidText;
158 use super::{DeserializeError, InvalidArgument, SerializeError};
159
160 impl Error for DeserializeError {}
161 impl Error for SerializeError {}
162 impl Error for InvalidArgument {}
163 impl Error for super::authentication::BadStart {}
164 impl Error for super::authentication::DataTooLong {}
165 impl<T> Error for InvalidText<T> where InvalidText<T>: fmt::Debug + fmt::Display {}
166}
167
168mod sealed {
171 use super::{accounting, authentication, authorization};
172 use super::{Packet, PacketBody};
173
174 pub trait Sealed {}
175
176 impl Sealed for authentication::Start<'_> {}
178 impl Sealed for authentication::Continue<'_> {}
179 impl Sealed for authentication::Reply<'_> {}
180
181 impl Sealed for authorization::Request<'_> {}
183 impl Sealed for authorization::Reply<'_> {}
184
185 impl Sealed for accounting::Request<'_> {}
187 impl Sealed for accounting::Reply<'_> {}
188
189 impl<B: PacketBody> Sealed for Packet<B> {}
191}
192
193#[repr(u8)]
195#[non_exhaustive]
196#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
197pub enum MajorVersion {
198 RFC8907 = 0xc,
200}
201
202impl fmt::Display for MajorVersion {
203 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
204 write!(
205 f,
206 "{}",
207 match self {
208 MajorVersion::RFC8907 => "RFC 8907",
209 }
210 )
211 }
212}
213
214#[repr(u8)]
216#[non_exhaustive]
217#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
218pub enum MinorVersion {
219 Default = 0x0,
221 V1 = 0x1,
223}
224
225impl fmt::Display for MinorVersion {
226 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
227 write!(
228 f,
229 "{}",
230 match self {
231 Self::Default => "default",
232 Self::V1 => "1",
233 }
234 )
235 }
236}
237
238#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, CopyGetters)]
240#[getset(get_copy = "pub")]
241pub struct Version {
242 major: MajorVersion,
244
245 minor: MinorVersion,
247}
248
249impl Version {
250 pub fn new(major: MajorVersion, minor: MinorVersion) -> Self {
252 Self { major, minor }
253 }
254}
255
256impl Default for Version {
257 fn default() -> Self {
258 Self {
259 major: MajorVersion::RFC8907,
260 minor: MinorVersion::Default,
261 }
262 }
263}
264
265impl fmt::Display for Version {
266 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
267 write!(f, "major {}, minor {}", self.major(), self.minor())
268 }
269}
270
271impl PartialOrd for Version {
272 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
273 Some(self.cmp(other))
274 }
275}
276
277impl Ord for Version {
278 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
279 self.major
281 .cmp(&other.major)
282 .then(self.minor.cmp(&other.minor))
283 }
284}
285
286impl TryFrom<u8> for Version {
287 type Error = DeserializeError;
288
289 fn try_from(value: u8) -> Result<Self, Self::Error> {
290 if value >> 4 == MajorVersion::RFC8907 as u8 {
292 let minor_version = match value & 0xf {
293 0 => Ok(MinorVersion::Default),
294 1 => Ok(MinorVersion::V1),
295 _ => Err(DeserializeError::InvalidVersion(value)),
296 }?;
297
298 Ok(Self {
299 major: MajorVersion::RFC8907,
300 minor: minor_version,
301 })
302 } else {
303 Err(DeserializeError::InvalidVersion(value))
304 }
305 }
306}
307
308impl From<Version> for u8 {
309 fn from(value: Version) -> Self {
310 ((value.major as u8) << 4) | (value.minor as u8 & 0xf)
311 }
312}
313
314pub trait PacketBody: sealed::Sealed {
320 const TYPE: PacketType;
322
323 const REQUIRED_FIELDS_LENGTH: usize;
325
326 fn required_minor_version(&self) -> Option<MinorVersion> {
330 None
331 }
332}
333
334#[doc(hidden)]
336pub trait Serialize: sealed::Sealed {
337 fn wire_size(&self) -> usize;
339
340 fn serialize_into_buffer(&self, buffer: &mut [u8]) -> Result<usize, SerializeError>;
342}
343
344#[doc(hidden)]
346pub trait Deserialize<'raw>: sealed::Sealed + Sized {
347 fn deserialize_from_buffer(buffer: &'raw [u8]) -> Result<Self, DeserializeError>;
349}