Skip to content

Instantly share code, notes, and snippets.

@richo
Created June 16, 2015 04:18
Show Gist options
  • Select an option

  • Save richo/4a67af2758ee4b68356b to your computer and use it in GitHub Desktop.

Select an option

Save richo/4a67af2758ee4b68356b to your computer and use it in GitHub Desktop.
use nom::{IResult};
use nom::{le_u64,le_u32,le_u16};
use std::str::from_utf8;
// FIXME(richo) Flesh this out properly with it's own discrete parser.
#[derive(Debug)]
pub struct Options<'a> {
pub options: Vec<Opt<'a>>,
}
#[derive(Debug)]
pub struct Opt<'a> {
code: u16,
length: u16,
value: &'a [u8],
}
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | Option Code | Option Length |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// / Option Value /
// / /* variable length, aligned to 32 bits */ /
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// / /
// / . . . other options . . . /
// / /
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | Option Code == opt_endofopt | Option Length == 0 |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
#[inline(always)]
// Debug helper, since at various points being able to dump how much it's planning to read has been
// really handy
fn expand(i: u16) -> usize {
i as usize
}
named!(option<&[u8],Opt>,
chain!(
code: le_u16 ~
length: le_u16 ~
value: take!(expand(length)) ,
||{
Opt {
code: code,
length: length,
value: value,
}
}
)
);
// It's not abundantly clear to me that this is actually safe.
// My belief is that because we're operating on a &[u8] that was carved out of the high level
// buffer, and that *it* is a fat pointer with a length, the runtime will stop us from running off
// the end, but it needs to be actually proven.
named!(pub parse_options< &[u8],Options >,
chain!(
opts: many1!(option),
||{
// It's also not super clear to me that we actually want to include the final option
// in the vector
if let Some(last) = opts.last() {
assert_eq!(last.code, 0x0);
assert_eq!(last.length, 0x0);
}
Options {
options: opts
}
}
)
);
#[test]
fn test_parse_options() {
let input = b"\x12\x42\x08\x00asdfasdf\x00\x00\x00\x00";
match parse_options(input) {
IResult::Done(left, opts) => {
assert_eq!(opts.options.len(), 2);
let o = &opts.options[0];
assert_eq!(o.code, 0x4212);
assert_eq!(o.length, 0x08);
assert_eq!(o.value, b"asdfasdf");
},
_ => {
panic!("Hit a codepath we shouldn't have");
},
}
}
#[test]
fn test_multiple_options() {
let input = b"\x03\x00\x0b\x00\x57\x69\x6e\x64\
\x6f\x77\x73\x20\x58\x50\x00\x00\x04\x00\x0c\x00\x54\x65\x73\x74\
\x30\x30\x34\x2e\x65\x78\x65\x00\x00\x00\x00\x00\x40\x00\x00\x00";
match parse_options(input) {
IResult::Done(left, opts) => {
// assert_eq!(opts.options.len(), 3);
let o = &opts.options[0];
assert_eq!(o.code, 0x03);
assert_eq!(o.length, 0x0b);
assert_eq!(&o.value[..], b"Windows XP\x00");
let o = &opts.options[1];
// assert_eq!(o.code, 0x4212);
// assert_eq!(o.length, 0x08);
// assert_eq!(from_utf8(o.value).unwrap(), "Windows XP");
},
_ => {
panic!("Hit a codepath we shouldn't have");
},
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment