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