Skip to content

Commit

Permalink
Merge pull request #23 from qwerty541/ipv6_support
Browse files Browse the repository at this point in the history
feat: IPv6 support
  • Loading branch information
qwerty541 authored Apr 13, 2024
2 parents 4858522 + 5a06d6e commit 2dc4100
Show file tree
Hide file tree
Showing 3 changed files with 144 additions and 30 deletions.
8 changes: 6 additions & 2 deletions .github/workflows/check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,12 @@ jobs:
- name: Run tests
run: cargo test

- name: Perform test application run
run: cargo run
- name: Perform test application run (IPv4)
run: cargo run -- --name-servers-ip v4 --lookup-ip v4 --timeout 1

# Disabled due to GitHub Actions not supporting IPv6 yet.
# - name: Perform test application run (IPv6)
# run: cargo run -- --name-servers-ip v6 --lookup-ip v6 --timeout 1

msrv:
name: Check minimal supported rust version (MSRV).
Expand Down
57 changes: 57 additions & 0 deletions src/args.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use clap::builder::PossibleValue;
use clap::Parser;
use clap::ValueEnum;
use hickory_resolver::config::LookupIpStrategy;
use hickory_resolver::config::Protocol as ResolverProtocol;
use std::fmt;
use std::str::FromStr;
Expand All @@ -24,6 +25,62 @@ pub struct Arguments {
/// The protocol to use.
#[arg(long, default_value = "udp")]
pub protocol: Protocol,
/// The IP version to use for the name servers.
#[arg(long, default_value = "v4")]
pub name_servers_ip: IpAddr,
/// The IP version to use for the lookup.
#[arg(long, default_value = "v4")]
pub lookup_ip: IpAddr,
}

#[derive(Debug, Clone, Copy)]
pub enum IpAddr {
V4,
V6,
}

impl From<IpAddr> for LookupIpStrategy {
fn from(val: IpAddr) -> Self {
match val {
IpAddr::V4 => LookupIpStrategy::Ipv4Only,
IpAddr::V6 => LookupIpStrategy::Ipv6Only,
}
}
}

impl ValueEnum for IpAddr {
fn value_variants<'a>() -> &'a [Self] {
&[Self::V4, Self::V6]
}

fn to_possible_value(&self) -> Option<PossibleValue> {
Some(match self {
Self::V4 => PossibleValue::new("v4"),
Self::V6 => PossibleValue::new("v6"),
})
}
}

impl FromStr for IpAddr {
type Err = String;

fn from_str(s: &str) -> Result<Self, Self::Err> {
for variant in Self::value_variants() {
if variant.to_possible_value().unwrap().matches(s, false) {
return Ok(*variant);
}
}
Err(format!("Invalid variant: {}", s))
}
}

impl fmt::Display for IpAddr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.to_possible_value()
.expect("no values are skipped")
.get_name()
.fmt(f)
}
}

#[derive(Debug, Clone, Copy)]
Expand Down
109 changes: 81 additions & 28 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use std::collections;
use std::fmt;
use std::net::IpAddr;
use std::net::Ipv4Addr;
use std::net::Ipv6Addr;
use std::net::SocketAddr;
use std::sync;
use std::thread;
Expand All @@ -27,7 +28,7 @@ struct DnsEntry {
socker_addr: SocketAddr,
}

