use packet_dissector_core::field::{FieldDescriptor, FieldType, FieldValue, format_utf8_lossy};
use packet_dissector_core::packet::DissectBuffer;
static FD_INLINE_PASSWORD: FieldDescriptor =
FieldDescriptor::new("password", "Password", FieldType::Bytes)
.with_format_fn(format_utf8_lossy);
static FD_INLINE_PEER_ID: FieldDescriptor =
FieldDescriptor::new("peer_id", "Peer-ID", FieldType::Bytes).with_format_fn(format_utf8_lossy);
static FD_INLINE_MESSAGE: FieldDescriptor =
FieldDescriptor::new("message", "Message", FieldType::Bytes).with_format_fn(format_utf8_lossy);
pub fn parse<'pkt>(data: &'pkt [u8], offset: usize, buf: &mut DissectBuffer<'pkt>) {
let Some((code, length)) =
crate::parse_header(data, offset, crate::PAP_HEADER_DESCRIPTORS, buf)
else {
static FD_RAW: FieldDescriptor = FieldDescriptor::new("data", "Data", FieldType::Bytes);
buf.push_field(
&FD_RAW,
FieldValue::Bytes(data),
offset..offset + data.len(),
);
return;
};
if data.len() <= crate::PPP_HEADER_SIZE {
return;
}
let pap_data = if (length as usize) <= data.len() && length >= crate::PPP_HEADER_SIZE as u16 {
&data[crate::PPP_HEADER_SIZE..length as usize]
} else {
&data[crate::PPP_HEADER_SIZE..]
};
let payload_offset = offset + crate::PPP_HEADER_SIZE;
match code {
1 => parse_authenticate_request(pap_data, payload_offset, buf),
2 | 3 => parse_authenticate_reply(pap_data, payload_offset, buf),
_ => {}
}
}
fn parse_authenticate_request<'pkt>(
pap_data: &'pkt [u8],
payload_offset: usize,
buf: &mut DissectBuffer<'pkt>,
) {
if pap_data.is_empty() {
return;
}
let peer_id_len = pap_data[0] as usize;
if 1 + peer_id_len >= pap_data.len() {
return;
}
buf.push_field(
&FD_INLINE_PEER_ID,
FieldValue::Bytes(&pap_data[1..1 + peer_id_len]),
payload_offset + 1..payload_offset + 1 + peer_id_len,
);
let pw_offset = 1 + peer_id_len;
let pw_len = pap_data[pw_offset] as usize;
if pw_offset + 1 + pw_len <= pap_data.len() {
buf.push_field(
&FD_INLINE_PASSWORD,
FieldValue::Bytes(&pap_data[pw_offset + 1..pw_offset + 1 + pw_len]),
payload_offset + pw_offset + 1..payload_offset + pw_offset + 1 + pw_len,
);
}
}
fn parse_authenticate_reply<'pkt>(
pap_data: &'pkt [u8],
payload_offset: usize,
buf: &mut DissectBuffer<'pkt>,
) {
if pap_data.is_empty() {
return;
}
let msg_len = pap_data[0] as usize;
if 1 + msg_len > pap_data.len() {
return;
}
buf.push_field(
&FD_INLINE_MESSAGE,
FieldValue::Bytes(&pap_data[1..1 + msg_len]),
payload_offset + 1..payload_offset + 1 + msg_len,
);
}
#[cfg(test)]
mod tests {
use super::*;
fn obj_range(buf: &DissectBuffer<'_>) -> core::ops::Range<u32> {
let layer = &buf.layers()[0];
let fields = buf.layer_fields(layer);
match &fields[0].value {
FieldValue::Object(r) => r.clone(),
_ => panic!("expected Object"),
}
}
fn fill_buf<'a>(buf: &mut DissectBuffer<'a>, data: &'a [u8]) {
buf.begin_layer("test", None, &[], 0..data.len());
let idx = buf.begin_container(
&crate::FIELD_DESCRIPTORS[crate::FD_PAYLOAD],
FieldValue::Object(0..0),
0..data.len(),
);
parse(data, 0, buf);
buf.end_container(idx);
buf.end_layer();
}
#[test]
fn parse_authenticate_request() {
let data = [
0x01, 0x01, 0x00, 0x0E, 4, b'u', b's', b'e', b'r', 4, b'p', b'a', b's', b's',
];
let mut buf = DissectBuffer::new();
fill_buf(&mut buf, &data);
let r = obj_range(&buf);
let f = buf.nested_fields(&r);
assert_eq!(f.len(), 5);
assert_eq!(
f[0].descriptor.display_fn.unwrap()(&f[0].value, &[]),
Some("Authenticate-Request")
);
assert_eq!(f[3].name(), "peer_id");
assert_eq!(f[3].value, FieldValue::Bytes(b"user" as &[u8]));
assert_eq!(f[4].name(), "password");
assert_eq!(f[4].value, FieldValue::Bytes(b"pass" as &[u8]));
}
#[test]
fn parse_authenticate_request_malformed() {
#[rustfmt::skip]
let data = [0x01, 0x01, 0x00, 0x07, 0xFF, b'u', b's'];
let mut buf = DissectBuffer::new();
fill_buf(&mut buf, &data);
let r = obj_range(&buf);
let f = buf.nested_fields(&r);
assert_eq!(f.len(), 3);
}
#[test]
fn parse_authenticate_ack() {
let data = [0x02, 0x01, 0x00, 0x05, 0x00];
let mut buf = DissectBuffer::new();
fill_buf(&mut buf, &data);
let r = obj_range(&buf);
let f = buf.nested_fields(&r);
assert_eq!(f.len(), 4);
assert_eq!(
f[0].descriptor.display_fn.unwrap()(&f[0].value, &[]),
Some("Authenticate-Ack")
);
assert_eq!(f[3].name(), "message");
assert_eq!(f[3].value, FieldValue::Bytes(&[]));
}
#[test]
fn parse_authenticate_ack_with_message() {
#[rustfmt::skip]
let data = [0x02, 0x01, 0x00, 0x07, 2, b'O', b'K'];
let mut buf = DissectBuffer::new();
fill_buf(&mut buf, &data);
let r = obj_range(&buf);
let f = buf.nested_fields(&r);
assert_eq!(f.len(), 4);
assert_eq!(
f[0].descriptor.display_fn.unwrap()(&f[0].value, &[]),
Some("Authenticate-Ack")
);
assert_eq!(f[3].name(), "message");
assert_eq!(f[3].value, FieldValue::Bytes(b"OK" as &[u8]));
}
#[test]
fn parse_authenticate_nak_with_message() {
#[rustfmt::skip]
let data = [0x03, 0x02, 0x00, 0x09, 4, b'F', b'A', b'I', b'L'];
let mut buf = DissectBuffer::new();
fill_buf(&mut buf, &data);
let r = obj_range(&buf);
let f = buf.nested_fields(&r);
assert_eq!(f.len(), 4);
assert_eq!(
f[0].descriptor.display_fn.unwrap()(&f[0].value, &[]),
Some("Authenticate-Nak")
);
assert_eq!(f[3].value, FieldValue::Bytes(b"FAIL" as &[u8]));
}
#[test]
fn parse_ack_length_clipping() {
#[rustfmt::skip]
let data = [0x02, 0x01, 0x00, 0x05, 0x00, 0xDE, 0xAD, 0xBE];
let mut buf = DissectBuffer::new();
fill_buf(&mut buf, &data);
let r = obj_range(&buf);
let f = buf.nested_fields(&r);
assert_eq!(f.len(), 4);
assert_eq!(f[3].value, FieldValue::Bytes(&[]));
}
#[test]
fn parse_truncated() {
let data = [0x01];
let mut buf = DissectBuffer::new();
buf.begin_layer("test", None, &[], 0..1);
parse(&data, 0, &mut buf);
buf.end_layer();
let layer = &buf.layers()[0];
let fields = buf.layer_fields(layer);
assert!(matches!(fields[0].value, FieldValue::Bytes(_)));
}
}