Added response validity check

This commit is contained in:
pradt2 2021-11-18 20:52:05 +00:00
parent eb7115efe0
commit 659f558bb9
2 changed files with 62 additions and 36 deletions

View File

@ -15,7 +15,7 @@ mod stun;
#[tokio::main(flavor = "current_thread")] #[tokio::main(flavor = "current_thread")]
async fn main() -> io::Result<()> { async fn main() -> io::Result<()> {
let stream = candidates::get_candidates().await?.into_iter(); let stream = candidates::get_candidates().await?.into_iter();
let futs = stream let profiles = stream
.map(|candidate| async move { .map(|candidate| async move {
let res = stun::check_candidate(candidate.clone()).await; let res = stun::check_candidate(candidate.clone()).await;
match &res { match &res {
@ -25,34 +25,39 @@ async fn main() -> io::Result<()> {
res res
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let zz = Instant::now(); let timestamp = Instant::now();
let results = futures::future::join_all(futs).await; let profiles = futures::future::join_all(profiles).await;
let mut all_ok = 0; let mut all_ok = 0;
let mut dns_unresolved = 0; let mut dns_unresolved = 0;
let mut partial_timeout = 0; let mut partial_timeout = 0;
let mut complete_timeout = 0; let mut complete_timeout = 0;
results.iter().for_each(|res| { let mut incorrect_mapping_returned = 0;
profiles.iter().for_each(|res| {
match res { match res {
Ok(_) => { all_ok += 1; } Ok(_) => { all_ok += 1; }
Err(CheckError::DnsResolutionFailed) => { dns_unresolved += 1; } Err(CheckError::DnsResolutionFailed) => { dns_unresolved += 1; }
Err(CheckError::PartialTimeout) => { partial_timeout += 1; } Err(CheckError::PartialTimeout) => { partial_timeout += 1; }
Err(CheckError::Timeout) => { complete_timeout += 1; } Err(CheckError::Timeout) => { complete_timeout += 1; }
Err(CheckError::IncorrectMappingReturned) => { incorrect_mapping_returned += 1; }
} }
}); });
println!("OK {} , DNS failure {} , p/Timeout {} , Timeout {}", all_ok, dns_unresolved, partial_timeout, complete_timeout); println!(
"OK {} , DNS failure {} , p/Timeout {} , Timeout {} , Incorrect {}",
all_ok, dns_unresolved, partial_timeout, complete_timeout, incorrect_mapping_returned
);
let mut output = results.iter() let mut output_hosts = profiles.iter()
.filter_map(|res| res.as_ref().ok()) .filter_map(|res| res.as_ref().ok())
.map(|profile| profile.candidate.clone()) .map(|profile| profile.candidate.clone())
.collect::<Vec<_>>(); .collect::<Vec<_>>();
output.shuffle(&mut thread_rng()); output_hosts.shuffle(&mut thread_rng());
let output_hosts = output.into_iter() let output_hosts = output_hosts.into_iter()
.map(|candidate| String::from(candidate)) .map(|candidate| String::from(candidate))
.reduce(|a, b| format!("{}\n{}", a, b)) .reduce(|a, b| format!("{}\n{}", a, b))
.unwrap_or(String::from("")); .unwrap_or(String::from(""));
tokio::fs::write("valid_hosts.txt", output_hosts).await?; tokio::fs::write("valid_hosts.txt", output_hosts).await?;
let output_ip4 = results.iter() let output_ip4 = profiles.iter()
.filter_map(|res| res.as_ref().ok()) .filter_map(|res| res.as_ref().ok())
.flat_map(|profile| profile.addrs.clone().into_iter()) .flat_map(|profile| profile.addrs.clone().into_iter())
.filter(|addr| addr.is_ipv4()) .filter(|addr| addr.is_ipv4())
@ -66,7 +71,7 @@ async fn main() -> io::Result<()> {
.unwrap_or(String::from("")); .unwrap_or(String::from(""));
tokio::fs::write("valid_ipv4s.txt", output_ip4).await?; tokio::fs::write("valid_ipv4s.txt", output_ip4).await?;
let output_ip6 = results.iter() let output_ip6 = profiles.iter()
.filter_map(|res| res.as_ref().ok()) .filter_map(|res| res.as_ref().ok())
.flat_map(|profile| profile.addrs.clone().into_iter()) .flat_map(|profile| profile.addrs.clone().into_iter())
.filter(|addr| addr.is_ipv6()) .filter(|addr| addr.is_ipv6())
@ -80,6 +85,6 @@ async fn main() -> io::Result<()> {
.unwrap_or(String::from("")); .unwrap_or(String::from(""));
tokio::fs::write("valid_ipv6s.txt", output_ip6).await?; tokio::fs::write("valid_ipv6s.txt", output_ip6).await?;
println!("Finished in {:?}", zz.elapsed()); println!("Finished in {:?}", timestamp.elapsed());
Ok(()) Ok(())
} }

View File

@ -6,18 +6,19 @@ use tokio::io;
use tokio::net::{lookup_host}; use tokio::net::{lookup_host};
use crate::candidates::StunCandidate; use crate::candidates::StunCandidate;
#[derive(Debug)] #[derive(Clone, Copy, Debug, PartialEq)]
pub enum CheckError { pub enum CheckError {
DnsResolutionFailed, DnsResolutionFailed,
IncorrectMappingReturned,
PartialTimeout, PartialTimeout,
Timeout Timeout,
} }
#[derive(Debug)] #[derive(Debug)]
pub struct CandidateProfile { pub struct CandidateProfile {
pub candidate: StunCandidate, pub candidate: StunCandidate,
pub addrs: Vec<SocketAddr>, pub addrs: Vec<SocketAddr>,
pub rtt_ms: u32 pub rtt_ms: u32,
} }
pub async fn check_candidate(candidate: StunCandidate) -> Result<CandidateProfile, CheckError> { pub async fn check_candidate(candidate: StunCandidate) -> Result<CandidateProfile, CheckError> {
@ -30,38 +31,58 @@ pub async fn check_candidate(candidate: StunCandidate) -> Result<CandidateProfil
SocketAddr::V4(..) => { "0.0.0.0:0" } SocketAddr::V4(..) => { "0.0.0.0:0" }
SocketAddr::V6(..) => { "[::]:0" } SocketAddr::V6(..) => { "[::]:0" }
}.parse::<std::net::SocketAddr>().unwrap()).await.unwrap(); }.parse::<std::net::SocketAddr>().unwrap()).await.unwrap();
let local_address = u.local_addr().expect("Local address to be available");
let mut client = stunclient::StunClient::new(*address); let mut client = stunclient::StunClient::new(*address);
client.set_timeout(Duration::from_secs(1)); client.set_timeout(Duration::from_secs(1));
let time = Instant::now(); let time = Instant::now();
let res = client.query_external_address_async(&u).await; let res = client.query_external_address_async(&u).await;
(res, time.elapsed()) match res {
Ok(mapped_address) => if mapped_address.port() == local_address.port() {
Ok((mapped_address, time.elapsed()))
} else {
Err(CheckError::IncorrectMappingReturned)
},
Err(_) => { Err(CheckError::Timeout) }
}
}).collect::<Vec<_>>(); }).collect::<Vec<_>>();
let responses = futures::future::join_all(responses).await; let responses = futures::future::join_all(responses).await;
let ok_count = responses.iter() let ok_count = responses.iter()
.filter(|res_tuple| { .filter(|response| { response.is_ok() })
res_tuple.0.is_ok()
})
.count(); .count();
let are_all_ok = ok_count == responses.len();
let are_non_ok = ok_count == 0; if ok_count == responses.len() {
if !are_all_ok { let rtt_ms = responses.iter()
return if are_non_ok { .filter_map(|response| response.as_ref().ok())
Err(CheckError::Timeout) .map(|response| response.1)
} else { .map(|duration| duration.as_millis() as u32)
Err(CheckError::PartialTimeout) .sum::<u32>() / responses.len() as u32;
}
return Ok(CandidateProfile {
candidate,
addrs,
rtt_ms,
});
} }
let rtt_ms = responses.iter() let ip_fails = responses.iter()
.map(|res_tuple| res_tuple.1) .filter_map(|response| response.err())
.map(|duration| duration.as_millis() as u32) .filter(|err| err == &CheckError::IncorrectMappingReturned)
.sum::<u32>() / responses.len() as u32; .count();
Ok(CandidateProfile { if ip_fails > 0 {
candidate, return Err(CheckError::IncorrectMappingReturned);
addrs, }
rtt_ms
}) let timeouts = responses.iter()
} .filter_map(|response| response.err())
.filter(|err| err == &CheckError::Timeout)
.count();
if timeouts < responses.len() {
return Err(CheckError::PartialTimeout);
}
return Err(CheckError::Timeout);
}