|
1 |
| -use nom::branch::alt; |
2 |
| -use nom::bytes::complete::{tag, tag_no_case, take_till}; |
3 |
| -use nom::combinator::{map, map_opt, map_res, rest}; |
4 |
| -use nom::multi::many0; |
5 |
| -use nom::number::complete::be_u16; |
6 |
| -use nom::sequence::tuple; |
7 |
| -use nom::IResult; |
| 1 | +use std::convert::TryInto; |
8 | 2 | use std::str::{self, FromStr};
|
9 | 3 |
|
10 |
| -use crate::error::Result; |
11 |
| -use crate::packet::{self, *}; |
12 |
| - |
13 |
| -#[derive(Debug)] |
14 |
| -enum Opt<'a> { |
15 |
| - BlkSize(u16), |
16 |
| - Timeout(u8), |
17 |
| - Tsize(u64), |
18 |
| - Invalid(&'a str, &'a str), |
19 |
| -} |
| 4 | +use crate::error::{Error, Result}; |
| 5 | +use crate::packet::{ |
| 6 | + Error as PacketError, Mode, Opts, Packet, PacketType, RwReq, |
| 7 | +}; |
20 | 8 |
|
21 | 9 | pub(crate) fn parse_packet(input: &[u8]) -> Result<Packet> {
|
22 |
| - let (rest, packet) = match parse_packet_type(input)? { |
23 |
| - (data, PacketType::Rrq) => parse_rrq(data)?, |
24 |
| - (data, PacketType::Wrq) => parse_wrq(data)?, |
25 |
| - (data, PacketType::Data) => parse_data(data)?, |
26 |
| - (data, PacketType::Ack) => parse_ack(data)?, |
27 |
| - (data, PacketType::Error) => parse_error(data)?, |
28 |
| - (data, PacketType::OAck) => parse_oack(data)?, |
29 |
| - }; |
30 |
| - |
31 |
| - if rest.is_empty() { |
32 |
| - Ok(packet) |
33 |
| - } else { |
34 |
| - Err(crate::Error::InvalidPacket) |
35 |
| - } |
36 |
| -} |
37 |
| - |
38 |
| -fn nul_str(input: &[u8]) -> IResult<&[u8], &str> { |
39 |
| - map_res( |
40 |
| - tuple((take_till(|c| c == b'\0'), tag(b"\0"))), |
41 |
| - |(s, _): (&[u8], _)| str::from_utf8(s), |
42 |
| - )(input) |
| 10 | + parse_packet_type(input) |
| 11 | + .and_then(|(packet_type, data)| match packet_type { |
| 12 | + PacketType::Rrq => parse_rrq(data), |
| 13 | + PacketType::Wrq => parse_wrq(data), |
| 14 | + PacketType::Data => parse_data(data), |
| 15 | + PacketType::Ack => parse_ack(data), |
| 16 | + PacketType::Error => parse_error(data), |
| 17 | + PacketType::OAck => parse_oack(data), |
| 18 | + }) |
| 19 | + .ok_or(Error::InvalidPacket) |
43 | 20 | }
|
44 | 21 |
|
45 |
| -fn parse_packet_type(input: &[u8]) -> IResult<&[u8], PacketType> { |
46 |
| - map_opt(be_u16, PacketType::from_u16)(input) |
| 22 | +fn parse_nul_str(input: &[u8]) -> Option<(&str, &[u8])> { |
| 23 | + let pos = input.iter().position(|c| *c == b'\0')?; |
| 24 | + let s = str::from_utf8(&input[..pos]).ok()?; |
| 25 | + Some((s, &input[pos + 1..])) |
47 | 26 | }
|
48 | 27 |
|
49 |
| -fn parse_mode(input: &[u8]) -> IResult<&[u8], Mode> { |
50 |
| - alt(( |
51 |
| - map(tag_no_case(b"netascii\0"), |_| Mode::Netascii), |
52 |
| - map(tag_no_case(b"octet\0"), |_| Mode::Octet), |
53 |
| - map(tag_no_case(b"mail\0"), |_| Mode::Mail), |
54 |
| - ))(input) |
| 28 | +fn parse_u16_be(input: &[u8]) -> Option<(u16, &[u8])> { |
| 29 | + let bytes = input.get(..2)?; |
| 30 | + let num = u16::from_be_bytes(bytes.try_into().ok()?); |
| 31 | + Some((num, &input[2..])) |
55 | 32 | }
|
56 | 33 |
|
57 |
| -fn parse_opt_blksize(input: &[u8]) -> IResult<&[u8], Opt> { |
58 |
| - map_opt(tuple((tag_no_case(b"blksize\0"), nul_str)), |(_, n): (_, &str)| { |
59 |
| - u16::from_str(n) |
60 |
| - .ok() |
61 |
| - .filter(|n| *n >= 8 && *n <= 65464) |
62 |
| - .map(Opt::BlkSize) |
63 |
| - })(input) |
| 34 | +fn parse_packet_type(input: &[u8]) -> Option<(PacketType, &[u8])> { |
| 35 | + let (num, rest) = parse_u16_be(input)?; |
| 36 | + let val = PacketType::from_u16(num)?; |
| 37 | + Some((val, rest)) |
64 | 38 | }
|
65 | 39 |
|
66 |
| -fn parse_opt_timeout(input: &[u8]) -> IResult<&[u8], Opt> { |
67 |
| - map_opt(tuple((tag_no_case(b"timeout\0"), nul_str)), |(_, n): (_, &str)| { |
68 |
| - u8::from_str(n).ok().filter(|n| *n >= 1).map(Opt::Timeout) |
69 |
| - })(input) |
70 |
| -} |
| 40 | +fn parse_mode(input: &[u8]) -> Option<(Mode, &[u8])> { |
| 41 | + let (s, rest) = parse_nul_str(input)?; |
71 | 42 |
|
72 |
| -fn parse_opt_tsize(input: &[u8]) -> IResult<&[u8], Opt> { |
73 |
| - map_opt(tuple((tag_no_case(b"tsize\0"), nul_str)), |(_, n): (_, &str)| { |
74 |
| - u64::from_str(n).ok().map(Opt::Tsize) |
75 |
| - })(input) |
76 |
| -} |
| 43 | + let mode = if s.eq_ignore_ascii_case("netascii") { |
| 44 | + Mode::Netascii |
| 45 | + } else if s.eq_ignore_ascii_case("octet") { |
| 46 | + Mode::Octet |
| 47 | + } else if s.eq_ignore_ascii_case("mail") { |
| 48 | + Mode::Mail |
| 49 | + } else { |
| 50 | + return None; |
| 51 | + }; |
77 | 52 |
|
78 |
| -pub(crate) fn parse_opts(input: &[u8]) -> IResult<&[u8], Opts> { |
79 |
| - many0(alt(( |
80 |
| - parse_opt_blksize, |
81 |
| - parse_opt_timeout, |
82 |
| - parse_opt_tsize, |
83 |
| - map(tuple((nul_str, nul_str)), |(k, v)| Opt::Invalid(k, v)), |
84 |
| - )))(input) |
85 |
| - .map(|(i, opt_vec)| (i, to_opts(opt_vec))) |
| 53 | + Some((mode, rest)) |
86 | 54 | }
|
87 | 55 |
|
88 |
| -fn to_opts(opt_vec: Vec<Opt>) -> Opts { |
| 56 | +pub(crate) fn parse_opts(mut input: &[u8]) -> Option<Opts> { |
89 | 57 | let mut opts = Opts::default();
|
90 | 58 |
|
91 |
| - for opt in opt_vec { |
92 |
| - match opt { |
93 |
| - Opt::BlkSize(size) => { |
94 |
| - if opts.block_size.is_none() { |
95 |
| - opts.block_size.replace(size); |
| 59 | + while !input.is_empty() { |
| 60 | + let (name, rest) = parse_nul_str(input)?; |
| 61 | + let (val, rest) = parse_nul_str(rest)?; |
| 62 | + |
| 63 | + if name.eq_ignore_ascii_case("blksize") { |
| 64 | + if let Ok(val) = u16::from_str(val) { |
| 65 | + if val >= 8 && val <= 65464 { |
| 66 | + opts.block_size = Some(val); |
96 | 67 | }
|
97 | 68 | }
|
98 |
| - Opt::Timeout(timeout) => { |
99 |
| - if opts.timeout.is_none() { |
100 |
| - opts.timeout.replace(timeout); |
| 69 | + } else if name.eq_ignore_ascii_case("timeout") { |
| 70 | + if let Ok(val) = u8::from_str(val) { |
| 71 | + if val >= 1 { |
| 72 | + opts.timeout = Some(val); |
101 | 73 | }
|
102 | 74 | }
|
103 |
| - Opt::Tsize(size) => { |
104 |
| - if opts.transfer_size.is_none() { |
105 |
| - opts.transfer_size.replace(size); |
106 |
| - } |
| 75 | + } else if name.eq_ignore_ascii_case("tsize") { |
| 76 | + if let Ok(val) = u64::from_str(val) { |
| 77 | + opts.transfer_size = Some(val); |
107 | 78 | }
|
108 |
| - Opt::Invalid(..) => {} |
109 | 79 | }
|
| 80 | + |
| 81 | + input = rest; |
110 | 82 | }
|
111 | 83 |
|
112 |
| - opts |
| 84 | + Some(opts) |
| 85 | +} |
| 86 | + |
| 87 | +fn parse_rrq(input: &[u8]) -> Option<Packet> { |
| 88 | + let (filename, rest) = parse_nul_str(input)?; |
| 89 | + let (mode, rest) = parse_mode(rest)?; |
| 90 | + let opts = parse_opts(rest)?; |
| 91 | + |
| 92 | + Some(Packet::Rrq(RwReq { |
| 93 | + filename: filename.to_owned(), |
| 94 | + mode, |
| 95 | + opts, |
| 96 | + })) |
113 | 97 | }
|
114 | 98 |
|
115 |
| -fn parse_rrq(input: &[u8]) -> IResult<&[u8], Packet> { |
116 |
| - let (input, (filename, mode, opts)) = |
117 |
| - tuple((nul_str, parse_mode, parse_opts))(input)?; |
118 |
| - |
119 |
| - Ok(( |
120 |
| - input, |
121 |
| - Packet::Rrq(RwReq { |
122 |
| - filename: filename.to_owned(), |
123 |
| - mode, |
124 |
| - opts, |
125 |
| - }), |
126 |
| - )) |
| 99 | +fn parse_wrq(input: &[u8]) -> Option<Packet> { |
| 100 | + let (filename, rest) = parse_nul_str(input)?; |
| 101 | + let (mode, rest) = parse_mode(rest)?; |
| 102 | + let opts = parse_opts(rest)?; |
| 103 | + |
| 104 | + Some(Packet::Wrq(RwReq { |
| 105 | + filename: filename.to_owned(), |
| 106 | + mode, |
| 107 | + opts, |
| 108 | + })) |
127 | 109 | }
|
128 | 110 |
|
129 |
| -fn parse_wrq(input: &[u8]) -> IResult<&[u8], Packet> { |
130 |
| - let (input, (filename, mode, opts)) = |
131 |
| - tuple((nul_str, parse_mode, parse_opts))(input)?; |
132 |
| - |
133 |
| - Ok(( |
134 |
| - input, |
135 |
| - Packet::Wrq(RwReq { |
136 |
| - filename: filename.to_owned(), |
137 |
| - mode, |
138 |
| - opts, |
139 |
| - }), |
140 |
| - )) |
| 111 | +fn parse_data(input: &[u8]) -> Option<Packet> { |
| 112 | + let (block_nr, rest) = parse_u16_be(input)?; |
| 113 | + Some(Packet::Data(block_nr, rest)) |
141 | 114 | }
|
142 | 115 |
|
143 |
| -fn parse_data(input: &[u8]) -> IResult<&[u8], Packet> { |
144 |
| - tuple((be_u16, rest))(input) |
145 |
| - .map(|(i, (block_nr, data))| (i, Packet::Data(block_nr, data))) |
| 116 | +fn parse_ack(input: &[u8]) -> Option<Packet> { |
| 117 | + let (block_nr, rest) = parse_u16_be(input)?; |
| 118 | + |
| 119 | + if !rest.is_empty() { |
| 120 | + return None; |
| 121 | + } |
| 122 | + |
| 123 | + Some(Packet::Ack(block_nr)) |
146 | 124 | }
|
147 | 125 |
|
148 |
| -fn parse_ack(input: &[u8]) -> IResult<&[u8], Packet> { |
149 |
| - be_u16(input).map(|(i, block_nr)| (i, Packet::Ack(block_nr))) |
| 126 | +fn parse_error(input: &[u8]) -> Option<Packet> { |
| 127 | + let (code, rest) = parse_u16_be(input)?; |
| 128 | + let (msg, rest) = parse_nul_str(rest)?; |
| 129 | + |
| 130 | + if !rest.is_empty() { |
| 131 | + return None; |
| 132 | + } |
| 133 | + |
| 134 | + Some(Packet::Error(PacketError::from_code(code, Some(msg)))) |
150 | 135 | }
|
151 | 136 |
|
152 |
| -fn parse_error(input: &[u8]) -> IResult<&[u8], Packet> { |
153 |
| - tuple((be_u16, nul_str))(input).map(|(i, (code, msg))| { |
154 |
| - (i, packet::Error::from_code(code, Some(msg)).into()) |
155 |
| - }) |
| 137 | +fn parse_oack(input: &[u8]) -> Option<Packet> { |
| 138 | + let opts = parse_opts(input)?; |
| 139 | + Some(Packet::OAck(opts)) |
156 | 140 | }
|
157 | 141 |
|
158 |
| -fn parse_oack(input: &[u8]) -> IResult<&[u8], Packet> { |
159 |
| - parse_opts(input).map(|(i, opts)| (i, Packet::OAck(opts))) |
| 142 | +#[cfg(test)] |
| 143 | +mod tests { |
| 144 | + use super::*; |
| 145 | + |
| 146 | + #[test] |
| 147 | + fn nul_str() { |
| 148 | + let (s, rest) = parse_nul_str(b"123\0").unwrap(); |
| 149 | + assert_eq!(s, "123"); |
| 150 | + assert!(rest.is_empty()); |
| 151 | + |
| 152 | + let (s, rest) = parse_nul_str(b"123\0\0").unwrap(); |
| 153 | + assert_eq!(s, "123"); |
| 154 | + assert_eq!(rest, b"\0"); |
| 155 | + |
| 156 | + let (s1, rest) = parse_nul_str(b"123\0abc\0\xff\xff").unwrap(); |
| 157 | + let (s2, rest) = parse_nul_str(rest).unwrap(); |
| 158 | + assert_eq!(s1, "123"); |
| 159 | + assert_eq!(s2, "abc"); |
| 160 | + assert_eq!(rest, b"\xff\xff"); |
| 161 | + |
| 162 | + let (s1, rest) = parse_nul_str(b"\0\0").unwrap(); |
| 163 | + let (s2, rest) = parse_nul_str(rest).unwrap(); |
| 164 | + assert_eq!(s1, ""); |
| 165 | + assert_eq!(s2, ""); |
| 166 | + assert!(rest.is_empty()); |
| 167 | + |
| 168 | + assert!(parse_nul_str(b"").is_none()); |
| 169 | + assert!(parse_nul_str(b"123").is_none()); |
| 170 | + assert!(parse_nul_str(b"123\xff\xff\0").is_none()); |
| 171 | + } |
| 172 | + |
| 173 | + #[test] |
| 174 | + fn u16_be() { |
| 175 | + let (n, rest) = parse_u16_be(b"\x11\x22").unwrap(); |
| 176 | + assert_eq!(n, 0x1122); |
| 177 | + assert!(rest.is_empty()); |
| 178 | + |
| 179 | + let (n, rest) = parse_u16_be(b"\x11\x22\x33").unwrap(); |
| 180 | + assert_eq!(n, 0x1122); |
| 181 | + assert_eq!(rest, b"\x33"); |
| 182 | + |
| 183 | + let (n1, rest) = parse_u16_be(b"\x11\x22\x33\x44\x55").unwrap(); |
| 184 | + let (n2, rest) = parse_u16_be(rest).unwrap(); |
| 185 | + assert_eq!(n1, 0x1122); |
| 186 | + assert_eq!(n2, 0x3344); |
| 187 | + assert_eq!(rest, b"\x55"); |
| 188 | + |
| 189 | + assert!(parse_u16_be(b"").is_none()); |
| 190 | + assert!(parse_u16_be(b"\x11").is_none()); |
| 191 | + } |
160 | 192 | }
|
0 commit comments