Skip to content

Commit 06b1fc2

Browse files
committed
Implement packet decoding with nom
1 parent cdfd43c commit 06b1fc2

File tree

4 files changed

+137
-151
lines changed

4 files changed

+137
-151
lines changed

Cargo.toml

+1-4
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,4 @@ edition = "2018"
77
[dependencies]
88
failure = "0.1"
99
bytes = "0.4.12"
10-
memchr = "2.2.0"
11-
12-
num-traits = "0.2"
13-
num-derive = "0.2"
10+
nom = "4.2.3"

src/error.rs

+11-14
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,24 @@ pub use std::result::Result as StdResult;
22

33
use failure::{AsFail, Backtrace, Causes, Context, Fail};
44
use std::fmt;
5-
use std::sync::Arc;
65

76
pub type Result<T> = StdResult<T, Error>;
87

9-
#[derive(Debug, Clone)]
8+
#[derive(Debug)]
109
pub struct Error {
1110
inner: Context<ErrorKind>,
1211
}
1312

1413
#[derive(Debug, Fail, Clone, PartialEq)]
1514
pub enum ErrorKind {
16-
#[fail(display = "Decode error: {}", _0)]
17-
DecodeError(&'static str),
15+
#[fail(display = "Invalid mode")]
16+
InvalidMode,
17+
18+
#[fail(display = "Invalid packet")]
19+
InvalidPacket,
20+
21+
#[fail(display = "Packet too large")]
22+
PacketTooLarge,
1823
}
1924

2025
impl Error {
@@ -52,21 +57,13 @@ impl PartialEq for Error {
5257
impl From<ErrorKind> for Error {
5358
fn from(kind: ErrorKind) -> Error {
5459
Error {
55-
inner: Arc::new(Context::new(kind)),
60+
inner: Context::new(kind),
5661
}
5762
}
5863
}
5964

6065
impl From<Context<ErrorKind>> for Error {
61-
fn from(context: Context<ErrorKind>) -> Error {
62-
Error {
63-
inner: Arc::new(context),
64-
}
65-
}
66-
}
67-
68-
impl From<Arc<Context<ErrorKind>>> for Error {
69-
fn from(inner: Arc<Context<ErrorKind>>) -> Error {
66+
fn from(inner: Context<ErrorKind>) -> Error {
7067
Error {
7168
inner,
7269
}

src/lib.rs

+3-8
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,8 @@
1+
#[macro_use]
2+
extern crate nom;
3+
14
mod error;
25
mod packet;
36

47
pub use crate::error::*;
58
pub use crate::packet::*;
6-
7-
#[cfg(test)]
8-
mod tests {
9-
#[test]
10-
fn it_works() {
11-
assert_eq!(2 + 2, 4);
12-
}
13-
}

src/packet.rs

+122-125
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,20 @@
1-
// Clippy gives us a false possitive on FromPrimitive/ToPrimitive derives.
2-
// This is fixed on rust-clippy#3932.
3-
// TODO: remove this on the next clippy release.
4-
#![allow(clippy::useless_attribute)]
5-
6-
use bytes::Buf;
7-
use failure::ResultExt;
8-
use memchr::memchr;
9-
use num_derive::{FromPrimitive, ToPrimitive};
10-
use num_traits::{FromPrimitive, ToPrimitive};
11-
use std::io::Cursor;
1+
// clippy finds redundant closures in nom macros.
2+
// allow this until it is fixed in nom.
3+
#![allow(clippy::redundant_closure)]
4+
5+
use bytes::BufMut;
6+
use nom::be_u16;
127
use std::str::{self, FromStr};
138

149
use crate::error::*;
1510

16-
#[derive(Debug)]
11+
const RRQ: u16 = 1;
12+
const WRQ: u16 = 2;
13+
const DATA: u16 = 3;
14+
const ACK: u16 = 4;
15+
const ERROR: u16 = 5;
16+
17+
#[derive(Debug, PartialEq)]
1718
pub enum Packet {
1819
Rrq(String, Mode),
1920
Wrq(String, Mode),
@@ -22,20 +23,120 @@ pub enum Packet {
2223
Error(u16, String),
2324
}
2425

25-
#[derive(Debug)]
26+
#[derive(Debug, PartialEq)]
2627
pub enum Mode {
2728
Netascii,
2829
Octet,
2930
Mail,
3031
}
3132

32-
#[derive(Debug, FromPrimitive, ToPrimitive)]
33-
pub enum OpCode {
34-
Rrq = 1,
35-
Wrq = 2,
36-
Data = 3,
37-
Ack = 4,
38-
Error = 5,
33+
fn take_512_max(i: &[u8]) -> nom::IResult<&[u8], &[u8]> {
34+
if i.len() <= 512 {
35+
Ok((&[], i))
36+
} else {
37+
Ok((&i[512..], &i[..512]))
38+
}
39+
}
40+
41+
named!(nul_string<&[u8], &str>,
42+
do_parse!(
43+
s: map_res!(take_till!(|ch| ch == b'\0'), str::from_utf8) >>
44+
take!(1) >>
45+
46+
(s)
47+
)
48+
);
49+
50+
named!(filename_mode<&[u8], (&str, Mode)>,
51+
do_parse!(
52+
filename: nul_string >>
53+
mode: alt!(
54+
tag_no_case!("netascii") |
55+
tag_no_case!("octet") |
56+
tag_no_case!("mail")
57+
) >>
58+
tag!("\0") >>
59+
60+
({
61+
let mode = str::from_utf8(mode).unwrap();
62+
let mode = Mode::from_str(mode).unwrap();
63+
(filename, mode)
64+
})
65+
)
66+
);
67+
68+
named!(rrq<&[u8], Packet>,
69+
do_parse!(
70+
fm: filename_mode >>
71+
72+
({
73+
let (filename, mode) = fm;
74+
Packet::Rrq(filename.to_owned(), mode)
75+
})
76+
)
77+
);
78+
79+
named!(wrq<&[u8], Packet>,
80+
do_parse!(
81+
fm: filename_mode >>
82+
83+
({
84+
let (filename, mode) = fm;
85+
Packet::Wrq(filename.to_owned(), mode)
86+
})
87+
)
88+
);
89+
90+
named!(data<&[u8], Packet>,
91+
do_parse!(
92+
block: be_u16 >>
93+
data: take_512_max >>
94+
95+
(Packet::Data(block, data.to_vec()))
96+
)
97+
);
98+
99+
named!(ack<&[u8], Packet>,
100+
do_parse!(
101+
block: be_u16 >>
102+
103+
(Packet::Ack(block))
104+
)
105+
);
106+
107+
named!(error<&[u8], Packet>,
108+
do_parse!(
109+
code: be_u16 >>
110+
msg: nul_string >>
111+
112+
(Packet::Error(code, msg.to_owned()))
113+
)
114+
);
115+
116+
named!(packet<&[u8], Packet>,
117+
do_parse!(
118+
packet: switch!(be_u16,
119+
RRQ => call!(rrq) |
120+
WRQ => call!(wrq) |
121+
DATA => call!(data) |
122+
ACK => call!(ack) |
123+
ERROR => call!(error)
124+
) >>
125+
126+
(packet)
127+
)
128+
);
129+
130+
impl Packet {
131+
pub fn from_bytes(data: &[u8]) -> Result<Packet> {
132+
let (rest, p) = packet(data).map_err(|_| Error::from(ErrorKind::InvalidPacket))?;
133+
134+
if rest.is_empty() {
135+
Ok(p)
136+
} else {
137+
Err(ErrorKind::PacketTooLarge.into())
138+
}
139+
}
39140
}
40141

41142
impl Mode {
@@ -56,111 +157,7 @@ impl FromStr for Mode {
56157
"netascii" => Ok(Mode::Netascii),
57158
"octet" => Ok(Mode::Octet),
58159
"mail" => Ok(Mode::Mail),
59-
_ => Err(ErrorKind::DecodeError("Invalid mode").into()),
160+
_ => Err(ErrorKind::InvalidMode.into()),
60161
}
61162
}
62163
}
63-
64-
fn read_string(buf: &mut Cursor<&[u8]>) -> Result<String> {
65-
let b = buf.bytes();
66-
67-
let pos =
68-
memchr(0, b).ok_or_else(|| Error::from(ErrorKind::DecodeError("No string ending")))?;
69-
70-
let s = str::from_utf8(&b[..pos])
71-
.context(ErrorKind::DecodeError("Invalid UTF-8 string"))?
72-
.to_string();
73-
74-
buf.advance(pos + 1);
75-
76-
Ok(s)
77-
}
78-
79-
impl Packet {
80-
pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
81-
let mut buf = Cursor::new(bytes);
82-
83-
if buf.remaining() < 2 {
84-
return Err(ErrorKind::DecodeError("Insufficient packet length").into());
85-
}
86-
87-
match FromPrimitive::from_u16(buf.get_u16_be()) {
88-
Some(OpCode::Rrq) => {
89-
let filename = read_string(&mut buf)?;
90-
let mode = Mode::from_str(read_string(&mut buf)?.as_str())?;
91-
Ok(Packet::Rrq(filename, mode))
92-
}
93-
Some(OpCode::Wrq) => {
94-
let filename = read_string(&mut buf)?;
95-
let mode = Mode::from_str(read_string(&mut buf)?.as_str())?;
96-
Ok(Packet::Wrq(filename, mode))
97-
}
98-
Some(OpCode::Data) => {
99-
if buf.remaining() < 2 {
100-
return Err(ErrorKind::DecodeError("Insufficient packet length").into());
101-
}
102-
let block_nr = buf.get_u16_be();
103-
let data = buf.collect();
104-
Ok(Packet::Data(block_nr, data))
105-
}
106-
Some(OpCode::Ack) => {
107-
if buf.remaining() < 2 {
108-
return Err(ErrorKind::DecodeError("Insufficient packet length").into());
109-
}
110-
let block_nr = buf.get_u16_be();
111-
Ok(Packet::Ack(block_nr))
112-
}
113-
Some(OpCode::Error) => {
114-
if buf.remaining() < 2 {
115-
return Err(ErrorKind::DecodeError("Insufficient packet length").into());
116-
}
117-
let code = buf.get_u16_be();
118-
let msg = read_string(&mut buf)?;
119-
Ok(Packet::Error(code, msg))
120-
}
121-
None => Err(ErrorKind::DecodeError("Invalid opcode").into()),
122-
}
123-
}
124-
125-
pub fn to_bytes(&self) -> Option<Vec<u8>> {
126-
let mut buf = Vec::new();
127-
128-
match self {
129-
Packet::Rrq(filename, mode) => {
130-
let opcode = ToPrimitive::to_u16(&OpCode::Rrq)?.to_be_bytes();
131-
buf.extend_from_slice(&opcode[..]);
132-
buf.extend_from_slice(filename.as_bytes());
133-
buf.push(0);
134-
buf.extend_from_slice(mode.to_str().as_bytes());
135-
buf.push(0);
136-
Some(buf)
137-
}
138-
Packet::Wrq(filename, mode) => {
139-
let opcode = ToPrimitive::to_u16(&OpCode::Wrq)?.to_be_bytes();
140-
buf.extend_from_slice(&opcode[..]);
141-
buf.extend_from_slice(filename.as_bytes());
142-
buf.push(0);
143-
buf.extend_from_slice(mode.to_str().as_bytes());
144-
buf.push(0);
145-
Some(buf)
146-
}
147-
_ => unimplemented!(),
148-
}
149-
}
150-
}
151-
152-
#[cfg(test)]
153-
mod tests {
154-
use super::*;
155-
use bytes::IntoBuf;
156-
157-
#[test]
158-
fn check() {
159-
let mut b = b"abc\0def\0".into_buf();
160-
161-
assert_eq!(read_string(&mut b), Ok("abc".to_string()));
162-
assert_eq!(read_string(&mut b), Ok("def".to_string()));
163-
164-
//let b = Packet::from_buf(vec![1u8]).expect("XXX");
165-
}
166-
}

0 commit comments

Comments
 (0)