feat(dns): Add flag to specify if we should prefer IPv4 over IPv6
This commit is contained in:
parent
90d378e768
commit
6351cd9e52
4 changed files with 123 additions and 64 deletions
97
src/dns.rs
97
src/dns.rs
|
@ -15,10 +15,11 @@ use std::time::Duration;
|
||||||
use tokio::net::{TcpStream, UdpSocket};
|
use tokio::net::{TcpStream, UdpSocket};
|
||||||
use url::{Host, Url};
|
use url::{Host, Url};
|
||||||
|
|
||||||
// Interweave v4 and v6 addresses as per RFC8305.
|
// Interleave v4 and v6 addresses as per RFC8305.
|
||||||
// The first address is v6 if we have any v6 addresses.
|
// The first address is v6 if we have any v6 addresses.
|
||||||
pub fn sort_socket_addrs(socket_addrs: &[SocketAddr]) -> impl Iterator<Item = &'_ SocketAddr> {
|
#[inline]
|
||||||
let mut pick_v6 = false;
|
fn sort_socket_addrs(socket_addrs: &[SocketAddr], prefer_ipv6: bool) -> impl Iterator<Item = &'_ SocketAddr> {
|
||||||
|
let mut pick_v6 = !prefer_ipv6;
|
||||||
let mut v6 = socket_addrs.iter().filter(|s| matches!(s, SocketAddr::V6(_)));
|
let mut v6 = socket_addrs.iter().filter(|s| matches!(s, SocketAddr::V6(_)));
|
||||||
let mut v4 = socket_addrs.iter().filter(|s| matches!(s, SocketAddr::V4(_)));
|
let mut v4 = socket_addrs.iter().filter(|s| matches!(s, SocketAddr::V4(_)));
|
||||||
std::iter::from_fn(move || {
|
std::iter::from_fn(move || {
|
||||||
|
@ -34,28 +35,39 @@ pub fn sort_socket_addrs(socket_addrs: &[SocketAddr]) -> impl Iterator<Item = &'
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub enum DnsResolver {
|
pub enum DnsResolver {
|
||||||
System,
|
System,
|
||||||
TrustDns(AsyncResolver<GenericConnector<TokioRuntimeProviderWithSoMark>>),
|
TrustDns {
|
||||||
|
resolver: AsyncResolver<GenericConnector<TokioRuntimeProviderWithSoMark>>,
|
||||||
|
prefer_ipv6: bool,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DnsResolver {
|
impl DnsResolver {
|
||||||
pub async fn lookup_host(&self, domain: &str, port: u16) -> anyhow::Result<Vec<SocketAddr>> {
|
pub async fn lookup_host(&self, domain: &str, port: u16) -> anyhow::Result<Vec<SocketAddr>> {
|
||||||
let addrs: Vec<SocketAddr> = match self {
|
let addrs: Vec<SocketAddr> = match self {
|
||||||
Self::System => tokio::net::lookup_host(format!("{}:{}", domain, port)).await?.collect(),
|
Self::System => tokio::net::lookup_host(format!("{}:{}", domain, port)).await?.collect(),
|
||||||
Self::TrustDns(dns_resolver) => dns_resolver
|
Self::TrustDns { resolver, prefer_ipv6 } => {
|
||||||
.lookup_ip(domain)
|
let addrs: Vec<_> = resolver
|
||||||
.await?
|
.lookup_ip(domain)
|
||||||
.into_iter()
|
.await?
|
||||||
.map(|ip| match ip {
|
.into_iter()
|
||||||
IpAddr::V4(ip) => SocketAddr::V4(SocketAddrV4::new(ip, port)),
|
.map(|ip| match ip {
|
||||||
IpAddr::V6(ip) => SocketAddr::V6(SocketAddrV6::new(ip, port, 0, 0)),
|
IpAddr::V4(ip) => SocketAddr::V4(SocketAddrV4::new(ip, port)),
|
||||||
})
|
IpAddr::V6(ip) => SocketAddr::V6(SocketAddrV6::new(ip, port, 0, 0)),
|
||||||
.collect(),
|
})
|
||||||
|
.collect();
|
||||||
|
sort_socket_addrs(&addrs, *prefer_ipv6).copied().collect()
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(addrs)
|
Ok(addrs)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_from_urls(resolvers: &[Url], proxy: Option<Url>, so_mark: Option<u32>) -> anyhow::Result<Self> {
|
pub fn new_from_urls(
|
||||||
|
resolvers: &[Url],
|
||||||
|
proxy: Option<Url>,
|
||||||
|
so_mark: Option<u32>,
|
||||||
|
prefer_ipv6: bool,
|
||||||
|
) -> anyhow::Result<Self> {
|
||||||
if resolvers.is_empty() {
|
if resolvers.is_empty() {
|
||||||
// no dns resolver specified, fall-back to default one
|
// no dns resolver specified, fall-back to default one
|
||||||
let Ok((cfg, mut opts)) = hickory_resolver::system_conf::read_system_conf() else {
|
let Ok((cfg, mut opts)) = hickory_resolver::system_conf::read_system_conf() else {
|
||||||
|
@ -63,7 +75,8 @@ impl DnsResolver {
|
||||||
return Ok(Self::System);
|
return Ok(Self::System);
|
||||||
};
|
};
|
||||||
|
|
||||||
opts.timeout = std::time::Duration::from_secs(1);
|
opts.ip_strategy = LookupIpStrategy::Ipv4AndIpv6;
|
||||||
|
opts.timeout = Duration::from_secs(1);
|
||||||
// Windows end-up with too many dns resolvers, which causes a performance issue
|
// Windows end-up with too many dns resolvers, which causes a performance issue
|
||||||
// https://github.com/hickory-dns/hickory-dns/issues/1968
|
// https://github.com/hickory-dns/hickory-dns/issues/1968
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
|
@ -71,11 +84,14 @@ impl DnsResolver {
|
||||||
opts.cache_size = 1024;
|
opts.cache_size = 1024;
|
||||||
opts.num_concurrent_reqs = cfg.name_servers().len();
|
opts.num_concurrent_reqs = cfg.name_servers().len();
|
||||||
}
|
}
|
||||||
return Ok(Self::TrustDns(AsyncResolver::new(
|
return Ok(Self::TrustDns {
|
||||||
cfg,
|
resolver: AsyncResolver::new(
|
||||||
opts,
|
cfg,
|
||||||
GenericConnector::new(TokioRuntimeProviderWithSoMark::new(proxy, so_mark)),
|
opts,
|
||||||
)));
|
GenericConnector::new(TokioRuntimeProviderWithSoMark::new(proxy, so_mark)),
|
||||||
|
),
|
||||||
|
prefer_ipv6,
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// if one is specified as system, use the default one from libc
|
// if one is specified as system, use the default one from libc
|
||||||
|
@ -127,13 +143,16 @@ impl DnsResolver {
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut opts = ResolverOpts::default();
|
let mut opts = ResolverOpts::default();
|
||||||
opts.timeout = std::time::Duration::from_secs(1);
|
opts.timeout = Duration::from_secs(1);
|
||||||
opts.ip_strategy = LookupIpStrategy::Ipv4AndIpv6;
|
opts.ip_strategy = LookupIpStrategy::Ipv4AndIpv6;
|
||||||
Ok(Self::TrustDns(AsyncResolver::new(
|
Ok(Self::TrustDns {
|
||||||
cfg,
|
resolver: AsyncResolver::new(
|
||||||
opts,
|
cfg,
|
||||||
GenericConnector::new(TokioRuntimeProviderWithSoMark::new(proxy, so_mark)),
|
opts,
|
||||||
)))
|
GenericConnector::new(TokioRuntimeProviderWithSoMark::new(proxy, so_mark)),
|
||||||
|
),
|
||||||
|
prefer_ipv6,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -235,3 +254,29 @@ impl RuntimeProvider for TokioRuntimeProviderWithSoMark {
|
||||||
Box::pin(socket)
|
Box::pin(socket)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::dns::sort_socket_addrs;
|
||||||
|
use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_sort_socket_addrs() {
|
||||||
|
let addrs = [
|
||||||
|
SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 1)),
|
||||||
|
SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 2), 1)),
|
||||||
|
SocketAddr::V6(SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 127, 0, 0, 1), 1, 0, 0)),
|
||||||
|
SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 3), 1)),
|
||||||
|
SocketAddr::V6(SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 127, 0, 0, 2), 1, 0, 0)),
|
||||||
|
];
|
||||||
|
let expected = [
|
||||||
|
SocketAddr::V6(SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 127, 0, 0, 1), 1, 0, 0)),
|
||||||
|
SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 1)),
|
||||||
|
SocketAddr::V6(SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 127, 0, 0, 2), 1, 0, 0)),
|
||||||
|
SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 2), 1)),
|
||||||
|
SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 3), 1)),
|
||||||
|
];
|
||||||
|
let actual: Vec<_> = sort_socket_addrs(&addrs, true).copied().collect();
|
||||||
|
assert_eq!(expected, *actual);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
49
src/main.rs
49
src/main.rs
|
@ -258,6 +258,17 @@ struct Client {
|
||||||
/// **WARN** On windows you may want to specify explicitly the DNS resolver to avoid excessive DNS queries
|
/// **WARN** On windows you may want to specify explicitly the DNS resolver to avoid excessive DNS queries
|
||||||
#[arg(long, verbatim_doc_comment)]
|
#[arg(long, verbatim_doc_comment)]
|
||||||
dns_resolver: Vec<Url>,
|
dns_resolver: Vec<Url>,
|
||||||
|
|
||||||
|
/// Enable if you prefer the dns resolver to prioritize IPv4 over IPv6
|
||||||
|
/// This is useful if you have a broken IPv6 connection, and want to avoid the delay of trying to connect to IPv6
|
||||||
|
/// If you don't have any IPv6 this does not change anything.
|
||||||
|
#[arg(
|
||||||
|
long,
|
||||||
|
default_value = "false",
|
||||||
|
env = "WSTUNNEL_DNS_PREFER_IPV4",
|
||||||
|
verbatim_doc_comment
|
||||||
|
)]
|
||||||
|
dns_resolver_prefer_ipv4: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(clap::Args, Debug)]
|
#[derive(clap::Args, Debug)]
|
||||||
|
@ -295,6 +306,17 @@ struct Server {
|
||||||
#[arg(long, verbatim_doc_comment)]
|
#[arg(long, verbatim_doc_comment)]
|
||||||
dns_resolver: Vec<Url>,
|
dns_resolver: Vec<Url>,
|
||||||
|
|
||||||
|
/// Enable if you prefer the dns resolver to prioritize IPv4 over IPv6
|
||||||
|
/// This is useful if you have a broken IPv6 connection, and want to avoid the delay of trying to connect to IPv6
|
||||||
|
/// If you don't have any IPv6 this does not change anything.
|
||||||
|
#[arg(
|
||||||
|
long,
|
||||||
|
default_value = "false",
|
||||||
|
env = "WSTUNNEL_DNS_PREFER_IPV4",
|
||||||
|
verbatim_doc_comment
|
||||||
|
)]
|
||||||
|
dns_resolver_prefer_ipv4: bool,
|
||||||
|
|
||||||
/// Server will only accept connection from the specified tunnel information.
|
/// Server will only accept connection from the specified tunnel information.
|
||||||
/// Can be specified multiple time
|
/// Can be specified multiple time
|
||||||
/// Example: --restrict-to "google.com:443" --restrict-to "localhost:22"
|
/// Example: --restrict-to "google.com:443" --restrict-to "localhost:22"
|
||||||
|
@ -755,8 +777,13 @@ impl WsClientConfig {
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
let args = Wstunnel::parse();
|
let args = Wstunnel::parse();
|
||||||
let socket = UdpSocket::bind(SocketAddrV6::new(Ipv6Addr::UNSPECIFIED, 0, 0, 0)).await.unwrap();
|
let socket = UdpSocket::bind(SocketAddrV6::new(Ipv6Addr::UNSPECIFIED, 0, 0, 0))
|
||||||
socket.connect("[2001:4810:0:3::78]:443".parse::<SocketAddr>().unwrap()).await.unwrap();
|
.await
|
||||||
|
.unwrap();
|
||||||
|
socket
|
||||||
|
.connect("[2001:4810:0:3::78]:443".parse::<SocketAddr>().unwrap())
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
// Setup logging
|
// Setup logging
|
||||||
let mut env_filter = EnvFilter::builder().parse(&args.log_lvl).expect("Invalid log level");
|
let mut env_filter = EnvFilter::builder().parse(&args.log_lvl).expect("Invalid log level");
|
||||||
|
@ -902,8 +929,13 @@ async fn main() {
|
||||||
websocket_mask_frame: args.websocket_mask_frame,
|
websocket_mask_frame: args.websocket_mask_frame,
|
||||||
cnx_pool: None,
|
cnx_pool: None,
|
||||||
tls_reloader: None,
|
tls_reloader: None,
|
||||||
dns_resolver: DnsResolver::new_from_urls(&args.dns_resolver, http_proxy.clone(), args.socket_so_mark)
|
dns_resolver: DnsResolver::new_from_urls(
|
||||||
.expect("cannot create dns resolver"),
|
&args.dns_resolver,
|
||||||
|
http_proxy.clone(),
|
||||||
|
args.socket_so_mark,
|
||||||
|
!args.dns_resolver_prefer_ipv4,
|
||||||
|
)
|
||||||
|
.expect("cannot create dns resolver"),
|
||||||
http_proxy,
|
http_proxy,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1324,8 +1356,13 @@ async fn main() {
|
||||||
timeout_connect: Duration::from_secs(10),
|
timeout_connect: Duration::from_secs(10),
|
||||||
websocket_mask_frame: args.websocket_mask_frame,
|
websocket_mask_frame: args.websocket_mask_frame,
|
||||||
tls: tls_config,
|
tls: tls_config,
|
||||||
dns_resolver: DnsResolver::new_from_urls(&args.dns_resolver, None, args.socket_so_mark)
|
dns_resolver: DnsResolver::new_from_urls(
|
||||||
.expect("Cannot create DNS resolver"),
|
&args.dns_resolver,
|
||||||
|
None,
|
||||||
|
args.socket_so_mark,
|
||||||
|
!args.dns_resolver_prefer_ipv4,
|
||||||
|
)
|
||||||
|
.expect("Cannot create DNS resolver"),
|
||||||
restriction_config: args.restrict_config,
|
restriction_config: args.restrict_config,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
32
src/tcp.rs
32
src/tcp.rs
|
@ -2,7 +2,7 @@ use anyhow::{anyhow, Context};
|
||||||
use std::{io, vec};
|
use std::{io, vec};
|
||||||
use tokio::task::JoinSet;
|
use tokio::task::JoinSet;
|
||||||
|
|
||||||
use crate::dns::{self, DnsResolver};
|
use crate::dns::DnsResolver;
|
||||||
use base64::Engine;
|
use base64::Engine;
|
||||||
use bytes::BytesMut;
|
use bytes::BytesMut;
|
||||||
use log::warn;
|
use log::warn;
|
||||||
|
@ -73,14 +73,11 @@ pub async fn connect(
|
||||||
let mut last_err = None;
|
let mut last_err = None;
|
||||||
let mut join_set = JoinSet::new();
|
let mut join_set = JoinSet::new();
|
||||||
|
|
||||||
for (ix, addr) in dns::sort_socket_addrs(&socket_addrs).copied().enumerate() {
|
for (ix, addr) in socket_addrs.into_iter().enumerate() {
|
||||||
debug!("Connecting to {}", addr);
|
|
||||||
|
|
||||||
let socket = match &addr {
|
let socket = match &addr {
|
||||||
SocketAddr::V4(_) => TcpSocket::new_v4()?,
|
SocketAddr::V4(_) => TcpSocket::new_v4()?,
|
||||||
SocketAddr::V6(_) => TcpSocket::new_v6()?,
|
SocketAddr::V6(_) => TcpSocket::new_v6()?,
|
||||||
};
|
};
|
||||||
|
|
||||||
configure_socket(socket2::SockRef::from(&socket), &so_mark)?;
|
configure_socket(socket2::SockRef::from(&socket), &so_mark)?;
|
||||||
|
|
||||||
// Spawn the connection attempt in the join set.
|
// Spawn the connection attempt in the join set.
|
||||||
|
@ -90,6 +87,7 @@ pub async fn connect(
|
||||||
if ix > 0 {
|
if ix > 0 {
|
||||||
sleep(Duration::from_millis(250 * ix as u64)).await;
|
sleep(Duration::from_millis(250 * ix as u64)).await;
|
||||||
}
|
}
|
||||||
|
debug!("Connecting to {}", addr);
|
||||||
match timeout(connect_timeout, socket.connect(addr)).await {
|
match timeout(connect_timeout, socket.connect(addr)).await {
|
||||||
Ok(Ok(s)) => Ok(Ok(s)),
|
Ok(Ok(s)) => Ok(Ok(s)),
|
||||||
Ok(Err(e)) => Ok(Err((addr, e))),
|
Ok(Err(e)) => Ok(Err((addr, e))),
|
||||||
|
@ -107,7 +105,7 @@ pub async fn connect(
|
||||||
match res? {
|
match res? {
|
||||||
Ok(Ok(stream)) => {
|
Ok(Ok(stream)) => {
|
||||||
// We've got a successful connection, so we can abort all other
|
// We've got a successful connection, so we can abort all other
|
||||||
// on-going attempts.
|
// ongoing attempts.
|
||||||
join_set.abort_all();
|
join_set.abort_all();
|
||||||
|
|
||||||
debug!(
|
debug!(
|
||||||
|
@ -227,7 +225,7 @@ pub async fn run_server(bind: SocketAddr, ip_transparent: bool) -> Result<TcpLis
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use futures_util::pin_mut;
|
use futures_util::pin_mut;
|
||||||
use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr};
|
use std::net::SocketAddr;
|
||||||
use testcontainers::core::WaitFor;
|
use testcontainers::core::WaitFor;
|
||||||
use testcontainers::runners::AsyncRunner;
|
use testcontainers::runners::AsyncRunner;
|
||||||
use testcontainers::{ContainerAsync, Image, ImageArgs, RunnableImage};
|
use testcontainers::{ContainerAsync, Image, ImageArgs, RunnableImage};
|
||||||
|
@ -259,26 +257,6 @@ mod tests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_sort_socket_addrs() {
|
|
||||||
let addrs = [
|
|
||||||
SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 1)),
|
|
||||||
SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 2), 1)),
|
|
||||||
SocketAddr::V6(SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 127, 0, 0, 1), 1, 0, 0)),
|
|
||||||
SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 3), 1)),
|
|
||||||
SocketAddr::V6(SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 127, 0, 0, 2), 1, 0, 0)),
|
|
||||||
];
|
|
||||||
let expected = [
|
|
||||||
SocketAddr::V6(SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 127, 0, 0, 1), 1, 0, 0)),
|
|
||||||
SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 1)),
|
|
||||||
SocketAddr::V6(SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 127, 0, 0, 2), 1, 0, 0)),
|
|
||||||
SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 2), 1)),
|
|
||||||
SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 3), 1)),
|
|
||||||
];
|
|
||||||
let actual: Vec<_> = dns::sort_socket_addrs(&addrs).copied().collect();
|
|
||||||
assert_eq!(expected, *actual);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_proxy_connection() {
|
async fn test_proxy_connection() {
|
||||||
let server_addr: SocketAddr = "[::1]:1236".parse().unwrap();
|
let server_addr: SocketAddr = "[::1]:1236".parse().unwrap();
|
||||||
|
|
|
@ -19,7 +19,7 @@ use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
|
||||||
use tokio::net::UdpSocket;
|
use tokio::net::UdpSocket;
|
||||||
use tokio::sync::futures::Notified;
|
use tokio::sync::futures::Notified;
|
||||||
|
|
||||||
use crate::dns::{self, DnsResolver};
|
use crate::dns::DnsResolver;
|
||||||
use tokio::sync::Notify;
|
use tokio::sync::Notify;
|
||||||
use tokio::time::{sleep, timeout, Interval};
|
use tokio::time::{sleep, timeout, Interval};
|
||||||
use tracing::{debug, error, info};
|
use tracing::{debug, error, info};
|
||||||
|
@ -340,9 +340,7 @@ pub async fn connect(
|
||||||
let mut last_err = None;
|
let mut last_err = None;
|
||||||
let mut join_set = JoinSet::new();
|
let mut join_set = JoinSet::new();
|
||||||
|
|
||||||
for (ix, addr) in dns::sort_socket_addrs(&socket_addrs).copied().enumerate() {
|
for (ix, addr) in socket_addrs.into_iter().enumerate() {
|
||||||
debug!("connecting to {}", addr);
|
|
||||||
|
|
||||||
let socket = match &addr {
|
let socket = match &addr {
|
||||||
SocketAddr::V4(_) => UdpSocket::bind(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, 0)).await,
|
SocketAddr::V4(_) => UdpSocket::bind(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, 0)).await,
|
||||||
SocketAddr::V6(_) => UdpSocket::bind(SocketAddrV6::new(Ipv6Addr::UNSPECIFIED, 0, 0, 0)).await,
|
SocketAddr::V6(_) => UdpSocket::bind(SocketAddrV6::new(Ipv6Addr::UNSPECIFIED, 0, 0, 0)).await,
|
||||||
|
@ -364,6 +362,7 @@ pub async fn connect(
|
||||||
sleep(Duration::from_millis(250 * ix as u64)).await;
|
sleep(Duration::from_millis(250 * ix as u64)).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
debug!("connecting to {}", addr);
|
||||||
match timeout(connect_timeout, socket.connect(addr)).await {
|
match timeout(connect_timeout, socket.connect(addr)).await {
|
||||||
Ok(Ok(())) => Ok(Ok(socket)),
|
Ok(Ok(())) => Ok(Ok(socket)),
|
||||||
Ok(Err(e)) => Ok(Err((addr, e))),
|
Ok(Err(e)) => Ok(Err((addr, e))),
|
||||||
|
@ -381,7 +380,7 @@ pub async fn connect(
|
||||||
match res? {
|
match res? {
|
||||||
Ok(Ok(socket)) => {
|
Ok(Ok(socket)) => {
|
||||||
// We've got a successful connection, so we can abort all other
|
// We've got a successful connection, so we can abort all other
|
||||||
// on-going attempts.
|
// ongoing attempts.
|
||||||
join_set.abort_all();
|
join_set.abort_all();
|
||||||
|
|
||||||
debug!(
|
debug!(
|
||||||
|
|
Loading…
Reference in a new issue