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