wireforge_app/dns/
name.rs1use wireforge_core::util::read_u16be;
4
5const MAX_JUMPS: u8 = 10;
7
8pub fn parse_name(buf: &[u8], start: usize) -> Option<(String, usize)> {
14 let mut name = String::new();
15 let mut pos = start;
16 let mut jumped = false;
17 let mut jump_count = 0u8;
18 let mut end_pos = start;
19
20 loop {
21 if jump_count > MAX_JUMPS {
22 return None;
23 }
24 if pos >= buf.len() {
25 return None;
26 }
27 let label_len = buf[pos] as usize;
28 if label_len & 0xC0 == 0xC0 {
29 if pos + 2 > buf.len() {
31 return None;
32 }
33 let offset = read_u16be(&buf[pos..pos + 2]) as usize & 0x3FFF;
34 if !jumped {
35 end_pos = pos + 2;
36 }
37 pos = offset;
38 jumped = true;
39 jump_count += 1;
40 } else if label_len == 0 {
41 if !jumped {
42 end_pos = pos + 1;
43 }
44 break;
45 } else {
46 if label_len > 63 {
47 return None;
48 }
49 if pos + 1 + label_len > buf.len() {
50 return None;
51 }
52 if !name.is_empty() {
53 name.push('.');
54 }
55 let label_bytes = &buf[pos + 1..pos + 1 + label_len];
56 name.push_str(core::str::from_utf8(label_bytes).ok()?);
58 pos += 1 + label_len;
59 if !jumped {
60 end_pos = pos;
61 }
62 }
63 }
64 Some((name, end_pos))
65}
66
67pub fn encode_name(name: &str) -> Vec<u8> {
69 let mut out = Vec::new();
70 for label in name.split('.') {
71 let label_bytes = label.as_bytes();
72 out.push(label_bytes.len() as u8);
73 out.extend_from_slice(label_bytes);
74 }
75 out.push(0); out
77}
78
79#[cfg(test)]
80mod tests {
81 use super::*;
82
83 #[test]
84 fn parse_simple_name() {
85 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];
87 let (name, pos) = parse_name(data, 0).unwrap();
88 assert_eq!(name, "www.example.com");
89 assert_eq!(pos, data.len());
90 }
91
92 #[test]
93 fn parse_compressed_name() {
94 let mut data = vec![0u8; 12];
96 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]);
97 data.extend_from_slice(&[3, b'w', b'w', b'w', 0xC0, 12]);
99
100 let (name, _) = parse_name(&data, 25).unwrap(); assert_eq!(name, "www.example.com");
102 }
103
104 #[test]
105 fn parse_multi_jump_compression() {
106 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];
108 let ptr1: &[u8] = &[3, b'w', b'w', b'w', 0xC0, 0]; let ptr2: &[u8] = &[0xC0, 13]; let mut data = Vec::new();
112 data.extend_from_slice(name_data); data.extend_from_slice(ptr1); data.extend_from_slice(ptr2); let (name, _) = parse_name(&data, 19).unwrap();
117 assert_eq!(name, "www.example.com");
118 }
119
120 #[test]
121 fn parse_compression_loop_returns_none() {
122 let data: &[u8] = &[0xC0, 2, 0xC0, 0];
124 assert!(parse_name(data, 0).is_none());
125 }
126
127 #[test]
128 fn parse_label_too_long() {
129 let mut data = vec![64u8]; data.extend(std::iter::repeat_n(b'x', 64));
131 assert!(parse_name(&data, 0).is_none());
132 }
133
134 #[test]
135 fn encode_name_roundtrip() {
136 let encoded = encode_name("www.example.com");
137 let (decoded, _) = parse_name(&encoded, 0).unwrap();
138 assert_eq!(decoded, "www.example.com");
139 }
140}