Skip to content

Commit e107d13

Browse files
author
Zane Othman
authored
Add owned variants of reply packet bodies (#5)
All owned packet bodies are currently crate-internal, but `ArgumentOwned` is publicly exposed.
1 parent 53e05ef commit e107d13

File tree

13 files changed

+304
-19
lines changed

13 files changed

+304
-19
lines changed

src/protocol.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ pub use packet::{Packet, PacketFlags, PacketType};
1313
mod arguments;
1414
pub use arguments::{Argument, Arguments, InvalidArgument};
1515

16+
#[cfg(feature = "std")]
17+
pub use arguments::ArgumentOwned;
18+
1619
mod fields;
1720
pub use fields::*;
1821

@@ -268,3 +271,16 @@ trait Serialize: sealed::Sealed {
268271
/// Serializes data into a buffer, returning the resulting length on success.
269272
fn serialize_into_buffer(&self, buffer: &mut [u8]) -> Result<usize, SerializeError>;
270273
}
274+
275+
/// Converts a reference-based packet to a packet that owns its fields.
276+
///
277+
/// A [`Borrow`](std::borrow::Borrow) impl for the different packet types would be nontrivial, if even possible,
278+
/// which is why the [`ToOwned`](std::borrow::ToOwned) trait isn't used.
279+
#[cfg(feature = "std")]
280+
pub(crate) trait ToOwnedBody: PacketBody {
281+
/// The resulting owned packet type.
282+
type Owned;
283+
284+
/// Converts the packet type with references to its data to one that owns its field data.
285+
fn to_owned(&self) -> Self::Owned;
286+
}

src/protocol/accounting.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ use crate::FieldText;
1414
#[cfg(test)]
1515
mod tests;
1616

17+
#[cfg(feature = "std")]
18+
pub(crate) mod owned;
19+
1720
bitflags! {
1821
/// Raw bitflags for accounting request packet.
1922
struct RawFlags: u8 {
@@ -204,7 +207,7 @@ pub struct Reply<'packet> {
204207

205208
/// Gets the administrative/log data received from the server.
206209
#[getset(get_copy = "pub")]
207-
data: &'packet [u8],
210+
data: FieldText<'packet>,
208211
}
209212

210213
/// Field lengths of a reply packet as well as the total length.
@@ -284,7 +287,10 @@ impl<'raw> TryFrom<&'raw [u8]> for Reply<'raw> {
284287
let server_message =
285288
FieldText::try_from(&buffer[Self::SERVER_MESSAGE_OFFSET..data_offset])
286289
.map_err(|_| DeserializeError::BadText)?;
287-
let data = &buffer[data_offset..data_offset + extracted_lengths.data_length as usize];
290+
let data = FieldText::try_from(
291+
&buffer[data_offset..data_offset + extracted_lengths.data_length as usize],
292+
)
293+
.map_err(|_| DeserializeError::BadText)?;
288294

289295
Ok(Self {
290296
status,

src/protocol/accounting/owned.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
use std::borrow::ToOwned;
2+
use std::string::String;
3+
4+
use super::{Reply, Status};
5+
use crate::protocol::ToOwnedBody;
6+
7+
/// An owned version of a [`Reply`](super::Reply).
8+
// TODO: stop ignoring dead_code lint when fields are actually used in client
9+
#[allow(dead_code)]
10+
pub(crate) struct ReplyOwned {
11+
/// The status returned by the server.
12+
pub(crate) status: Status,
13+
14+
// TODO: string or separate FieldTextOwned (?) type?
15+
/// The message to display to the user.
16+
pub(crate) server_message: String,
17+
18+
/// The console/administrative message from the server.
19+
pub(crate) data: String,
20+
}
21+
22+
impl ToOwnedBody for Reply<'_> {
23+
type Owned = ReplyOwned;
24+
25+
fn to_owned(&self) -> Self::Owned {
26+
ReplyOwned {
27+
status: self.status,
28+
server_message: self.server_message.as_ref().to_owned(),
29+
data: self.data.as_ref().to_owned(),
30+
}
31+
}
32+
}

src/protocol/accounting/tests.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -152,25 +152,25 @@ fn serialize_full_request_packet() {
152152

153153
#[test]
154154
fn deserialize_reply_all_fields() {
155-
let mut body_raw = array_vec!([u8; 60]);
155+
let mut body_raw = array_vec!([u8; 70]);
156156

157157
body_raw.extend_from_slice(&[
158158
0, 47, // server message length
159-
0, 2, // data length,
159+
0, 9, // data length,
160160
0x02, // status: error
161161
]);
162162

163163
let server_message = [b'A'; 47];
164164
body_raw.extend_from_slice(&server_message);
165165

166166
// data
167-
body_raw.extend_from_slice(&[0xa4, 0x42]);
167+
body_raw.extend_from_slice(b"some data");
168168

169169
assert_eq!(
170170
Ok(Reply {
171171
status: Status::Error,
172172
server_message: FieldText::try_from(server_message.as_slice()).unwrap(),
173-
data: &[0xa4, 0x42]
173+
data: FieldText::assert("some data")
174174
}),
175175
body_raw.as_slice().try_into()
176176
);
@@ -212,7 +212,7 @@ fn deserialize_full_reply_packet() {
212212
let expected_body = Reply {
213213
status: Status::Error,
214214
server_message: FieldText::assert("hello"),
215-
data: b"fifteen letters",
215+
data: FieldText::assert("fifteen letters"),
216216
};
217217

218218
let expected_packet = Packet::new(expected_header, expected_body);

src/protocol/arguments.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,12 @@ use crate::FieldText;
99
#[cfg(test)]
1010
mod tests;
1111

12+
#[cfg(feature = "std")]
13+
mod owned;
14+
15+
#[cfg(feature = "std")]
16+
pub use owned::ArgumentOwned;
17+
1218
/// An argument in the TACACS+ protocol, which exists for extensibility.
1319
#[derive(Clone, Copy, Default, PartialEq, Eq, Debug, CopyGetters)]
1420
pub struct Argument<'data> {

src/protocol/arguments/owned.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
use std::borrow::ToOwned;
2+
use std::string::String;
3+
4+
use super::Argument;
5+
6+
/// An argument that owns its name and value.
7+
#[derive(Debug, PartialEq, Eq)]
8+
pub struct ArgumentOwned {
9+
/// The name of the argument.
10+
pub name: String,
11+
12+
/// The value of the argument.
13+
pub value: String,
14+
15+
/// Whether this argument is required.
16+
pub required: bool,
17+
}
18+
19+
impl Argument<'_> {
20+
/// Converts this `Argument` to one which owns its fields.
21+
pub fn to_owned(&self) -> ArgumentOwned {
22+
ArgumentOwned {
23+
name: self.name.as_ref().to_owned(),
24+
value: self.value.as_ref().to_owned(),
25+
required: self.required,
26+
}
27+
}
28+
}

src/protocol/arguments/tests.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,3 +157,22 @@ fn deserialize_both_delims_star_first() {
157157
})
158158
);
159159
}
160+
161+
#[cfg(feature = "std")]
162+
#[test]
163+
fn argument_to_owned_impl() {
164+
use std::string::String;
165+
166+
let argument_unowned =
167+
Argument::new(FieldText::assert("name"), FieldText::assert("value"), true)
168+
.expect("argument should have been valid");
169+
170+
assert_eq!(
171+
argument_unowned.to_owned(),
172+
ArgumentOwned {
173+
name: String::from("name"),
174+
value: String::from("value"),
175+
required: true
176+
}
177+
);
178+
}

src/protocol/authentication.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ use crate::FieldText;
1616
#[cfg(test)]
1717
mod tests;
1818

19+
#[cfg(feature = "std")]
20+
pub(crate) mod owned;
21+
1922
/// The authentication action, as indicated upon initiation of an authentication session.
2023
#[repr(u8)]
2124
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
@@ -256,7 +259,7 @@ impl Serialize for Start<'_> {
256259

257260
/// Flags received in an authentication reply packet.
258261
#[repr(transparent)]
259-
#[derive(Debug, PartialEq, Eq)]
262+
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
260263
pub struct ReplyFlags(u8);
261264

262265
impl ReplyFlags {

src/protocol/authentication/owned.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
use std::borrow::ToOwned;
2+
use std::string::String;
3+
use std::vec::Vec;
4+
5+
use crate::protocol::ToOwnedBody;
6+
7+
use super::Reply;
8+
use super::{ReplyFlags, Status};
9+
10+
/// An authentication reply packet with owned fields.
11+
// TODO: stop ignoring dead_code lint when fields are actually used
12+
#[allow(dead_code)]
13+
pub(crate) struct ReplyOwned {
14+
/// The status, as returned by the server.
15+
pub(crate) status: Status,
16+
17+
/// The flags set in the server response.
18+
pub(crate) flags: ReplyFlags,
19+
20+
/// The message to be displayed to the user.
21+
pub(crate) server_message: String,
22+
23+
/// The domain-specific data included in the reply.
24+
pub(crate) data: Vec<u8>,
25+
}
26+
27+
impl ToOwnedBody for Reply<'_> {
28+
type Owned = ReplyOwned;
29+
30+
fn to_owned(&self) -> Self::Owned {
31+
ReplyOwned {
32+
status: self.status,
33+
flags: self.flags,
34+
server_message: self.server_message.as_ref().to_owned(),
35+
data: self.data.to_owned(),
36+
}
37+
}
38+
}

src/protocol/authorization.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ use crate::FieldText;
1313
#[cfg(test)]
1414
mod tests;
1515

16+
#[cfg(feature = "std")]
17+
pub(crate) mod owned;
18+
1619
/// An authorization request packet body, including arguments.
1720
pub struct Request<'packet> {
1821
/// Method used to authenticate to TACACS+ client.
@@ -159,9 +162,9 @@ pub struct Reply<'packet> {
159162
#[getset(get_copy = "pub")]
160163
server_message: FieldText<'packet>,
161164

162-
/// Gets the administrative/log data returned from the server.
165+
/// Gets the administrative log message returned from the server.
163166
#[getset(get_copy = "pub")]
164-
data: &'packet [u8],
167+
data: FieldText<'packet>,
165168

166169
// this field not publicly exposed on purpose
167170
// (used for iterating over arguments)
@@ -323,7 +326,8 @@ impl<'raw> TryFrom<&'raw [u8]> for Reply<'raw> {
323326

324327
let server_message = FieldText::try_from(&buffer[body_start..data_start])
325328
.map_err(|_| DeserializeError::BadText)?;
326-
let data = &buffer[data_start..arguments_start];
329+
let data = FieldText::try_from(&buffer[data_start..arguments_start])
330+
.map_err(|_| DeserializeError::BadText)?;
327331

328332
// arguments occupy the rest of the buffer
329333
let argument_lengths = &buffer[Self::ARGUMENT_LENGTHS_START..body_start];

0 commit comments

Comments
 (0)