mio_httpc/dns_parser/
name.rs1use std::fmt;
2use std::fmt::Write;
3use std::str::from_utf8;
4use super::Error;
6use byteorder::{BigEndian, ByteOrder};
7
8#[derive(Debug, Clone, Copy)]
13pub struct Name<'a> {
14 labels: &'a [u8],
15 original: &'a [u8],
18}
19
20impl<'a> Name<'a> {
21 pub fn scan(data: &'a [u8], original: &'a [u8]) -> Result<Name<'a>, Error> {
22 let mut parse_data = data;
23 let mut return_pos = None;
24 let mut pos = 0;
25 if parse_data.len() <= pos {
26 return Err(Error::UnexpectedEOF);
27 }
28 let mut largest_pos = original.len();
31 let mut byte = parse_data[pos];
32 while byte != 0 {
33 if parse_data.len() <= pos {
34 return Err(Error::UnexpectedEOF);
35 }
36 if byte & 0b1100_0000 == 0b1100_0000 {
37 if parse_data.len() < pos + 2 {
38 return Err(Error::UnexpectedEOF);
39 }
40 let off = (BigEndian::read_u16(&parse_data[pos..pos + 2]) & !0b1100_0000_0000_0000)
41 as usize;
42 if off >= original.len() {
43 return Err(Error::UnexpectedEOF);
44 }
45 if let None = return_pos {
49 return_pos = Some(pos);
50 }
51
52 if off >= largest_pos {
55 return Err(Error::BadPointer);
56 }
57 largest_pos = off;
58 pos = 0;
59 parse_data = &original[off..];
60 } else if byte & 0b1100_0000 == 0 {
61 let end = pos + byte as usize + 1;
62 if parse_data.len() < end {
63 return Err(Error::UnexpectedEOF);
64 }
65 if !parse_data[pos + 1..end].is_ascii() {
66 return Err(Error::LabelIsNotAscii);
67 }
68 pos = end;
69 if parse_data.len() <= pos {
70 return Err(Error::UnexpectedEOF);
71 }
72 } else {
73 return Err(Error::UnknownLabelFormat);
74 }
75 byte = parse_data[pos];
76 }
77 if let Some(return_pos) = return_pos {
78 return Ok(Name {
79 labels: &data[..return_pos + 2],
80 original: original,
81 });
82 } else {
83 return Ok(Name {
84 labels: &data[..pos + 1],
85 original: original,
86 });
87 }
88 }
89 pub fn byte_len(&self) -> usize {
90 self.labels.len()
91 }
92}
93
94impl<'a> fmt::Display for Name<'a> {
95 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
96 let data = self.labels;
97 let original = self.original;
98 let mut pos = 0;
99 loop {
100 let byte = data[pos];
101 if byte == 0 {
102 return Ok(());
103 } else if byte & 0b1100_0000 == 0b1100_0000 {
104 let off =
105 (BigEndian::read_u16(&data[pos..pos + 2]) & !0b1100_0000_0000_0000) as usize;
106 if pos != 0 {
107 fmt.write_char('.')?;
108 }
109 return fmt::Display::fmt(&Name::scan(&original[off..], original).unwrap(), fmt);
110 } else if byte & 0b1100_0000 == 0 {
111 if pos != 0 {
112 fmt.write_char('.')?;
113 }
114 let end = pos + byte as usize + 1;
115 fmt.write_str(from_utf8(&data[pos + 1..end]).unwrap())?;
116 pos = end;
117 continue;
118 } else {
119 unreachable!();
120 }
121 }
122 }
123}
124
125#[cfg(test)]
126mod test {
127 use crate::dns_parser::{Error, Name};
128
129 #[test]
130 fn parse_badpointer_same_offset() {
131 let same_offset = vec![192, 2, 192, 2];
134 let is_match = matches!(
135 Name::scan(&same_offset, &same_offset),
136 Err(Error::BadPointer)
137 );
138
139 assert!(is_match);
140 }
141
142 #[test]
143 fn parse_badpointer_forward_offset() {
144 let forwards_offset = vec![192, 2, 192, 4, 192, 2];
147 let is_match = matches!(
148 Name::scan(&forwards_offset, &forwards_offset),
149 Err(Error::BadPointer)
150 );
151
152 assert!(is_match);
153 }
154
155 #[test]
156 fn nested_names() {
157 let buf = b"\x02xx\x00\x02yy\xc0\x00\x02zz\xc0\x04";
159
160 assert_eq!(Name::scan(&buf[..], buf).unwrap().to_string(), "xx");
161 assert_eq!(Name::scan(&buf[..], buf).unwrap().labels, b"\x02xx\x00");
162 assert_eq!(Name::scan(&buf[4..], buf).unwrap().to_string(), "yy.xx");
163 assert_eq!(
164 Name::scan(&buf[4..], buf).unwrap().labels,
165 b"\x02yy\xc0\x00"
166 );
167 assert_eq!(Name::scan(&buf[9..], buf).unwrap().to_string(), "zz.yy.xx");
168 assert_eq!(
169 Name::scan(&buf[9..], buf).unwrap().labels,
170 b"\x02zz\xc0\x04"
171 );
172 }
173}