macro_rules! dns_entry {
macro_rules! ipv4_dns_entry {
($name:expr, $ip:expr, $port:expr) => {
DnsEntry {
name: String::from($name),
Expand All @@ -39,29 +40,65 @@ macro_rules! dns_entry {
};
}

macro_rules! ipv6_dns_entry {
($name:expr, $ip:expr, $port:expr) => {
DnsEntry {
name: String::from($name),
socker_addr: SocketAddr::new(
IpAddr::V6(Ipv6Addr::new(
$ip.0, $ip.1, $ip.2, $ip.3, $ip.4, $ip.5, $ip.6, $ip.7,
)),
$port,
),
}
};
}

lazy_static::lazy_static! {
static ref IPV4_DNS_ENTRIES: Vec<DnsEntry> = vec![
ipv4_dns_entry!("Google", (8, 8, 8, 8), 53),
ipv4_dns_entry!("Google", (8, 8, 4, 4), 53),
ipv4_dns_entry!("Cloudflare", (1, 1, 1, 1), 53),
ipv4_dns_entry!("Cloudflare", (1, 0, 0, 1), 53),
ipv4_dns_entry!("Quad9", (9, 9, 9, 9), 53),
ipv4_dns_entry!("Quad9", (149, 112, 112, 112), 53),
ipv4_dns_entry!("Provider", (192, 168, 0, 1), 53),
ipv4_dns_entry!("Control D", (76, 76, 2, 0), 53),
ipv4_dns_entry!("Control D", (76, 76, 10, 0), 53),
ipv4_dns_entry!("OpenDNS Home", (208, 67, 222, 222), 53),
ipv4_dns_entry!("OpenDNS Home", (208, 67, 220, 220), 53),
ipv4_dns_entry!("CleanBrowsing", (185, 228, 168, 9), 53),
ipv4_dns_entry!("CleanBrowsing", (185, 228, 169, 9), 53),
ipv4_dns_entry!("AdGuard DNS", (94, 140, 14, 14), 53),
ipv4_dns_entry!("AdGuard DNS", (94, 140, 15, 15), 53),
ipv4_dns_entry!("Comodo Secure DNS", (8, 26, 56, 26), 53),
ipv4_dns_entry!("Comodo Secure DNS", (8, 20, 247, 20), 53),
ipv4_dns_entry!("Level3", (209, 244, 0, 3), 53),
ipv4_dns_entry!("Level3", (209, 244, 0, 4), 53),
ipv4_dns_entry!("Verisign", (64, 6, 64, 6), 53),
ipv4_dns_entry!("Verisign", (64, 6, 65, 6), 53),
];
}

lazy_static::lazy_static! {
static ref DNS_ENTRIES: Vec<DnsEntry> = vec![
dns_entry!("Google", (8, 8, 8, 8), 53),
dns_entry!("Google", (8, 8, 4, 4), 53),
dns_entry!("Cloudflare", (1, 1, 1, 1), 53),
dns_entry!("Cloudflare", (1, 0, 0, 1), 53),
dns_entry!("Quad9", (9, 9, 9, 9), 53),
dns_entry!("Quad9", (149, 112, 112, 112), 53),
dns_entry!("Provider", (192, 168, 0, 1), 53),
dns_entry!("Control D", (76, 76, 2, 0), 53),
dns_entry!("Control D", (76, 76, 10, 0), 53),
dns_entry!("OpenDNS Home", (208, 67, 222, 222), 53),
dns_entry!("OpenDNS Home", (208, 67, 220, 220), 53),
dns_entry!("CleanBrowsing", (185, 228, 168, 9), 53),
dns_entry!("CleanBrowsing", (185, 228, 169, 9), 53),
dns_entry!("AdGuard DNS", (94, 140, 14, 14), 53),
dns_entry!("AdGuard DNS", (94, 140, 15, 15), 53),
dns_entry!("Comodo Secure DNS", (8, 26, 56, 26), 53),
dns_entry!("Comodo Secure DNS", (8, 20, 247, 20), 53),
dns_entry!("Level3", (209, 244, 0, 3), 53),
dns_entry!("Level3", (209, 244, 0, 4), 53),
dns_entry!("Verisign", (64, 6, 64, 6), 53),
dns_entry!("Verisign", (64, 6, 65, 6), 53),
static ref IPV6_DNS_ENTRIES: Vec<DnsEntry> = vec![
ipv6_dns_entry!("Google", (0x2001, 0x4860, 0x4860, 0, 0, 0, 0, 0x8888), 53),
ipv6_dns_entry!("Google", (0x2001, 0x4860, 0x4860, 0, 0, 0, 0, 0x8844), 53),
ipv6_dns_entry!("Cloudflare", (0x2606, 0x4700, 0x4700, 0, 0, 0, 0, 0x1111), 53),
ipv6_dns_entry!("Cloudflare", (0x2606, 0x4700, 0x4700, 0, 0, 0, 0, 0x1001), 53),
ipv6_dns_entry!("Quad9", (0x2620, 0x00fe, 0, 0, 0, 0, 0, 0x00fe), 53),
ipv6_dns_entry!("Quad9", (0x2620, 0x00fe, 0, 0, 0, 0, 0, 0x0009), 53),
ipv6_dns_entry!("Provider", (0xfe80, 0, 0, 0, 0, 0, 0, 0x0001), 53),
ipv6_dns_entry!("Control D", (0x2606, 0x1a40, 0, 0, 0, 0, 0, 0), 53),
ipv6_dns_entry!("Control D", (0x2606, 0x1a40, 0x0001, 0, 0, 0, 0, 0), 53),
ipv6_dns_entry!("OpenDNS Home", (0x2620, 0x0119, 0x0035, 0, 0, 0, 0, 0x0035), 53),
ipv6_dns_entry!("OpenDNS Home", (0x2620, 0x0119, 0x0053, 0, 0, 0, 0, 0x0053), 53),
ipv6_dns_entry!("CleanBrowsing", (0x2a0d, 0x2a00, 0x0001, 0, 0, 0, 0, 0x0002), 53),
ipv6_dns_entry!("CleanBrowsing", (0x2a0d, 0x2a00, 0x0002, 0, 0, 0, 0, 0x0002), 53),
ipv6_dns_entry!("AdGuard DNS", (0x2a10, 0x50c0, 0, 0, 0, 0, 0x0ad1, 0x00ff), 53),
ipv6_dns_entry!("AdGuard DNS", (0x2a10, 0x50c0, 0, 0, 0, 0, 0x0ad2, 0x00ff), 53),
ipv6_dns_entry!("Verisign", (0x2620, 0x0074, 0x001b, 0, 0, 0, 0x0001, 0x0001), 53),
ipv6_dns_entry!("Verisign", (0x2620, 0x0074, 0x001c, 0, 0, 0, 0x0002, 0x0002), 53),
];
}

Expand Down Expand Up @@ -146,12 +183,21 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
Domain: {}\n\
Threads: {}\n\
Requests: {}\n\
Protocol: {}",
arguments.domain, arguments.threads, arguments.requests, arguments.protocol
Protocol: {}\n\
Name servers: IP{}; Lookup: IP{}",
arguments.domain,
arguments.threads,
arguments.requests,
arguments.protocol,
arguments.name_servers_ip,
arguments.lookup_ip,
);

// Create a progress bar with the desired style
let pb = ProgressBar::new((DNS_ENTRIES.len() * arguments.requests) as u64);
let pb = ProgressBar::new(match arguments.name_servers_ip {
args::IpAddr::V4 => IPV4_DNS_ENTRIES.len() * arguments.requests,
args::IpAddr::V6 => IPV6_DNS_ENTRIES.len() * arguments.requests,
} as u64);
pb.set_style(
ProgressStyle::default_bar()
.template("{spinner:.bold.green} [{elapsed}] [{bar:80.cyan/blue}] {pos}/{len} ({eta})")?
Expand All @@ -160,7 +206,10 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {

// Create the shared DNS entries and result entries
let dns_entries = sync::Arc::new(sync::Mutex::new(collections::VecDeque::from(
DNS_ENTRIES.clone(),
match arguments.name_servers_ip {
args::IpAddr::V4 => IPV4_DNS_ENTRIES.clone(),
args::IpAddr::V6 => IPV6_DNS_ENTRIES.clone(),
},
)));
let result_entries = sync::Arc::new(sync::Mutex::new(Vec::new()));
let mut handles = Vec::new();
Expand Down Expand Up @@ -195,6 +244,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut resolver_opts = ResolverOpts::default();
resolver_opts.attempts = 0;
resolver_opts.timeout = Duration::from_secs(arguments_clone.timeout);
resolver_opts.ip_strategy = arguments_clone.lookup_ip.into();
let resolver = Resolver::new(resolver_config, resolver_opts).unwrap();

// Measure the time it takes to resolve the domain
Expand All @@ -213,7 +263,10 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
Err(e) => MeasureResult {
name: dns_entry.name.clone(),
ip: dns_entry.socker_addr.ip(),
resolved_ip: IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)),
resolved_ip: match arguments_clone.lookup_ip {
args::IpAddr::V4 => IpAddr::V4(Ipv4Addr::UNSPECIFIED),
args::IpAddr::V6 => IpAddr::V6(Ipv6Addr::UNSPECIFIED),
},
time: TimeResult::Failed(e.to_string()),
},
};
Expand Down

0 comments on commit 2dc4100

Please sign in to comment.