use super::{NameRef, ParseError};
fn build_with_labels(labels: &[&[u8]]) -> std::vec::Vec<u8> {
use std::vec::Vec;
let mut v = Vec::new();
for l in labels {
v.push(l.len() as u8);
v.extend_from_slice(l);
}
v.push(0);
v
}
#[test]
fn parses_simple_name() {
let buf = build_with_labels(&[b"foo", b"local"]);
let (n, consumed) = NameRef::try_parse(&buf, 0).unwrap();
assert_eq!(consumed, buf.len());
let labels: std::vec::Vec<&[u8]> = n.labels().map(|r| r.unwrap()).collect();
assert_eq!(labels, std::vec![b"foo".as_slice(), b"local".as_slice()]);
}
#[test]
fn rejects_label_over_63() {
let buf = [64u8]; let err = NameRef::try_parse(&buf, 0).unwrap_err();
assert!(matches!(
err,
ParseError::InvalidLabelKind(_) | ParseError::LabelTooLong(_)
));
}
#[test]
fn labels_iteration_rejects_over_long_expanded_name() {
let labels: std::vec::Vec<&[u8]> = std::vec![b"a".as_slice(); 128];
let buf = build_with_labels(&labels);
assert_eq!(buf.len(), 257);
let (n, _) = NameRef::try_parse(&buf, 0).unwrap();
let mut saw_too_long = false;
for r in n.labels() {
if let Err(ParseError::NameTooLong(_)) = r {
saw_too_long = true;
break;
}
}
assert!(
saw_too_long,
"labels() must reject a name whose expanded wire form exceeds 255 octets"
);
let ok_labels: std::vec::Vec<&[u8]> = std::vec![b"a".as_slice(); 127];
let ok_buf = build_with_labels(&ok_labels); assert_eq!(ok_buf.len(), 255);
let (n2, _) = NameRef::try_parse(&ok_buf, 0).unwrap();
assert!(
n2.labels().all(|r| r.is_ok()),
"a name whose expanded wire form is exactly 255 octets must iterate cleanly"
);
}
#[test]
fn rejects_forward_pointer() {
let buf = [0xC0u8, 0x00, 0];
let (n, _) = NameRef::try_parse(&buf, 0).unwrap();
let first = n.labels().next().unwrap();
assert!(matches!(first, Err(ParseError::PointerForward(_))));
}
#[test]
fn pointer_resolves_backwards() {
let mut buf = std::vec::Vec::new();
buf.push(2u8);
buf.extend_from_slice(b"ab");
buf.push(0);
buf.push(0xC0);
buf.push(0x00);
let (n, _) = NameRef::try_parse(&buf, 4).unwrap();
let labels: std::vec::Vec<&[u8]> = n.labels().map(|r| r.unwrap()).collect();
assert_eq!(labels, std::vec![b"ab".as_slice()]);
}
#[test]
fn equality_ignoring_case() {
let buf1 = build_with_labels(&[b"Foo", b"Local"]);
let buf2 = build_with_labels(&[b"foo", b"LOCAL"]);
let n1 = NameRef::try_parse(&buf1, 0).unwrap().0;
let n2 = NameRef::try_parse(&buf2, 0).unwrap().0;
assert!(n1.equals_ignoring_case(&n2));
}
#[test]
fn try_parse_rejects_truncated_pointer() {
assert!(matches!(
NameRef::try_parse(&[0xC0u8], 0),
Err(ParseError::BufferTooShort(_))
));
}
#[test]
fn try_parse_rejects_truncated_label() {
assert!(matches!(
NameRef::try_parse(&[3u8, b'a'], 0),
Err(ParseError::BufferTooShort(_))
));
}
#[test]
fn equals_ignoring_case_detects_every_kind_of_difference() {
let base_buf = build_with_labels(&[b"foo", b"local"]);
let base = NameRef::try_parse(&base_buf, 0).unwrap().0;
let longer = build_with_labels(&[b"fooo", b"local"]);
assert!(!base.equals_ignoring_case(&NameRef::try_parse(&longer, 0).unwrap().0));
let other = build_with_labels(&[b"bar", b"local"]);
assert!(!base.equals_ignoring_case(&NameRef::try_parse(&other, 0).unwrap().0));
let shorter = build_with_labels(&[b"foo"]);
assert!(!base.equals_ignoring_case(&NameRef::try_parse(&shorter, 0).unwrap().0));
}
#[test]
fn labels_reject_invalid_kind_via_backward_pointer() {
let buf = [0x80u8, 0x00, 0xC0, 0x00];
let (n, _) = NameRef::try_parse(&buf, 2).unwrap();
assert!(matches!(
n.labels().next(),
Some(Err(ParseError::InvalidLabelKind(0x80)))
));
}
#[test]
fn labels_reject_truncated_label_via_backward_pointer() {
let buf = [5u8, 0xC0, 0x00];
let (n, _) = NameRef::try_parse(&buf, 1).unwrap();
assert!(matches!(
n.labels().next(),
Some(Err(ParseError::BufferTooShort(_)))
));
}
#[test]
fn try_parse_rejects_reserved_label_kind_64() {
let buf = [0x40u8];
let err = NameRef::try_parse(&buf, 0).unwrap_err();
assert!(matches!(err, ParseError::InvalidLabelKind(0x40)));
}
#[test]
fn start_reports_parse_offset() {
let mut buf = std::vec::Vec::new();
buf.push(2u8);
buf.extend_from_slice(b"ab");
buf.push(0);
buf.push(0xC0);
buf.push(0x00);
let (n, _) = NameRef::try_parse(&buf, 4).unwrap();
assert_eq!(n.start(), 4);
let zero = NameRef::try_parse(&buf, 0).unwrap().0;
assert_eq!(zero.start(), 0);
}
#[test]
fn write_wire_folds_or_preserves_case() {
let buf = build_with_labels(&[b"Foo", b"LOCAL"]);
let (n, _) = NameRef::try_parse(&buf, 0).unwrap();
let mut folded = std::vec::Vec::new();
n.write_wire(&mut folded, true).unwrap();
assert_eq!(folded, build_with_labels(&[b"foo", b"local"]));
let mut preserved = std::vec::Vec::new();
n.write_wire(&mut preserved, false).unwrap();
assert_eq!(preserved, build_with_labels(&[b"Foo", b"LOCAL"]));
}
#[test]
fn write_wire_propagates_iterator_error() {
let buf = [5u8, 0xC0, 0x00];
let (n, _) = NameRef::try_parse(&buf, 1).unwrap();
let mut out = std::vec::Vec::new();
let err = n.write_wire(&mut out, false).unwrap_err();
assert!(matches!(err, ParseError::BufferTooShort(_)));
}
#[test]
fn labels_next_is_fused_after_completion() {
let buf = build_with_labels(&[b"foo"]);
let (n, _) = NameRef::try_parse(&buf, 0).unwrap();
let mut it = n.labels();
assert_eq!(it.next().map(|r| r.unwrap()), Some(b"foo".as_slice()));
assert!(it.next().is_none()); assert!(it.next().is_none()); assert!(it.next().is_none());
}
#[test]
fn labels_reject_label_running_to_end_of_message() {
let buf = [0x03u8, 0xC0, 0x00, b'a'];
assert_eq!(buf.len(), 4);
let (n, _) = NameRef::try_parse(&buf, 1).unwrap();
let mut it = n.labels();
assert_eq!(it.next().map(|r| r.unwrap()), Some(&buf[1..4]));
match it.next() {
Some(Err(ParseError::BufferTooShort(d))) => assert_eq!(d.at(), 4),
other => panic!("expected BufferTooShort at end of message, got {other:?}"),
}
}
#[test]
fn labels_reject_truncated_pointer_reached_mid_chain() {
let buf = [0x03u8, 0xC0, 0x00, b'a', 0xC0];
assert_eq!(buf.len(), 5);
let (n, _) = NameRef::try_parse(&buf, 1).unwrap();
let mut it = n.labels();
assert_eq!(it.next().map(|r| r.unwrap()), Some(&buf[1..4]));
match it.next() {
Some(Err(ParseError::BufferTooShort(d))) => assert_eq!(d.at(), 5),
other => panic!("expected BufferTooShort for the truncated pointer, got {other:?}"),
}
}
#[test]
fn labels_reject_pointer_chain_over_max_hops() {
use crate::constants::MAX_POINTER_HOPS;
let count = usize::from(MAX_POINTER_HOPS) + 1; let mut buf = std::vec::Vec::new();
for i in 0..count {
let target = if i == 0 { 0u16 } else { (2 * (i - 1)) as u16 };
buf.push(0xC0u8); buf.push((target & 0xFF) as u8);
}
let start = 2 * (count - 1); let (n, _) = NameRef::try_parse(&buf, start).unwrap();
let mut saw_chain_too_long = false;
for r in n.labels() {
if let Err(ParseError::PointerChainTooLong(max)) = r {
assert_eq!(max, MAX_POINTER_HOPS);
saw_chain_too_long = true;
break;
}
}
assert!(
saw_chain_too_long,
"a chain exceeding MAX_POINTER_HOPS must yield PointerChainTooLong"
);
}
#[test]
fn labels_reject_label_ending_past_u16_range() {
let label_start = 65_500usize;
let label_len = 63usize; let label_end = label_start + 1 + label_len;
let mut buf = std::vec![0u8; label_end + 1];
buf[label_start] = label_len as u8;
for b in buf.iter_mut().take(label_end).skip(label_start + 1) {
*b = b'a';
}
buf[label_end] = 0; let (n, _) = NameRef::try_parse(&buf, label_start).unwrap();
match n.labels().next() {
Some(Err(ParseError::IntegerConversion(_))) => {}
other => panic!("expected IntegerConversion past u16 range, got {other:?}"),
}
}