Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement prefer IPv6 option for the client #376

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
6 changes: 3 additions & 3 deletions src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@ async fn run_data_channel<T: Transport>(args: Arc<RunDataChannelArgs<T>>) -> Res
if args.service.service_type != ServiceType::Udp {
bail!("Expect UDP traffic. Please check the configuration.")
}
run_data_channel_for_udp::<T>(conn, &args.service.local_addr).await?;
run_data_channel_for_udp::<T>(conn, &args.service.local_addr, args.service.prefer_ipv6).await?;
}
}
Ok(())
Expand Down Expand Up @@ -255,7 +255,7 @@ async fn run_data_channel_for_tcp<T: Transport>(
type UdpPortMap = Arc<RwLock<HashMap<SocketAddr, mpsc::Sender<Bytes>>>>;

#[instrument(skip(conn))]
async fn run_data_channel_for_udp<T: Transport>(conn: T::Stream, local_addr: &str) -> Result<()> {
async fn run_data_channel_for_udp<T: Transport>(conn: T::Stream, local_addr: &str, prefer_ipv6: bool) -> Result<()> {
debug!("New data channel starts forwarding");

let port_map: UdpPortMap = Arc::new(RwLock::new(HashMap::new()));
Expand Down Expand Up @@ -305,7 +305,7 @@ async fn run_data_channel_for_udp<T: Transport>(conn: T::Stream, local_addr: &st
// grabbing the writer lock
let mut m = port_map.write().await;

match udp_connect(local_addr).await {
match udp_connect(local_addr, prefer_ipv6).await {
Ok(s) => {
let (inbound_tx, inbound_rx) = mpsc::channel(UDP_SENDQ_SIZE);
m.insert(packet.from, inbound_tx);
Expand Down
3 changes: 3 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ pub struct ClientServiceConfig {
#[serde(skip)]
pub name: String,
pub local_addr: String,
#[serde(default)] // Default to false
pub prefer_ipv6: bool,
pub token: Option<MaskedString>,
pub nodelay: Option<bool>,
pub retry_interval: Option<u64>,
Expand Down Expand Up @@ -201,6 +203,7 @@ fn default_client_retry_interval() -> u64 {
pub struct ClientConfig {
pub remote_addr: String,
pub default_token: Option<MaskedString>,
pub prefer_ipv6: Option<bool>,
pub services: HashMap<String, ClientServiceConfig>,
#[serde(default)]
pub transport: TransportConfig,
Expand Down
43 changes: 36 additions & 7 deletions src/helper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,16 +64,45 @@ pub fn host_port_pair(s: &str) -> Result<(&str, u16)> {
}

/// Create a UDP socket and connect to `addr`
pub async fn udp_connect<A: ToSocketAddrs>(addr: A) -> Result<UdpSocket> {
let addr = to_socket_addr(addr).await?;
pub async fn udp_connect<A: ToSocketAddrs>(addr: A, prefer_ipv6: bool) -> Result<UdpSocket> {

let bind_addr = match addr {
SocketAddr::V4(_) => "0.0.0.0:0",
SocketAddr::V6(_) => ":::0",
};
let (socket_addr, bind_addr);

match prefer_ipv6 {
false => {
socket_addr = to_socket_addr(addr).await?;

bind_addr = match socket_addr {
SocketAddr::V4(_) => "0.0.0.0:0",
SocketAddr::V6(_) => ":::0",
};
},
true => {
let all_host_addresses: Vec<SocketAddr> = lookup_host(addr).await?.collect();

// Try to find an IPv6 address
match all_host_addresses.clone().iter().find(|x| x.is_ipv6()) {
Some(socket_addr_ipv6) => {
socket_addr = *socket_addr_ipv6;
bind_addr = ":::0";
},
None => {
let socket_addr_ipv4 = all_host_addresses.iter().find(|x| x.is_ipv4());
match socket_addr_ipv4 {
None => return Err(anyhow!("Failed to lookup the host")),
// fallback to IPv4
Some(socket_addr_ipv4) => {
socket_addr = *socket_addr_ipv4;
bind_addr = "0.0.0.0:0";
}
}
}
}
}
};
let s = UdpSocket::bind(bind_addr).await?;
s.connect(addr).await?;
s.connect(socket_addr).await?;
s.connect(socket_addr).await?;
Ok(s)
}

Expand Down
1 change: 1 addition & 0 deletions tests/config_test/valid_config/full.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ remote_public_key = "key_encoded_in_base64" # Optional
[client.services.service1] # A service that needs forwarding. The name `service1` can change arbitrarily, as long as identical to the name in the server's configuration
type = "tcp" # Optional. The protocol that needs forwarding. Possible values: ["tcp", "udp"]. Default: "tcp"
token = "whatever" # Necessary if `client.default_token` not set
prefer_ipv6 = false # Optional. If the client prefers to use IPv6 when connecting to the server (e.g.: When the client is behind an ISP's NAT). Default: false
local_addr = "127.0.0.1:1081" # Necessary. The address of the service that needs to be forwarded

[client.services.service2] # Multiple services can be defined
Expand Down
Loading