use wireforge_core::util::read_u16be;
const MAX_JUMPS: u8 = 10;
pub fn parse_name(buf: &[u8], start: usize) -> Option<(String, usize)> {
let mut name = String::new();
let mut pos = start;
let mut jumped = false;
let mut jump_count = 0u8;
let mut end_pos = start;
loop {
if jump_count > MAX_JUMPS {
return None;
}
if pos >= buf.len() {
return None;
}
let label_len = buf[pos] as usize;
if label_len & 0xC0 == 0xC0 {
if pos + 2 > buf.len() {
return None;
}
let offset = read_u16be(&buf[pos..pos + 2]) as usize & 0x3FFF;
if !jumped {
end_pos = pos + 2;
}
pos = offset;
jumped = true;
jump_count += 1;
} else if label_len == 0 {
if !jumped {
end_pos = pos + 1;
}
break;
} else {
if label_len > 63 {
return None;
}
if pos + 1 + label_len > buf.len() {
return None;
}
if !name.is_empty() {
name.push('.');
}
let label_bytes = &buf[pos + 1..pos + 1 + label_len];
name.push_str(core::str::from_utf8(label_bytes).ok()?);
pos += 1 + label_len;
if !jumped {
end_pos = pos;
}
}
}
Some((name, end_pos))
}
pub fn encode_name(name: &str) -> Vec<u8> {
let mut out = Vec::new();
for label in name.split('.') {
let label_bytes = label.as_bytes();
out.push(label_bytes.len() as u8);
out.extend_from_slice(label_bytes);
}
out.push(0); out
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parse_simple_name() {
let data: &[u8] = &[3, b'w', b'w', b'w', 7, b'e', b'x', b'a', b'm', b'p', b'l', b'e', 3, b'c', b'o', b'm', 0];
let (name, pos) = parse_name(data, 0).unwrap();
assert_eq!(name, "www.example.com");
assert_eq!(pos, data.len());
}
#[test]
fn parse_compressed_name() {
let mut data = vec![0u8; 12];
data.extend_from_slice(&[7, b'e', b'x', b'a', b'm', b'p', b'l', b'e', 3, b'c', b'o', b'm', 0]);
data.extend_from_slice(&[3, b'w', b'w', b'w', 0xC0, 12]);
let (name, _) = parse_name(&data, 25).unwrap(); assert_eq!(name, "www.example.com");
}
#[test]
fn parse_multi_jump_compression() {
let name_data: &[u8] = &[7, b'e', b'x', b'a', b'm', b'p', b'l', b'e', 3, b'c', b'o', b'm', 0];
let ptr1: &[u8] = &[3, b'w', b'w', b'w', 0xC0, 0]; let ptr2: &[u8] = &[0xC0, 13];
let mut data = Vec::new();
data.extend_from_slice(name_data); data.extend_from_slice(ptr1); data.extend_from_slice(ptr2);
let (name, _) = parse_name(&data, 19).unwrap();
assert_eq!(name, "www.example.com");
}
#[test]
fn parse_compression_loop_returns_none() {
let data: &[u8] = &[0xC0, 2, 0xC0, 0];
assert!(parse_name(data, 0).is_none());
}
#[test]
fn parse_label_too_long() {
let mut data = vec![64u8]; data.extend(std::iter::repeat_n(b'x', 64));
assert!(parse_name(&data, 0).is_none());
}
#[test]
fn encode_name_roundtrip() {
let encoded = encode_name("www.example.com");
let (decoded, _) = parse_name(&encoded, 0).unwrap();
assert_eq!(decoded, "www.example.com");
}
}