Skip to content

Commit 0e4d8a8

Browse files
committed
refactor(wip): change resolver chain
- remove extraneous `std::net::` prefixes for already-imported `IpAddr`. - move derivation to a function: - parse input/file as normal; - attempt to use the system DNS resolution; - fall back to previous CloudFlare behaviour.
1 parent c0b0d5a commit 0e4d8a8

File tree

2 files changed

+78
-45
lines changed

2 files changed

+78
-45
lines changed

src/address.rs

Lines changed: 77 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -30,33 +30,10 @@ use crate::warning;
3030
pub fn parse_addresses(input: &Opts) -> Vec<IpAddr> {
3131
let mut ips: Vec<IpAddr> = Vec::new();
3232
let mut unresolved_addresses: Vec<&str> = Vec::new();
33-
let mut self_resolve = true;
34-
let mut backup_resolver =
35-
Resolver::new(ResolverConfig::cloudflare_tls(), ResolverOpts::default()).unwrap();
36-
37-
if let Some(resolver) = &input.resolver {
38-
let resolver_ips = if let Ok(r) = read_resolver_from_file(resolver) {
39-
r
40-
} else {
41-
// Get resolvers from the comma-delimited list string
42-
resolver
43-
.split(',')
44-
.filter_map(|r| IpAddr::from_str(r).ok())
45-
.collect::<Vec<_>>()
46-
};
47-
let mut rc = ResolverConfig::new();
48-
for ip in resolver_ips {
49-
rc.add_name_server(NameServerConfig::new(
50-
SocketAddr::new(ip, 53),
51-
Protocol::Udp,
52-
));
53-
}
54-
backup_resolver = Resolver::new(rc, ResolverOpts::default()).unwrap();
55-
self_resolve = false;
56-
}
33+
let backup_resolver = get_resolver(&input.resolver);
5734

5835
for address in &input.addresses {
59-
let parsed_ips = parse_address(address, &backup_resolver, self_resolve);
36+
let parsed_ips = parse_address(address, &backup_resolver);
6037
if !parsed_ips.is_empty() {
6138
ips.extend(parsed_ips);
6239
} else {
@@ -78,7 +55,7 @@ pub fn parse_addresses(input: &Opts) -> Vec<IpAddr> {
7855
continue;
7956
}
8057

81-
if let Ok(x) = read_ips_from_file(file_path, &backup_resolver, self_resolve) {
58+
if let Ok(x) = read_ips_from_file(file_path, &backup_resolver) {
8259
ips.extend(x);
8360
} else {
8461
warning!(
@@ -103,17 +80,13 @@ pub fn parse_addresses(input: &Opts) -> Vec<IpAddr> {
10380
/// ```rust
10481
/// # use rustscan::address::parse_address;
10582
/// # use hickory_resolver::Resolver;
106-
/// let ips = parse_address("127.0.0.1", &Resolver::default().unwrap(), false);
83+
/// let ips = parse_address("127.0.0.1", &Resolver::default().unwrap());
10784
/// ```
108-
pub fn parse_address(address: &str, resolver: &Resolver, self_resolve: bool) -> Vec<IpAddr> {
85+
pub fn parse_address(address: &str, resolver: &Resolver) -> Vec<IpAddr> {
10986
IpCidr::from_str(address)
11087
.map(|cidr| cidr.iter().collect())
11188
.ok()
11289
.or_else(|| {
113-
if !self_resolve {
114-
// This will trigger `unwrap_or_else` then call `resolve_ips_from_host` function
115-
return None;
116-
};
11790
format!("{}:{}", &address, 80)
11891
.to_socket_addrs()
11992
.ok()
@@ -124,7 +97,7 @@ pub fn parse_address(address: &str, resolver: &Resolver, self_resolve: bool) ->
12497

12598
/// Uses DNS to get the IPS associated with host
12699
fn resolve_ips_from_host(source: &str, backup_resolver: &Resolver) -> Vec<IpAddr> {
127-
let mut ips: Vec<std::net::IpAddr> = Vec::new();
100+
let mut ips: Vec<IpAddr> = Vec::new();
128101

129102
if let Ok(addrs) = source.to_socket_addrs() {
130103
for ip in addrs {
@@ -137,13 +110,51 @@ fn resolve_ips_from_host(source: &str, backup_resolver: &Resolver) -> Vec<IpAddr
137110
ips
138111
}
139112

140-
fn read_resolver_from_file(path: &str) -> Result<Vec<std::net::IpAddr>, std::io::Error> {
141-
let mut ips = Vec::new();
142-
fs::read_to_string(path)?.split('\n').for_each(|line| {
143-
if let Ok(ip) = IpAddr::from_str(line) {
144-
ips.push(ip)
113+
/// Derive a DNS resolver.
114+
///
115+
/// 1. if the `resolver` parameter has been set:
116+
/// 1. assume the parameter is a path and attempt to read IPs.
117+
/// 2. parse the input as a comma-separated list of IPs.
118+
/// 2. if `resolver` is not set:
119+
/// 1. attempt to derive a resolver from the system config. (e.g.
120+
/// `/etc/resolv.conf` on *nix).
121+
/// 2. finally, build a CloudFlare-based resolver (default
122+
/// behaviour).
123+
fn get_resolver(resolver: &Option<String>) -> Resolver {
124+
match resolver {
125+
Some(r) => {
126+
let mut config = ResolverConfig::new();
127+
let resolver_ips = match read_resolver_from_file(r) {
128+
Ok(ips) => ips,
129+
Err(_) => r
130+
.split(',')
131+
.filter_map(|r| IpAddr::from_str(r).ok())
132+
.collect::<Vec<_>>(),
133+
};
134+
for ip in resolver_ips {
135+
config.add_name_server(NameServerConfig::new(
136+
SocketAddr::new(ip, 53),
137+
Protocol::Udp,
138+
));
139+
}
140+
Resolver::new(config, ResolverOpts::default()).unwrap()
145141
}
146-
});
142+
None => match Resolver::from_system_conf() {
143+
Ok(resolver) => resolver,
144+
Err(_) => {
145+
Resolver::new(ResolverConfig::cloudflare_tls(), ResolverOpts::default()).unwrap()
146+
}
147+
},
148+
}
149+
}
150+
151+
/// Parses and input file of IPs for use in DNS resolution.
152+
fn read_resolver_from_file(path: &str) -> Result<Vec<IpAddr>, std::io::Error> {
153+
let ips = fs::read_to_string(path)?
154+
.lines()
155+
.filter_map(|line| IpAddr::from_str(line.trim()).ok())
156+
.collect();
157+
147158
Ok(ips)
148159
}
149160

@@ -152,16 +163,15 @@ fn read_resolver_from_file(path: &str) -> Result<Vec<std::net::IpAddr>, std::io:
152163
fn read_ips_from_file(
153164
ips: &std::path::Path,
154165
backup_resolver: &Resolver,
155-
self_resolve: bool,
156-
) -> Result<Vec<std::net::IpAddr>, std::io::Error> {
166+
) -> Result<Vec<IpAddr>, std::io::Error> {
157167
let file = File::open(ips)?;
158168
let reader = BufReader::new(file);
159169

160-
let mut ips: Vec<std::net::IpAddr> = Vec::new();
170+
let mut ips: Vec<IpAddr> = Vec::new();
161171

162172
for address_line in reader.lines() {
163173
if let Ok(address) = address_line {
164-
ips.extend(parse_address(&address, backup_resolver, self_resolve));
174+
ips.extend(parse_address(&address, backup_resolver));
165175
} else {
166176
debug!("Line in file is not valid");
167177
}
@@ -172,7 +182,7 @@ fn read_ips_from_file(
172182

173183
#[cfg(test)]
174184
mod tests {
175-
use super::{parse_addresses, Opts};
185+
use super::{get_resolver, parse_addresses, Opts};
176186
use std::net::Ipv4Addr;
177187

178188
#[test]
@@ -245,4 +255,27 @@ mod tests {
245255
let ips = parse_addresses(&opts);
246256
assert_eq!(ips.len(), 0);
247257
}
258+
259+
#[test]
260+
fn resolver_default_cloudflare() {
261+
let opts = Opts::default();
262+
263+
let resolver = get_resolver(&opts.resolver);
264+
let lookup = resolver.lookup_ip("www.example.com.").unwrap();
265+
266+
assert!(opts.resolver.is_none());
267+
assert!(lookup.iter().next().is_some());
268+
}
269+
270+
#[test]
271+
fn resolver_args_google_dns() {
272+
let mut opts = Opts::default();
273+
// https://developers.google.com/speed/public-dns
274+
opts.resolver = Some("8.8.8.8,8.8.4.4".to_owned());
275+
276+
let resolver = get_resolver(&opts.resolver);
277+
let lookup = resolver.lookup_ip("www.example.com.").unwrap();
278+
279+
assert!(lookup.iter().next().is_some());
280+
}
248281
}

src/input.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ pub struct Opts {
100100
#[structopt(long)]
101101
pub accessible: bool,
102102

103-
/// A comma-delimited list or file of dns resolvers
103+
/// A comma-delimited list or file of DNS resolvers.
104104
#[structopt(long)]
105105
pub resolver: Option<String>,
106106

0 commit comments

Comments
 (0)