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}