webrtc_mdns/message/name.rs
1use std::collections::HashMap;
2use std::fmt;
3
4use crate::error::*;
5
6const NAME_LEN: usize = 255;
7
8// A Name is a non-encoded domain name. It is used instead of strings to avoid
9// allocations.
10#[derive(Default, PartialEq, Eq, Debug, Clone)]
11pub struct Name {
12 pub data: String,
13}
14
15// String implements fmt.Stringer.String.
16impl fmt::Display for Name {
17 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
18 write!(f, "{}", self.data)
19 }
20}
21
22impl Name {
23 pub fn new(data: &str) -> Result<Self> {
24 if data.len() > NAME_LEN {
25 Err(Error::ErrCalcLen)
26 } else {
27 Ok(Name {
28 data: data.to_owned(),
29 })
30 }
31 }
32
33 // pack appends the wire format of the Name to msg.
34 //
35 // Domain names are a sequence of counted strings split at the dots. They end
36 // with a zero-length string. Compression can be used to reuse domain suffixes.
37 //
38 // The compression map will be updated with new domain suffixes. If compression
39 // is nil, compression will not be used.
40 pub fn pack(
41 &self,
42 mut msg: Vec<u8>,
43 compression: &mut Option<HashMap<String, usize>>,
44 compression_off: usize,
45 ) -> Result<Vec<u8>> {
46 let data = self.data.as_bytes();
47
48 // Add a trailing dot to canonicalize name.
49 if data.is_empty() || data[data.len() - 1] != b'.' {
50 return Err(Error::ErrNonCanonicalName);
51 }
52
53 // Allow root domain.
54 if data.len() == 1 && data[0] == b'.' {
55 msg.push(0);
56 return Ok(msg);
57 }
58
59 // Emit sequence of counted strings, chopping at dots.
60 let mut begin = 0;
61 for i in 0..data.len() {
62 // Check for the end of the segment.
63 if data[i] == b'.' {
64 // The two most significant bits have special meaning.
65 // It isn't allowed for segments to be long enough to
66 // need them.
67 if i - begin >= (1 << 6) {
68 return Err(Error::ErrSegTooLong);
69 }
70
71 // Segments must have a non-zero length.
72 if i - begin == 0 {
73 return Err(Error::ErrZeroSegLen);
74 }
75
76 msg.push((i - begin) as u8);
77 msg.extend_from_slice(&data[begin..i]);
78
79 begin = i + 1;
80 continue;
81 }
82
83 // We can only compress domain suffixes starting with a new
84 // segment. A pointer is two bytes with the two most significant
85 // bits set to 1 to indicate that it is a pointer.
86 if i == 0 || data[i - 1] == b'.' {
87 if let Some(compression) = compression {
88 let key: String = self.data.chars().skip(i).collect();
89 if let Some(ptr) = compression.get(&key) {
90 // Hit. Emit a pointer instead of the rest of
91 // the domain.
92 msg.push(((ptr >> 8) | 0xC0) as u8);
93 msg.push((ptr & 0xFF) as u8);
94 return Ok(msg);
95 }
96
97 // Miss. Add the suffix to the compression table if the
98 // offset can be stored in the available 14 bytes.
99 if msg.len() <= 0x3FFF {
100 compression.insert(key, msg.len() - compression_off);
101 }
102 }
103 }
104 }
105
106 msg.push(0);
107 Ok(msg)
108 }
109
110 // unpack unpacks a domain name.
111 pub fn unpack(&mut self, msg: &[u8], off: usize) -> Result<usize> {
112 self.unpack_compressed(msg, off, true /* allowCompression */)
113 }
114
115 pub fn unpack_compressed(
116 &mut self,
117 msg: &[u8],
118 off: usize,
119 allow_compression: bool,
120 ) -> Result<usize> {
121 // curr_off is the current working offset.
122 let mut curr_off = off;
123
124 // new_off is the offset where the next record will start. Pointers lead
125 // to data that belongs to other names and thus doesn't count towards to
126 // the usage of this name.
127 let mut new_off = off;
128
129 // ptr is the number of pointers followed.
130 let mut ptr = 0;
131
132 // Name is a slice representation of the name data.
133 let mut name = String::new(); //n.Data[:0]
134
135 loop {
136 if curr_off >= msg.len() {
137 return Err(Error::ErrBaseLen);
138 }
139 let c = msg[curr_off];
140 curr_off += 1;
141 match c & 0xC0 {
142 0x00 => {
143 // String segment
144 if c == 0x00 {
145 // A zero length signals the end of the name.
146 break;
147 }
148 let end_off = curr_off + c as usize;
149 if end_off > msg.len() {
150 return Err(Error::ErrCalcLen);
151 }
152 name.push_str(String::from_utf8(msg[curr_off..end_off].to_vec())?.as_str());
153 name.push('.');
154 curr_off = end_off;
155 }
156 0xC0 => {
157 // Pointer
158 if !allow_compression {
159 return Err(Error::ErrCompressedSrv);
160 }
161 if curr_off >= msg.len() {
162 return Err(Error::ErrInvalidPtr);
163 }
164 let c1 = msg[curr_off];
165 curr_off += 1;
166 if ptr == 0 {
167 new_off = curr_off;
168 }
169 // Don't follow too many pointers, maybe there's a loop.
170 ptr += 1;
171 if ptr > 10 {
172 return Err(Error::ErrTooManyPtr);
173 }
174 curr_off = ((c ^ 0xC0) as usize) << 8 | (c1 as usize);
175 }
176 _ => {
177 // Prefixes 0x80 and 0x40 are reserved.
178 return Err(Error::ErrReserved);
179 }
180 }
181 }
182 if name.is_empty() {
183 name.push('.');
184 }
185 if name.len() > NAME_LEN {
186 return Err(Error::ErrCalcLen);
187 }
188 self.data = name;
189 if ptr == 0 {
190 new_off = curr_off;
191 }
192 Ok(new_off)
193 }
194
195 pub(crate) fn skip(msg: &[u8], off: usize) -> Result<usize> {
196 // new_off is the offset where the next record will start. Pointers lead
197 // to data that belongs to other names and thus doesn't count towards to
198 // the usage of this name.
199 let mut new_off = off;
200
201 loop {
202 if new_off >= msg.len() {
203 return Err(Error::ErrBaseLen);
204 }
205 let c = msg[new_off];
206 new_off += 1;
207 match c & 0xC0 {
208 0x00 => {
209 if c == 0x00 {
210 // A zero length signals the end of the name.
211 break;
212 }
213 // literal string
214 new_off += c as usize;
215 if new_off > msg.len() {
216 return Err(Error::ErrCalcLen);
217 }
218 }
219 0xC0 => {
220 // Pointer to somewhere else in msg.
221
222 // Pointers are two bytes.
223 new_off += 1;
224
225 // Don't follow the pointer as the data here has ended.
226 break;
227 }
228 _ => {
229 // Prefixes 0x80 and 0x40 are reserved.
230 return Err(Error::ErrReserved);
231 }
232 }
233 }
234
235 Ok(new_off)
236 }
237}