use std::ops::Range;
use packet_dissector_core::field::{FieldDescriptor, FieldValue, MacAddr};
use packet_dissector_core::packet::{DissectBuffer, Layer};
pub struct OwnedPacket {
pub data: Vec<u8>,
pub aux_data: Vec<u8>,
pub layers: Vec<Layer>,
pub fields: Vec<OwnedField>,
pub scratch: Vec<u8>,
}
pub struct OwnedField {
pub descriptor: &'static FieldDescriptor,
pub value: OwnedFieldValue,
pub range: Range<usize>,
}
pub enum OwnedFieldValue {
U8(u8),
U16(u16),
U32(u32),
U64(u64),
I32(i32),
Bytes(BytesSource),
Str(BytesSource),
Ipv4Addr([u8; 4]),
Ipv6Addr([u8; 16]),
MacAddr(MacAddr),
Array(Range<u32>),
Object(Range<u32>),
Scratch(Range<u32>),
}
pub enum BytesSource {
Data(Range<usize>),
AuxData(Range<usize>),
}
impl OwnedPacket {
pub fn from_dissect_buf(buf: &DissectBuffer<'_>, data: &[u8]) -> Self {
let owned_data: Vec<u8> = data.to_vec();
let owned_aux: Vec<u8> = buf.aux_data();
let data_range = data.as_ptr_range();
let fields: Vec<OwnedField> = buf
.fields()
.iter()
.map(|f| {
let value = convert_field_value(&f.value, data_range.start, data.len(), buf);
OwnedField {
descriptor: f.descriptor,
value,
range: f.range.clone(),
}
})
.collect();
OwnedPacket {
data: owned_data,
aux_data: owned_aux,
layers: buf.layers().to_vec(),
fields,
scratch: buf.scratch().to_vec(),
}
}
pub fn layer_fields(&self, layer: &Layer) -> &[OwnedField] {
&self.fields[layer.field_range.start as usize..layer.field_range.end as usize]
}
pub fn nested_fields(&self, range: &Range<u32>) -> &[OwnedField] {
&self.fields[range.start as usize..range.end as usize]
}
pub fn layer_by_name(&self, name: &str) -> Option<&Layer> {
self.layers.iter().find(|l| l.name == name)
}
pub fn field_by_name(&self, layer: &Layer, name: &str) -> Option<&OwnedField> {
self.layer_fields(layer).iter().find(|f| f.name() == name)
}
pub fn resolve_bytes<'a>(&'a self, source: &BytesSource) -> &'a [u8] {
match source {
BytesSource::Data(range) => &self.data[range.clone()],
BytesSource::AuxData(range) => &self.aux_data[range.clone()],
}
}
pub fn resolve_str<'a>(&'a self, source: &BytesSource) -> &'a str {
let bytes = self.resolve_bytes(source);
let s = std::str::from_utf8(bytes).unwrap_or("");
debug_assert!(
!s.is_empty() || bytes.is_empty(),
"Str source contained invalid UTF-8"
);
s
}
pub fn resolve_scratch(&self, range: &Range<u32>) -> &[u8] {
&self.scratch[range.start as usize..range.end as usize]
}
pub fn resolve_display_name(&self, layer: &Layer, name: &str) -> Option<&'static str> {
let base_name = name.strip_suffix("_name")?;
let fields = self.layer_fields(layer);
let base_field = fields.iter().find(|f| f.name() == base_name)?;
let display_fn = base_field.descriptor.display_fn?;
let temp_value = base_field.value.to_field_value(self);
let temp_fields: Vec<packet_dissector_core::field::Field<'_>> = fields
.iter()
.map(|f| packet_dissector_core::field::Field {
descriptor: f.descriptor,
value: f.value.to_field_value(self),
range: f.range.clone(),
})
.collect();
display_fn(&temp_value, &temp_fields)
}
}
impl OwnedField {
pub fn name(&self) -> &'static str {
self.descriptor.name
}
pub fn display_name(&self) -> &'static str {
self.descriptor.display_name
}
}
impl OwnedFieldValue {
pub fn to_field_value<'a>(&'a self, packet: &'a OwnedPacket) -> FieldValue<'a> {
match self {
OwnedFieldValue::U8(v) => FieldValue::U8(*v),
OwnedFieldValue::U16(v) => FieldValue::U16(*v),
OwnedFieldValue::U32(v) => FieldValue::U32(*v),
OwnedFieldValue::U64(v) => FieldValue::U64(*v),
OwnedFieldValue::I32(v) => FieldValue::I32(*v),
OwnedFieldValue::Bytes(src) => FieldValue::Bytes(packet.resolve_bytes(src)),
OwnedFieldValue::Str(src) => FieldValue::Str(packet.resolve_str(src)),
OwnedFieldValue::Ipv4Addr(a) => FieldValue::Ipv4Addr(*a),
OwnedFieldValue::Ipv6Addr(a) => FieldValue::Ipv6Addr(*a),
OwnedFieldValue::MacAddr(m) => FieldValue::MacAddr(*m),
OwnedFieldValue::Array(r) => FieldValue::Array(r.clone()),
OwnedFieldValue::Object(r) => FieldValue::Object(r.clone()),
OwnedFieldValue::Scratch(r) => FieldValue::Scratch(r.clone()),
}
}
}
fn convert_field_value(
value: &FieldValue<'_>,
data_base: *const u8,
data_len: usize,
buf: &DissectBuffer<'_>,
) -> OwnedFieldValue {
match value {
FieldValue::U8(v) => OwnedFieldValue::U8(*v),
FieldValue::U16(v) => OwnedFieldValue::U16(*v),
FieldValue::U32(v) => OwnedFieldValue::U32(*v),
FieldValue::U64(v) => OwnedFieldValue::U64(*v),
FieldValue::I32(v) => OwnedFieldValue::I32(*v),
FieldValue::Bytes(b) => {
let src = resolve_ptr_range(b.as_ptr(), b.len(), data_base, data_len, buf);
OwnedFieldValue::Bytes(src)
}
FieldValue::Str(s) => {
let b = s.as_bytes();
let src = resolve_ptr_range(b.as_ptr(), b.len(), data_base, data_len, buf);
OwnedFieldValue::Str(src)
}
FieldValue::Ipv4Addr(a) => OwnedFieldValue::Ipv4Addr(*a),
FieldValue::Ipv6Addr(a) => OwnedFieldValue::Ipv6Addr(*a),
FieldValue::MacAddr(m) => OwnedFieldValue::MacAddr(*m),
FieldValue::Array(r) => OwnedFieldValue::Array(r.clone()),
FieldValue::Object(r) => OwnedFieldValue::Object(r.clone()),
FieldValue::Scratch(r) => OwnedFieldValue::Scratch(r.clone()),
}
}
fn resolve_ptr_range(
ptr: *const u8,
len: usize,
data_base: *const u8,
data_len: usize,
buf: &DissectBuffer<'_>,
) -> BytesSource {
let addr = ptr as usize;
let data_start = data_base as usize;
let data_end = data_start + data_len;
if addr >= data_start && addr < data_end {
let offset = addr - data_start;
return BytesSource::Data(offset..offset + len);
}
if let Some(range) = buf.resolve_aux_ptr_range(ptr, len) {
return BytesSource::AuxData(range);
}
BytesSource::Data(0..0)
}
#[cfg(test)]
mod tests {
use super::*;
use packet_dissector_core::field::FieldType;
static TEST_DESC: FieldDescriptor = FieldDescriptor::new("test", "Test", FieldType::U16);
#[test]
fn from_dissect_buf_empty() {
let buf = DissectBuffer::new();
let data: &[u8] = &[];
let owned = OwnedPacket::from_dissect_buf(&buf, data);
assert!(owned.layers.is_empty());
assert!(owned.fields.is_empty());
}
#[test]
fn from_dissect_buf_preserves_layers() {
let data = [0u8; 14];
let mut buf = DissectBuffer::new();
buf.begin_layer("Ethernet", None, &[], 0..14);
buf.push_field(&TEST_DESC, FieldValue::U16(0x0800), 12..14);
buf.end_layer();
let owned = OwnedPacket::from_dissect_buf(&buf, &data);
assert_eq!(owned.layers.len(), 1);
assert_eq!(owned.layers[0].name, "Ethernet");
assert_eq!(owned.fields.len(), 1);
assert!(matches!(
owned.fields[0].value,
OwnedFieldValue::U16(0x0800)
));
}
#[test]
fn from_dissect_buf_resolves_bytes() {
let data: &[u8] = &[0x00, 0x11, 0x22, 0x33, 0x44, 0x55];
let mut buf = DissectBuffer::new();
static BYTES_DESC: FieldDescriptor =
FieldDescriptor::new("payload", "Payload", FieldType::Bytes);
buf.begin_layer("Test", None, &[], 0..6);
buf.push_field(&BYTES_DESC, FieldValue::Bytes(&data[2..5]), 2..5);
buf.end_layer();
let owned = OwnedPacket::from_dissect_buf(&buf, data);
match &owned.fields[0].value {
OwnedFieldValue::Bytes(src) => {
assert_eq!(owned.resolve_bytes(src), &[0x22, 0x33, 0x44]);
}
_ => panic!("expected Bytes"),
}
}
#[test]
fn from_dissect_buf_resolves_str() {
let data: &[u8] = b"hello world";
let mut buf = DissectBuffer::new();
static STR_DESC: FieldDescriptor = FieldDescriptor::new("msg", "Message", FieldType::Str);
buf.begin_layer("Test", None, &[], 0..11);
let s = std::str::from_utf8(&data[6..11]).unwrap();
buf.push_field(&STR_DESC, FieldValue::Str(s), 6..11);
buf.end_layer();
let owned = OwnedPacket::from_dissect_buf(&buf, data);
match &owned.fields[0].value {
OwnedFieldValue::Str(src) => {
assert_eq!(owned.resolve_str(src), "world");
}
_ => panic!("expected Str"),
}
}
#[test]
fn layer_by_name_works() {
let data = [0u8; 14];
let mut buf = DissectBuffer::new();
buf.begin_layer("Ethernet", None, &[], 0..14);
buf.end_layer();
buf.begin_layer("IPv4", None, &[], 14..34);
buf.end_layer();
let owned = OwnedPacket::from_dissect_buf(&buf, &data);
assert!(owned.layer_by_name("IPv4").is_some());
assert!(owned.layer_by_name("TCP").is_none());
}
#[test]
fn field_by_name_works() {
let data = [0u8; 14];
let mut buf = DissectBuffer::new();
buf.begin_layer("Test", None, &[], 0..14);
buf.push_field(&TEST_DESC, FieldValue::U16(42), 0..2);
buf.end_layer();
let owned = OwnedPacket::from_dissect_buf(&buf, &data);
let layer = owned.layer_by_name("Test").unwrap();
let field = owned.field_by_name(layer, "test").unwrap();
assert!(matches!(field.value, OwnedFieldValue::U16(42)));
}
#[test]
fn to_field_value_roundtrip() {
let data: &[u8] = b"test data here";
let mut buf = DissectBuffer::new();
static STR_DESC: FieldDescriptor = FieldDescriptor::new("val", "Value", FieldType::Str);
buf.begin_layer("Test", None, &[], 0..14);
let s = std::str::from_utf8(&data[5..9]).unwrap();
buf.push_field(&STR_DESC, FieldValue::Str(s), 5..9);
buf.end_layer();
let owned = OwnedPacket::from_dissect_buf(&buf, data);
let fv = owned.fields[0].value.to_field_value(&owned);
assert_eq!(fv, FieldValue::Str("data"));
}
}