xref: /webrtc/mdns/src/message/name.rs (revision ffe74184)
1 use crate::error::*;
2 
3 use std::collections::HashMap;
4 use std::fmt;
5 
6 const 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)]
11 pub struct Name {
12     pub data: String,
13 }
14 
15 // String implements fmt.Stringer.String.
16 impl fmt::Display for Name {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result17     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
18         write!(f, "{}", self.data)
19     }
20 }
21 
22 impl Name {
new(data: &str) -> Result<Self>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.
pack( &self, mut msg: Vec<u8>, compression: &mut Option<HashMap<String, usize>>, compression_off: usize, ) -> Result<Vec<u8>>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.
unpack(&mut self, msg: &[u8], off: usize) -> Result<usize>111     pub fn unpack(&mut self, msg: &[u8], off: usize) -> Result<usize> {
112         self.unpack_compressed(msg, off, true /* allowCompression */)
113     }
114 
unpack_compressed( &mut self, msg: &[u8], off: usize, allow_compression: bool, ) -> Result<usize>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 
skip(msg: &[u8], off: usize) -> Result<usize>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 }
238