Skip to content

Commit 72929ff

Browse files
committed
Improve parsing of [un]signed integers.
Allow the [un]signed functions to return integers smaller than [iu]64 and also use the standard `byteorder` definitions of endianness rather than implementing our own enum type. Unfortunately, Rust functions are no longer allowed to have default type arguments (see PR 30724, rust-lang/rust#30724), so we still have to specify the endianness explicitly every time we use one of these functions. The may require wrapping inside a type...
1 parent d1e461e commit 72929ff

File tree

5 files changed

+58
-71
lines changed

5 files changed

+58
-71
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ readme = "README.md"
1111
[dependencies]
1212
byteorder = "0.5.2"
1313
docopt = "0.6.80"
14+
num = "0.1.32"
1415
pcap = "0.5.7"
1516
promising-future = "0.2.2"
1617
rustc-serialize="0.3.19"

src/ethernet/mod.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
//! Dissection of Ethernet (IEEE 802.3) frames.
1111
1212
use {
13-
Endianness,
1413
Error,
1514
NamedValue,
1615
Protocol,
@@ -21,6 +20,8 @@ use {
2120
unsigned,
2221
};
2322

23+
use byteorder::*;
24+
2425

2526
/// The IEEE 802.3 Ethernet protocol.
2627
pub struct Ethernet;
@@ -59,12 +60,12 @@ impl Protocol for Ethernet {
5960
values.push(("Source".to_string(), mac_address(&data[6..12])));
6061

6162
// The type/length field might be either a type or a length.
62-
let tlen = unsigned(&data[12..14], Endianness::BigEndian);
63+
let tlen = unsigned::<u16,NetworkEndian>(&data[12..14]);
6364
let remainder = &data[14..];
6465

6566
match tlen {
6667
Ok(i) if i <= 1500 => {
67-
values.push(("Length".to_string(), Ok(Val::Unsigned(i))));
68+
values.push(("Length".to_string(), Ok(Val::Unsigned(i as u64))));
6869
},
6970

7071
Ok(i) => {
@@ -83,7 +84,7 @@ impl Protocol for Ethernet {
8384
let protoname = protocol.short_name().to_string();
8485
let description = protocol.full_name().to_string();
8586

86-
values.push(("Type".to_string(), Ok(Val::Enum(i, protoname))));
87+
values.push(("Type".to_string(), Ok(Val::Enum(i as u64, protoname))));
8788
values.push((description, protocol.dissect(remainder)));
8889
},
8990
Err(e) => {

src/ethernet/testproto.rs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
//! Dissection of the test protocol from the Xerox Blue Book (but not IEEE 802.3).
1111
1212
use {
13-
Endianness,
1413
Error,
1514
NamedValue,
1615
Protocol,
@@ -20,6 +19,8 @@ use {
2019
unsigned,
2120
};
2221

22+
use byteorder::*;
23+
2324

2425
/// Testing protocol from the Xerox Blue Book.
2526
///
@@ -28,7 +29,7 @@ use {
2829
pub struct TestProtocol;
2930

3031
enum TestMessage<'a> {
31-
Reply { receipt_number: Result<u64>, data: &'a [u8] },
32+
Reply { receipt_number: Result<u16>, data: &'a [u8] },
3233
ForwardData { dest: Result<Val>, message: Result<Box<TestMessage<'a>>> },
3334
}
3435

@@ -38,7 +39,7 @@ impl Protocol for TestProtocol {
3839
fn dissect(&self, data: &[u8]) -> Result {
3940
let mut values:Vec<NamedValue> = vec![];
4041

41-
let skip_count = unsigned(&data[0..2], Endianness::BigEndian);
42+
let skip_count = unsigned::<u64, NetworkEndian>(&data[0..2]);
4243
values.push(("Skip count".to_string(), skip_count.map(Val::Unsigned)));
4344

4445
let top_message =
@@ -68,7 +69,7 @@ impl <'a> TestMessage <'a> {
6869
vec![
6970
("Function Code".to_string(),
7071
Ok(Val::Enum(1, "Reply Message".to_string()))),
71-
("Receipt Number".to_string(), receipt_number.map(Val::Unsigned)),
72+
("Receipt Number".to_string(), receipt_number.and_then(Val::unsigned)),
7273
("Data".to_string(), Ok(Val::Bytes(data.to_vec()))),
7374
],
7475

@@ -91,10 +92,10 @@ impl <'a> TestMessage <'a> {
9192
}
9293

9394
fn parse(data: &[u8]) -> Result<TestMessage> {
94-
let function_code = unsigned(&data[0..2], Endianness::LittleEndian);
95+
let function_code = unsigned::<u16, LittleEndian>(&data[0..2]);
9596
match function_code {
9697
Ok(1) => Ok({
97-
let receipt_number = unsigned(&data[2..4], Endianness::LittleEndian);
98+
let receipt_number = unsigned::<u16, LittleEndian>(&data[2..4]);
9899

99100
TestMessage::Reply{
100101
receipt_number: receipt_number,

src/ip/mod.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
//! See [RFC 791](https://tools.ietf.org/html/rfc791).
1717
1818
use {
19-
Endianness,
2019
Error,
2120
NamedValue,
2221
Protocol,
@@ -26,6 +25,8 @@ use {
2625
unsigned,
2726
};
2827

28+
use byteorder::*;
29+
2930

3031
pub struct IPv4;
3132

@@ -58,8 +59,8 @@ impl Protocol for IPv4 {
5859
values.push(("ECN".to_string(), Ok(Val::Unsigned(ecn as u64))));
5960

6061
// Total length (including header)
61-
let length = unsigned(&data[2..4], Endianness::BigEndian);
62-
values.push(("Length".to_string(), length.map(Val::Unsigned)));
62+
let length = unsigned::<u16, NetworkEndian>(&data[2..4]);
63+
values.push(("Length".to_string(), length.and_then(Val::unsigned)));
6364

6465
// Identification (of datagraph fragments): RFC 6864
6566
values.push(("Identification".to_string(), Ok(Val::Unsigned(data[8] as u64))));

src/lib.rs

Lines changed: 41 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -61,12 +61,12 @@
6161
#![doc(html_logo_url = "https://raw.githubusercontent.com/musec/rusty-shark/master/artwork/wordmark.png")]
6262

6363
extern crate byteorder;
64+
extern crate num;
6465
extern crate promising_future;
6566

66-
use byteorder::ReadBytesExt;
67+
use byteorder::ByteOrder;
6768
pub use promising_future::Future;
6869
use std::fmt;
69-
use std::io;
7070

7171

7272
/// A description of a protocol, including code that can parse it.
@@ -121,6 +121,14 @@ pub enum Val {
121121
}
122122

123123
impl Val {
124+
pub fn unsigned<T>(x: T) -> Result<Val>
125+
where T: num::ToPrimitive + std::fmt::Display
126+
{
127+
x.to_u64()
128+
.map(Val::Unsigned)
129+
.ok_or(Error::InvalidData(format!["Cannot convert {} to u64", x]))
130+
}
131+
124132
pub fn pretty_print(self, indent_level:usize) -> String {
125133
match self {
126134
Val::Subpacket(values) => {
@@ -194,72 +202,47 @@ pub type Result<T=Val> = ::std::result::Result<T,Error>;
194202
/// A named value-or-error.
195203
pub type NamedValue = (String,Result<Val>);
196204

197-
/// Little- or big-endian integer representations.
198-
pub enum Endianness {
199-
BigEndian,
200-
LittleEndian,
201-
}
202205

203206
/// Parse a signed integer of a given endianness from a byte buffer.
204207
///
205208
/// The size of the buffer will be used to determine the size of the integer
206-
/// that should be parsed (i8, i16, i32 or i64), but the result will be stored
207-
/// in an i64.
208-
pub fn signed(buffer: &[u8], endianness: Endianness) -> Result<i64> {
209-
let mut reader = io::Cursor::new(buffer);
210-
211-
match endianness {
212-
Endianness::BigEndian => {
213-
match buffer.len() {
214-
1 => Ok(buffer[0] as i64),
215-
2 => Ok(reader.read_i16::<byteorder::BigEndian>().unwrap() as i64),
216-
4 => Ok(reader.read_i32::<byteorder::BigEndian>().unwrap() as i64),
217-
8 => Ok(reader.read_i64::<byteorder::BigEndian>().unwrap()),
218-
x => Err(Error::InvalidData(format!["Invalid integer size: {} B", x])),
219-
}
220-
}
221-
222-
Endianness::LittleEndian => {
223-
match buffer.len() {
224-
1 => Ok(buffer[0] as i64),
225-
2 => Ok(reader.read_i16::<byteorder::LittleEndian>().unwrap() as i64),
226-
4 => Ok(reader.read_i32::<byteorder::LittleEndian>().unwrap() as i64),
227-
8 => Ok(reader.read_i64::<byteorder::LittleEndian>().unwrap()),
228-
x => Err(Error::InvalidData(format!["Invalid integer size: {} B", x])),
229-
}
230-
}
209+
/// that should be parsed (i8, i16, i32 or i64).
210+
pub fn signed<T, E>(buffer: &[u8]) -> Result<T>
211+
where T: num::FromPrimitive, E: ByteOrder
212+
{
213+
let len = buffer.len();
214+
let conversion_error = "Failed to convert integer";
215+
216+
match len {
217+
1 => T::from_u8(buffer[0]).ok_or(conversion_error),
218+
2 => T::from_i16(E::read_i16(buffer)).ok_or(conversion_error),
219+
4 => T::from_i32(E::read_i32(buffer)).ok_or(conversion_error),
220+
8 => T::from_i64(E::read_i64(buffer)).ok_or(conversion_error),
221+
_ => Err("Invalid integer size"),
231222
}
223+
.map_err(|s| format!["{} ({} B)", s, len])
224+
.map_err(Error::InvalidData)
232225
}
233226

234-
/// Parse a signed integer of a given endianness from a byte buffer.
227+
/// Parse an unsigned integer of a given endianness from a byte buffer.
235228
///
236229
/// The size of the buffer will be used to determine the size of the integer
237-
/// that should be parsed (u8, u16, u32 or u64), but the result will be stored
238-
/// in a u64.
239-
pub fn unsigned(buffer: &[u8], endianness: Endianness) -> Result<u64> {
240-
let mut reader = io::Cursor::new(buffer);
241-
242-
match endianness {
243-
Endianness::BigEndian => {
244-
match buffer.len() {
245-
1 => Ok(buffer[0] as u64),
246-
2 => Ok(reader.read_u16::<byteorder::BigEndian>().unwrap() as u64),
247-
4 => Ok(reader.read_u32::<byteorder::BigEndian>().unwrap() as u64),
248-
8 => Ok(reader.read_u64::<byteorder::BigEndian>().unwrap()),
249-
x => Err(Error::InvalidData(format!["Invalid integer size: {} B", x])),
250-
}
251-
}
252-
253-
Endianness::LittleEndian => {
254-
match buffer.len() {
255-
1 => Ok(buffer[0] as u64),
256-
2 => Ok(reader.read_u16::<byteorder::LittleEndian>().unwrap() as u64),
257-
4 => Ok(reader.read_u32::<byteorder::LittleEndian>().unwrap() as u64),
258-
8 => Ok(reader.read_u64::<byteorder::LittleEndian>().unwrap()),
259-
x => Err(Error::InvalidData(format!["Invalid integer size: {} B", x])),
260-
}
261-
}
230+
/// that should be parsed (u8, u16, u32 or u64).
231+
pub fn unsigned<T, E>(buffer: &[u8]) -> Result<T>
232+
where T: num::FromPrimitive, E: ByteOrder
233+
{
234+
let len = buffer.len();
235+
let conversion_error = "Failed to convert {} B integer";
236+
237+
match len {
238+
1 => T::from_u8(buffer[0]).ok_or(conversion_error),
239+
2 => T::from_u16(E::read_u16(buffer)).ok_or(conversion_error),
240+
4 => T::from_u32(E::read_u32(buffer)).ok_or(conversion_error),
241+
8 => T::from_u64(E::read_u64(buffer)).ok_or(conversion_error),
242+
_ => Err("Invalid integer size: {}"),
262243
}
244+
.map_err(|s| format!["{} ({} B)", s, len])
245+
.map_err(Error::InvalidData)
263246
}
264247

265248

0 commit comments

Comments
 (0)