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