1use std::net::{Ipv4Addr, Ipv6Addr};
2use std::str::{from_utf8, Utf8Error};
3
4use crate::{AddrParseError, Config, Family, Lookup, Network};
5
6#[derive(Debug)]
8pub enum ParseError {
9 InvalidUtf8(usize, Utf8Error),
11 InvalidValue(usize),
14 InvalidOptionValue(usize),
17 InvalidOption(usize),
19 InvalidDirective(usize),
21 InvalidIp(usize, AddrParseError),
23 ExtraData(usize),
25}
26
27impl std::fmt::Display for ParseError {
28 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
29 match self {
30 ParseError::InvalidUtf8(line, err) => {
31 write!(f, "bad unicode at line {}: {}", line, err)
32 }
33 ParseError::InvalidValue(line) => write!(
34 f,
35 "directive at line {} is improperly formatted or contains invalid value",
36 line
37 ),
38 ParseError::InvalidOptionValue(line) => write!(
39 f,
40 "directive options at line {} contains invalid value of some option",
41 line
42 ),
43 ParseError::InvalidOption(line) => {
44 write!(f, "option at line {} is not recognized", line)
45 }
46 ParseError::InvalidDirective(line) => {
47 write!(f, "directive at line {} is not recognized", line)
48 }
49 ParseError::InvalidIp(line, err) => {
50 write!(f, "directive at line {} contains invalid IP: {}", line, err)
51 }
52 ParseError::ExtraData(line) => write!(f, "extra data at the end of the line {}", line),
53 }
54 }
55}
56
57impl std::error::Error for ParseError {
58 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
59 match self {
60 ParseError::InvalidUtf8(_, err) => Some(err),
61 _ => None,
62 }
63 }
64}
65
66fn ip_v4_netw(val: &str) -> Result<Network, AddrParseError> {
67 let mut pair = val.splitn(2, '/');
68 let ip: Ipv4Addr = pair.next().unwrap().parse()?;
69 if ip.is_unspecified() {
70 return Err(AddrParseError);
71 }
72 if let Some(mask) = pair.next() {
73 let mask = mask.parse()?;
74 let value: u32 = ip.octets().iter().fold(0, |acc, &x| acc + u32::from(x));
76 if value == 0 || (value & !value != 0) {
77 Err(AddrParseError)
78 } else {
79 Ok(Network::V4(ip, mask))
80 }
81 } else {
82 let octets = ip.octets();
93 let mask = if octets[3] == 0 {
94 if octets[2] == 0 {
95 if octets[1] == 0 {
96 Ipv4Addr::new(255, 0, 0, 0)
97 } else {
98 Ipv4Addr::new(255, 255, 0, 0)
99 }
100 } else {
101 Ipv4Addr::new(255, 255, 255, 0)
102 }
103 } else {
104 Ipv4Addr::new(255, 255, 255, 255)
105 };
106 Ok(Network::V4(ip, mask))
107 }
108}
109
110fn ip_v6_netw(val: &str) -> Result<Network, AddrParseError> {
111 let mut pair = val.splitn(2, '/');
112 let ip = pair.next().unwrap().parse()?;
113 if let Some(msk) = pair.next() {
114 Ok(Network::V6(ip, msk.parse()?))
116 } else {
117 Ok(Network::V6(
119 ip,
120 Ipv6Addr::new(
121 65_535, 65_535, 65_535, 65_535, 65_535, 65_535, 65_535, 65_535,
122 ),
123 ))
124 }
125}
126
127pub(crate) fn parse(bytes: &[u8]) -> Result<Config, ParseError> {
128 use self::ParseError::*;
129 let mut cfg = Config::new();
130 'lines: for (lineno, line) in bytes.split(|&x| x == b'\n').enumerate() {
131 for &c in line.iter() {
132 if c != b'\t' && c != b' ' {
133 if c == b';' || c == b'#' {
134 continue 'lines;
135 } else {
136 break;
137 }
138 }
139 }
140 let mut words = from_utf8(line)
142 .map_err(|e| InvalidUtf8(lineno, e))?
143 .split([';', '#'])
145 .next()
146 .ok_or(InvalidValue(lineno))?
147 .split_whitespace();
148 let keyword = match words.next() {
149 Some(x) => x,
150 None => continue,
151 };
152 match keyword {
153 "nameserver" => {
154 let srv = words
155 .next()
156 .ok_or(InvalidValue(lineno))
157 .map(|addr| addr.parse().map_err(|e| InvalidIp(lineno, e)))??;
158 cfg.nameservers.push(srv);
159 if words.next().is_some() {
160 return Err(ExtraData(lineno));
161 }
162 }
163 "domain" => {
164 let dom = words
165 .next()
166 .and_then(|x| x.parse().ok())
167 .ok_or(InvalidValue(lineno))?;
168 cfg.set_domain(dom);
169 if words.next().is_some() {
170 return Err(ExtraData(lineno));
171 }
172 }
173 "search" => {
174 cfg.set_search(words.map(|x| x.to_string()).collect());
175 }
176 "sortlist" => {
177 cfg.sortlist.clear();
178 for pair in words {
179 let netw = ip_v4_netw(pair)
180 .or_else(|_| ip_v6_netw(pair))
181 .map_err(|e| InvalidIp(lineno, e))?;
182 cfg.sortlist.push(netw);
183 }
184 }
185 "options" => {
186 for pair in words {
187 let mut iter = pair.splitn(2, ':');
188 let key = iter.next().unwrap();
189 let value = iter.next();
190 if iter.next().is_some() {
191 return Err(ExtraData(lineno));
192 }
193 match (key, value) {
194 ("debug", _) => cfg.debug = true,
196 ("ndots", Some(x)) => {
197 cfg.ndots = x.parse().map_err(|_| InvalidOptionValue(lineno))?
198 }
199 ("timeout", Some(x)) => {
200 cfg.timeout = x.parse().map_err(|_| InvalidOptionValue(lineno))?
201 }
202 ("attempts", Some(x)) => {
203 cfg.attempts = x.parse().map_err(|_| InvalidOptionValue(lineno))?
204 }
205 ("rotate", _) => cfg.rotate = true,
206 ("no-check-names", _) => cfg.no_check_names = true,
207 ("inet6", _) => cfg.inet6 = true,
208 ("ip6-bytestring", _) => cfg.ip6_bytestring = true,
209 ("ip6-dotint", _) => cfg.ip6_dotint = true,
210 ("no-ip6-dotint", _) => cfg.ip6_dotint = false,
211 ("edns0", _) => cfg.edns0 = true,
212 ("single-request", _) => cfg.single_request = true,
213 ("single-request-reopen", _) => cfg.single_request_reopen = true,
214 ("no-reload", _) => cfg.no_reload = true,
215 ("trust-ad", _) => cfg.trust_ad = true,
216 ("no-tld-query", _) => cfg.no_tld_query = true,
217 ("use-vc", _) => cfg.use_vc = true,
218 _ => return Err(InvalidOption(lineno)),
219 }
220 }
221 }
222 "lookup" => {
223 for word in words {
224 match word {
225 "file" => cfg.lookup.push(Lookup::File),
226 "bind" => cfg.lookup.push(Lookup::Bind),
227 extra => cfg.lookup.push(Lookup::Extra(extra.to_string())),
228 }
229 }
230 }
231 "family" => {
232 for word in words {
233 match word {
234 "inet4" => cfg.family.push(Family::Inet4),
235 "inet6" => cfg.family.push(Family::Inet6),
236 _ => return Err(InvalidValue(lineno)),
237 }
238 }
239 }
240 _ => return Err(InvalidDirective(lineno)),
241 }
242 }
243 Ok(cfg)
244}