rust/dhcp: convert parser to nom7 functions

pull/6581/head
Pierre Chifflier 4 years ago committed by Victor Julien
parent 17170c41aa
commit ebd5883da8

@ -18,9 +18,11 @@
use std::cmp::min; use std::cmp::min;
use crate::dhcp::dhcp::*; use crate::dhcp::dhcp::*;
use nom::IResult; use nom7::bytes::streaming::take;
use nom::combinator::rest; use nom7::combinator::{complete, verify};
use nom::number::streaming::{be_u8, be_u16, be_u32}; use nom7::multi::many0;
use nom7::number::streaming::{be_u16, be_u32, be_u8};
use nom7::IResult;
pub struct DHCPMessage { pub struct DHCPMessage {
pub header: DHCPHeader, pub header: DHCPHeader,
@ -81,25 +83,25 @@ pub struct DHCPOption {
pub option: DHCPOptionWrapper, pub option: DHCPOptionWrapper,
} }
named!(pub parse_header<DHCPHeader>, pub fn parse_header(i: &[u8]) -> IResult<&[u8], DHCPHeader> {
do_parse!( let (i, opcode) = be_u8(i)?;
opcode: be_u8 let (i, htype) = be_u8(i)?;
>> htype: be_u8 let (i, hlen) = be_u8(i)?;
>> hlen: be_u8 let (i, hops) = be_u8(i)?;
>> hops: be_u8 let (i, txid) = be_u32(i)?;
>> txid: be_u32 let (i, seconds) = be_u16(i)?;
>> seconds: be_u16 let (i, flags) = be_u16(i)?;
>> flags: be_u16 let (i, clientip) = take(4_usize)(i)?;
>> clientip: take!(4) let (i, yourip) = take(4_usize)(i)?;
>> yourip: take!(4) let (i, serverip) = take(4_usize)(i)?;
>> serverip: take!(4) let (i, giaddr) = take(4_usize)(i)?;
>> giaddr: take!(4) let (i, clienthw) = take(16_usize)(i)?;
>> clienthw: take!(16) let (i, servername) = take(64_usize)(i)?;
>> servername: take!(64) let (i, bootfilename) = take(128_usize)(i)?;
>> bootfilename: take!(128) let (i, magic) = take(4_usize)(i)?;
>> magic: take!(4) Ok((
>> ( i,
DHCPHeader{ DHCPHeader {
opcode: opcode, opcode: opcode,
htype: htype, htype: htype,
hlen: hlen, hlen: hlen,
@ -115,86 +117,90 @@ named!(pub parse_header<DHCPHeader>,
servername: servername.to_vec(), servername: servername.to_vec(),
bootfilename: bootfilename.to_vec(), bootfilename: bootfilename.to_vec(),
magic: magic.to_vec(), magic: magic.to_vec(),
} },
) ))
) }
);
named!(pub parse_clientid_option<DHCPOption>, pub fn parse_clientid_option(i: &[u8]) -> IResult<&[u8], DHCPOption> {
do_parse!( let (i, code) = be_u8(i)?;
code: be_u8 >> let (i, len) = verify(be_u8, |&v| v > 1)(i)?;
len: verify!(be_u8, |&v| v > 1) >> let (i, _htype) = be_u8(i)?;
_htype: be_u8 >> let (i, data) = take(len - 1)(i)?;
data: take!(len - 1) >> Ok((
( i,
DHCPOption{ DHCPOption {
code: code, code: code,
data: None, data: None,
option: DHCPOptionWrapper::ClientId(DHCPOptClientId{ option: DHCPOptionWrapper::ClientId(DHCPOptClientId {
htype: 1, htype: 1,
data: data.to_vec(), data: data.to_vec(),
}), }),
} },
) ))
) }
);
named!(pub parse_address_time_option<DHCPOption>, pub fn parse_address_time_option(i: &[u8]) -> IResult<&[u8], DHCPOption> {
do_parse!( let (i, code) = be_u8(i)?;
code: be_u8 >> let (i, _len) = be_u8(i)?;
_len: be_u8 >> let (i, seconds) = be_u32(i)?;
seconds: be_u32 >> Ok((
( i,
DHCPOption{ DHCPOption {
code: code, code: code,
data: None, data: None,
option: DHCPOptionWrapper::TimeValue(DHCPOptTimeValue{ option: DHCPOptionWrapper::TimeValue(DHCPOptTimeValue { seconds: seconds }),
seconds: seconds, },
}), ))
} }
)
)
);
named!(pub parse_generic_option<DHCPOption>, pub fn parse_generic_option(i: &[u8]) -> IResult<&[u8], DHCPOption> {
do_parse!( let (i, code) = be_u8(i)?;
code: be_u8 >> let (i, len) = be_u8(i)?;
len: be_u8 >> let (i, data) = take(len)(i)?;
data: take!(len) >> ( Ok((
DHCPOption{ i,
DHCPOption {
code: code, code: code,
data: None, data: None,
option: DHCPOptionWrapper::Generic(DHCPOptGeneric{ option: DHCPOptionWrapper::Generic(DHCPOptGeneric {
data: data.to_vec(), data: data.to_vec(),
}), }),
} },
)) ))
); }
// Parse a single DHCP option. When option 255 (END) is parsed, the remaining // Parse a single DHCP option. When option 255 (END) is parsed, the remaining
// data will be consumed. // data will be consumed.
named!(pub parse_option<DHCPOption>, pub fn parse_option(i: &[u8]) -> IResult<&[u8], DHCPOption> {
switch!(peek!(be_u8), let (_, opt) = be_u8(i)?;
match opt {
DHCP_OPT_END => {
// End of options case. We consume the rest of the data // End of options case. We consume the rest of the data
// so the parse is not called again. But is there a // so the parser is not called again. But is there a
// better way to "break"? // better way to "break"?
DHCP_OPT_END => do_parse!( let (data, code) = be_u8(i)?;
code: be_u8 >> Ok((
data: rest >> (DHCPOption{ &[],
code: code, DHCPOption {
code,
data: Some(data.to_vec()), data: Some(data.to_vec()),
option: DHCPOptionWrapper::End, option: DHCPOptionWrapper::End,
})) | },
DHCP_OPT_CLIENT_ID => call!(parse_clientid_option) | ))
DHCP_OPT_ADDRESS_TIME => call!(parse_address_time_option) | }
DHCP_OPT_RENEWAL_TIME => call!(parse_address_time_option) | DHCP_OPT_CLIENT_ID => parse_clientid_option(i),
DHCP_OPT_REBINDING_TIME => call!(parse_address_time_option) | DHCP_OPT_ADDRESS_TIME => parse_address_time_option(i),
_ => call!(parse_generic_option) DHCP_OPT_RENEWAL_TIME => parse_address_time_option(i),
)); DHCP_OPT_REBINDING_TIME => parse_address_time_option(i),
_ => parse_generic_option(i),
}
}
// Parse and return all the options. Upon the end of option indicator // Parse and return all the options. Upon the end of option indicator
// all the data will be consumed. // all the data will be consumed.
named!(pub parse_all_options<Vec<DHCPOption>>, many0!(complete!(call!(parse_option)))); pub fn parse_all_options(i: &[u8]) -> IResult<&[u8], Vec<DHCPOption>> {
many0(complete(parse_option))(i)
}
pub fn dhcp_parse(input: &[u8]) -> IResult<&[u8], DHCPMessage> { pub fn dhcp_parse(input: &[u8]) -> IResult<&[u8], DHCPMessage> {
match parse_header(input) { match parse_header(input) {
@ -257,8 +263,10 @@ mod tests {
assert_eq!(header.yourip, &[0, 0, 0, 0]); assert_eq!(header.yourip, &[0, 0, 0, 0]);
assert_eq!(header.serverip, &[0, 0, 0, 0]); assert_eq!(header.serverip, &[0, 0, 0, 0]);
assert_eq!(header.giaddr, &[0, 0, 0, 0]); assert_eq!(header.giaddr, &[0, 0, 0, 0]);
assert_eq!(&header.clienthw[..(header.hlen as usize)], assert_eq!(
&[0x00, 0x0b, 0x82, 0x01, 0xfc, 0x42]); &header.clienthw[..(header.hlen as usize)],
&[0x00, 0x0b, 0x82, 0x01, 0xfc, 0x42]
);
assert!(header.servername.iter().all(|&x| x == 0)); assert!(header.servername.iter().all(|&x| x == 0));
assert!(header.bootfilename.iter().all(|&x| x == 0)); assert!(header.bootfilename.iter().all(|&x| x == 0));
assert_eq!(header.magic, &[0x63, 0x82, 0x53, 0x63]); assert_eq!(header.magic, &[0x63, 0x82, 0x53, 0x63]);
@ -283,37 +291,34 @@ mod tests {
fn test_parse_client_id_too_short() { fn test_parse_client_id_too_short() {
// Length field of 0. // Length field of 0.
let buf: &[u8] = &[ let buf: &[u8] = &[
0x01, 0x01, 0x00, // Length of 0.
0x00, // Length of 0. 0x01, 0x01, // Junk data start here.
0x01, 0x02, 0x03,
0x01, // Junk data start here.
0x02,
0x03,
]; ];
let r = parse_clientid_option(buf); let r = parse_clientid_option(buf);
assert!(r.is_err()); assert!(r.is_err());
// Length field of 1. // Length field of 1.
let buf: &[u8] = &[ let buf: &[u8] = &[
0x01, 0x01, 0x01, // Length of 1.
0x01, // Length of 1. 0x01, 0x41,
0x01,
0x41,
]; ];
let r = parse_clientid_option(buf); let r = parse_clientid_option(buf);
assert!(r.is_err()); assert!(r.is_err());
// Length field of 2 -- OK. // Length field of 2 -- OK.
let buf: &[u8] = &[ let buf: &[u8] = &[
0x01, 0x01, 0x02, // Length of 2.
0x02, // Length of 2. 0x01, 0x41,
0x01,
0x41,
]; ];
let r = parse_clientid_option(buf); let r = parse_clientid_option(buf);
match r { match r {
Ok((rem, _)) => { assert_eq!(rem.len(), 0); }, Ok((rem, _)) => {
_ => { panic!("failed"); } assert_eq!(rem.len(), 0);
}
_ => {
panic!("failed");
}
} }
} }
} }

Loading…
Cancel
Save