net: allow dial randomly

This commit is contained in:
eric
2022-05-07 17:50:32 +08:00
parent 09775d6b12
commit c6f0b5b31e
3 changed files with 38 additions and 7 deletions

View File

@@ -1,13 +1,16 @@
use std::net::{IpAddr, SocketAddr};
use std::net::SocketAddr;
use anyhow::{anyhow, Result};
use futures::TryFutureExt;
use rand::prelude::SliceRandom;
use rand::rngs::StdRng;
use rand::SeedableRng;
use crate::app::SyncDnsClient;
use crate::proxy::DialOrder;
pub struct Resolver {
ips: Vec<IpAddr>,
port: u16,
addrs: Vec<SocketAddr>,
}
impl Resolver {
@@ -24,10 +27,17 @@ impl Resolver {
.map_err(|e| anyhow!("lookup {} failed: {}", address, e))
.await?
};
ips.reverse();
match *crate::option::OUTBOUND_DIAL_ORDER {
DialOrder::Ordered => ips.reverse(),
DialOrder::Random => ips.shuffle(&mut StdRng::from_entropy()),
DialOrder::PartialRandom => {
let head = ips.remove(0);
ips.shuffle(&mut StdRng::from_entropy());
ips.push(head);
}
}
Ok(Resolver {
ips,
port: port.to_owned(),
addrs: ips.into_iter().map(|x| SocketAddr::new(x, *port)).collect(),
})
}
}
@@ -36,6 +46,6 @@ impl Iterator for Resolver {
type Item = SocketAddr;
fn next(&mut self) -> Option<Self::Item> {
self.ips.pop().map(|ip| SocketAddr::new(ip, self.port))
self.addrs.pop()
}
}

View File

@@ -108,6 +108,14 @@ lazy_static! {
get_env_var_or("OUTBOUND_DIAL_TIMEOUT", 4)
};
pub static ref OUTBOUND_DIAL_ORDER: crate::proxy::DialOrder = {
match get_env_var_or("OUTBOUND_DIAL_ORDER", "ordered".to_string()).as_str() {
"random" => crate::proxy::DialOrder::Random,
"partial-random" => crate::proxy::DialOrder::PartialRandom,
_ => crate::proxy::DialOrder::Ordered,
}
};
/// Maximum outbound dial concurrency.
pub static ref OUTBOUND_DIAL_CONCURRENCY: usize = {
get_env_var_or("OUTBOUND_DIAL_CONCURRENCY", 1)

View File

@@ -332,6 +332,19 @@ fn apply_socket_opts<S: AsRawSocket>(socket: &S) -> io::Result<()> {
apply_socket_opts_internal(sock_ref)
}
// TCP dial order.
#[derive(PartialEq)]
pub enum DialOrder {
// Leave the order of IPs untouched.
Ordered,
// Randomize the IPs.
Random,
// Randomize the IPs except the first one. We have a little optimization in
// the DNS client that moves the previously connected IP to the head, we want
// that IP always tried first.
PartialRandom,
}
// A single TCP dial.
async fn tcp_dial_task(dial_addr: SocketAddr) -> io::Result<(AnyStream, SocketAddr)> {
let socket = match dial_addr {