From 47f30c7ad23cea40548d9a4075f63a2ecad90bd5 Mon Sep 17 00:00:00 2001 From: Stennsen Date: Sat, 6 Jul 2024 19:42:14 +0200 Subject: [PATCH 1/9] implement prefer_ipv6 option --- src/client.rs | 11 ++++++++--- src/config.rs | 2 ++ src/helper.rs | 41 ++++++++++++++++++++++++++++++++++------- 3 files changed, 44 insertions(+), 10 deletions(-) diff --git a/src/client.rs b/src/client.rs index 2564869c..c63e5f4e 100644 --- a/src/client.rs +++ b/src/client.rs @@ -110,6 +110,7 @@ impl Client { let handle = ControlChannelHandle::new( (*config).clone(), self.config.remote_addr.clone(), + self.config.prefer_ipv6, self.transport.clone(), self.config.heartbeat_timeout, ); @@ -152,6 +153,7 @@ impl Client { let handle = ControlChannelHandle::new( cfg, self.config.remote_addr.clone(), + self.config.prefer_ipv6, self.transport.clone(), self.config.heartbeat_timeout, ); @@ -227,7 +229,7 @@ async fn run_data_channel(args: Arc>) -> Res if args.service.service_type != ServiceType::Udp { bail!("Expect UDP traffic. Please check the configuration.") } - run_data_channel_for_udp::(conn, &args.service.local_addr).await?; + run_data_channel_for_udp::(conn, &args.service.local_addr, args.service.prefer_ipv6).await?; } } Ok(()) @@ -255,7 +257,7 @@ async fn run_data_channel_for_tcp( type UdpPortMap = Arc>>>; #[instrument(skip(conn))] -async fn run_data_channel_for_udp(conn: T::Stream, local_addr: &str) -> Result<()> { +async fn run_data_channel_for_udp(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())); @@ -305,7 +307,7 @@ async fn run_data_channel_for_udp(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); @@ -390,6 +392,7 @@ struct ControlChannel { service: ClientServiceConfig, // `[client.services.foo]` config block shutdown_rx: oneshot::Receiver, // Receives the shutdown signal remote_addr: String, // `client.remote_addr` + prefer_ipv6: Option, transport: Arc, // Wrapper around the transport layer heartbeat_timeout: u64, // Application layer heartbeat timeout in secs } @@ -499,6 +502,7 @@ impl ControlChannelHandle { fn new( service: ClientServiceConfig, remote_addr: String, + prefer_ipv6: Option, transport: Arc, heartbeat_timeout: u64, ) -> ControlChannelHandle { @@ -514,6 +518,7 @@ impl ControlChannelHandle { service, shutdown_rx, remote_addr, + prefer_ipv6, transport, heartbeat_timeout, }; diff --git a/src/config.rs b/src/config.rs index ca85fc20..c11430f0 100644 --- a/src/config.rs +++ b/src/config.rs @@ -63,6 +63,7 @@ pub struct ClientServiceConfig { #[serde(skip)] pub name: String, pub local_addr: String, + pub prefer_ipv6: bool, pub token: Option, pub nodelay: Option, pub retry_interval: Option, @@ -201,6 +202,7 @@ fn default_client_retry_interval() -> u64 { pub struct ClientConfig { pub remote_addr: String, pub default_token: Option, + pub prefer_ipv6: Option, pub services: HashMap, #[serde(default)] pub transport: TransportConfig, diff --git a/src/helper.rs b/src/helper.rs index a292969f..17631bc4 100644 --- a/src/helper.rs +++ b/src/helper.rs @@ -64,16 +64,43 @@ pub fn host_port_pair(s: &str) -> Result<(&str, u16)> { } /// Create a UDP socket and connect to `addr` -pub async fn udp_connect(addr: A) -> Result { - let addr = to_socket_addr(addr).await?; +pub async fn udp_connect(addr: A, prefer_ipv6: bool) -> Result { - 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 = 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.clone(); + 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")), + Some(socket_addr_ipv4) => { + socket_addr = socket_addr_ipv4.clone(); + bind_addr = "0.0.0.0:0"; + } + } + } + } + } + }; let s = UdpSocket::bind(bind_addr).await?; - s.connect(addr).await?; + s.connect(socket_addr).await?; Ok(s) } From 3b5e9d099d33f22d3a17479ea8652eb08ac4a12e Mon Sep 17 00:00:00 2001 From: Stennsen Date: Sat, 6 Jul 2024 19:42:14 +0200 Subject: [PATCH 2/9] implement prefer_ipv6 option --- src/client.rs | 11 ++++++++--- src/config.rs | 2 ++ src/helper.rs | 41 ++++++++++++++++++++++++++++++++++------- 3 files changed, 44 insertions(+), 10 deletions(-) diff --git a/src/client.rs b/src/client.rs index 2564869c..c63e5f4e 100644 --- a/src/client.rs +++ b/src/client.rs @@ -110,6 +110,7 @@ impl Client { let handle = ControlChannelHandle::new( (*config).clone(), self.config.remote_addr.clone(), + self.config.prefer_ipv6, self.transport.clone(), self.config.heartbeat_timeout, ); @@ -152,6 +153,7 @@ impl Client { let handle = ControlChannelHandle::new( cfg, self.config.remote_addr.clone(), + self.config.prefer_ipv6, self.transport.clone(), self.config.heartbeat_timeout, ); @@ -227,7 +229,7 @@ async fn run_data_channel(args: Arc>) -> Res if args.service.service_type != ServiceType::Udp { bail!("Expect UDP traffic. Please check the configuration.") } - run_data_channel_for_udp::(conn, &args.service.local_addr).await?; + run_data_channel_for_udp::(conn, &args.service.local_addr, args.service.prefer_ipv6).await?; } } Ok(()) @@ -255,7 +257,7 @@ async fn run_data_channel_for_tcp( type UdpPortMap = Arc>>>; #[instrument(skip(conn))] -async fn run_data_channel_for_udp(conn: T::Stream, local_addr: &str) -> Result<()> { +async fn run_data_channel_for_udp(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())); @@ -305,7 +307,7 @@ async fn run_data_channel_for_udp(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); @@ -390,6 +392,7 @@ struct ControlChannel { service: ClientServiceConfig, // `[client.services.foo]` config block shutdown_rx: oneshot::Receiver, // Receives the shutdown signal remote_addr: String, // `client.remote_addr` + prefer_ipv6: Option, transport: Arc, // Wrapper around the transport layer heartbeat_timeout: u64, // Application layer heartbeat timeout in secs } @@ -499,6 +502,7 @@ impl ControlChannelHandle { fn new( service: ClientServiceConfig, remote_addr: String, + prefer_ipv6: Option, transport: Arc, heartbeat_timeout: u64, ) -> ControlChannelHandle { @@ -514,6 +518,7 @@ impl ControlChannelHandle { service, shutdown_rx, remote_addr, + prefer_ipv6, transport, heartbeat_timeout, }; diff --git a/src/config.rs b/src/config.rs index ca85fc20..c11430f0 100644 --- a/src/config.rs +++ b/src/config.rs @@ -63,6 +63,7 @@ pub struct ClientServiceConfig { #[serde(skip)] pub name: String, pub local_addr: String, + pub prefer_ipv6: bool, pub token: Option, pub nodelay: Option, pub retry_interval: Option, @@ -201,6 +202,7 @@ fn default_client_retry_interval() -> u64 { pub struct ClientConfig { pub remote_addr: String, pub default_token: Option, + pub prefer_ipv6: Option, pub services: HashMap, #[serde(default)] pub transport: TransportConfig, diff --git a/src/helper.rs b/src/helper.rs index a292969f..17631bc4 100644 --- a/src/helper.rs +++ b/src/helper.rs @@ -64,16 +64,43 @@ pub fn host_port_pair(s: &str) -> Result<(&str, u16)> { } /// Create a UDP socket and connect to `addr` -pub async fn udp_connect(addr: A) -> Result { - let addr = to_socket_addr(addr).await?; +pub async fn udp_connect(addr: A, prefer_ipv6: bool) -> Result { - 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 = 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.clone(); + 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")), + Some(socket_addr_ipv4) => { + socket_addr = socket_addr_ipv4.clone(); + bind_addr = "0.0.0.0:0"; + } + } + } + } + } + }; let s = UdpSocket::bind(bind_addr).await?; - s.connect(addr).await?; + s.connect(socket_addr).await?; Ok(s) } From d8980193d11aef2d6af20f5ea36611ee634ed1a4 Mon Sep 17 00:00:00 2001 From: Stennsen Date: Sat, 6 Jul 2024 20:19:22 +0200 Subject: [PATCH 3/9] remove unused prefer_ipv6 --- src/client.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/client.rs b/src/client.rs index c63e5f4e..ef695b32 100644 --- a/src/client.rs +++ b/src/client.rs @@ -110,7 +110,6 @@ impl Client { let handle = ControlChannelHandle::new( (*config).clone(), self.config.remote_addr.clone(), - self.config.prefer_ipv6, self.transport.clone(), self.config.heartbeat_timeout, ); @@ -153,7 +152,6 @@ impl Client { let handle = ControlChannelHandle::new( cfg, self.config.remote_addr.clone(), - self.config.prefer_ipv6, self.transport.clone(), self.config.heartbeat_timeout, ); @@ -392,7 +390,6 @@ struct ControlChannel { service: ClientServiceConfig, // `[client.services.foo]` config block shutdown_rx: oneshot::Receiver, // Receives the shutdown signal remote_addr: String, // `client.remote_addr` - prefer_ipv6: Option, transport: Arc, // Wrapper around the transport layer heartbeat_timeout: u64, // Application layer heartbeat timeout in secs } @@ -502,7 +499,6 @@ impl ControlChannelHandle { fn new( service: ClientServiceConfig, remote_addr: String, - prefer_ipv6: Option, transport: Arc, heartbeat_timeout: u64, ) -> ControlChannelHandle { @@ -518,7 +514,6 @@ impl ControlChannelHandle { service, shutdown_rx, remote_addr, - prefer_ipv6, transport, heartbeat_timeout, }; From cdaf2c5f0304c5dd7253d1694dc7873c7dc16955 Mon Sep 17 00:00:00 2001 From: Stennsen Date: Sat, 6 Jul 2024 20:19:36 +0200 Subject: [PATCH 4/9] add prefer_ipv6 to sample config --- src/helper.rs | 1 + tests/config_test/valid_config/full.toml | 1 + 2 files changed, 2 insertions(+) diff --git a/src/helper.rs b/src/helper.rs index 17631bc4..937af897 100644 --- a/src/helper.rs +++ b/src/helper.rs @@ -90,6 +90,7 @@ pub async fn udp_connect(addr: A, prefer_ipv6: bool) -> Result 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.clone(); bind_addr = "0.0.0.0:0"; diff --git a/tests/config_test/valid_config/full.toml b/tests/config_test/valid_config/full.toml index 88cdf678..196a0df5 100644 --- a/tests/config_test/valid_config/full.toml +++ b/tests/config_test/valid_config/full.toml @@ -1,6 +1,7 @@ [client] remote_addr = "example.com:2333" # Necessary. The address of the server default_token = "default_token_if_not_specify" # Optional. The default token of services, if they don't define their own ones +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 [client.transport] type = "tcp" # Optional. Possible values: ["tcp", "tls"]. Default: "tcp" From 994a7ee0263c8f486da4e2b07d7e24583da16afd Mon Sep 17 00:00:00 2001 From: Stennsen Date: Sat, 6 Jul 2024 20:24:43 +0200 Subject: [PATCH 5/9] fix merge --- src/helper.rs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/helper.rs b/src/helper.rs index 3cf0ef31..9a27e4fc 100644 --- a/src/helper.rs +++ b/src/helper.rs @@ -68,13 +68,6 @@ pub async fn udp_connect(addr: A, prefer_ipv6: bool) -> Result let (socket_addr, bind_addr); - match prefer_ipv6 { - false => { - socket_addr = to_socket_addr(addr).await?; -pub async fn udp_connect(addr: A, prefer_ipv6: bool) -> Result { - - let (socket_addr, bind_addr); - match prefer_ipv6 { false => { socket_addr = to_socket_addr(addr).await?; From 7563461b8157cebf90eff6a1e5ae09803cdd1a40 Mon Sep 17 00:00:00 2001 From: Stennsen Date: Sat, 6 Jul 2024 20:27:13 +0200 Subject: [PATCH 6/9] remove unused prefer_ipv6 --- src/client.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/client.rs b/src/client.rs index c63e5f4e..ef695b32 100644 --- a/src/client.rs +++ b/src/client.rs @@ -110,7 +110,6 @@ impl Client { let handle = ControlChannelHandle::new( (*config).clone(), self.config.remote_addr.clone(), - self.config.prefer_ipv6, self.transport.clone(), self.config.heartbeat_timeout, ); @@ -153,7 +152,6 @@ impl Client { let handle = ControlChannelHandle::new( cfg, self.config.remote_addr.clone(), - self.config.prefer_ipv6, self.transport.clone(), self.config.heartbeat_timeout, ); @@ -392,7 +390,6 @@ struct ControlChannel { service: ClientServiceConfig, // `[client.services.foo]` config block shutdown_rx: oneshot::Receiver, // Receives the shutdown signal remote_addr: String, // `client.remote_addr` - prefer_ipv6: Option, transport: Arc, // Wrapper around the transport layer heartbeat_timeout: u64, // Application layer heartbeat timeout in secs } @@ -502,7 +499,6 @@ impl ControlChannelHandle { fn new( service: ClientServiceConfig, remote_addr: String, - prefer_ipv6: Option, transport: Arc, heartbeat_timeout: u64, ) -> ControlChannelHandle { @@ -518,7 +514,6 @@ impl ControlChannelHandle { service, shutdown_rx, remote_addr, - prefer_ipv6, transport, heartbeat_timeout, }; From dc45ffb67080e997234f776b9f580e077c17a7f5 Mon Sep 17 00:00:00 2001 From: Stennsen Date: Sat, 6 Jul 2024 21:47:52 +0200 Subject: [PATCH 7/9] move prefer_piv6 to correct position in full.toml --- tests/config_test/valid_config/full.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/config_test/valid_config/full.toml b/tests/config_test/valid_config/full.toml index 196a0df5..c3bf43a4 100644 --- a/tests/config_test/valid_config/full.toml +++ b/tests/config_test/valid_config/full.toml @@ -1,7 +1,6 @@ [client] remote_addr = "example.com:2333" # Necessary. The address of the server default_token = "default_token_if_not_specify" # Optional. The default token of services, if they don't define their own ones -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 [client.transport] type = "tcp" # Optional. Possible values: ["tcp", "tls"]. Default: "tcp" @@ -18,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 From 1843e19d82eba3e64721b3955fdfbf36579d4499 Mon Sep 17 00:00:00 2001 From: Stennsen Date: Sat, 6 Jul 2024 22:02:03 +0200 Subject: [PATCH 8/9] set prefer_ipv6 default value --- src/config.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/config.rs b/src/config.rs index c11430f0..270fed55 100644 --- a/src/config.rs +++ b/src/config.rs @@ -63,6 +63,7 @@ pub struct ClientServiceConfig { #[serde(skip)] pub name: String, pub local_addr: String, + #[serde(default)] // Default to false pub prefer_ipv6: bool, pub token: Option, pub nodelay: Option, From 9f3c40a4b53f3a06cd0058ed63d34b27792c4b2b Mon Sep 17 00:00:00 2001 From: Stennsen Date: Sat, 6 Jul 2024 22:08:27 +0200 Subject: [PATCH 9/9] run clippy --- src/helper.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/helper.rs b/src/helper.rs index 9a27e4fc..11002813 100644 --- a/src/helper.rs +++ b/src/helper.rs @@ -83,7 +83,7 @@ pub async fn udp_connect(addr: A, prefer_ipv6: bool) -> Result // 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.clone(); + socket_addr = *socket_addr_ipv6; bind_addr = ":::0"; }, None => { @@ -92,7 +92,7 @@ pub async fn udp_connect(addr: A, prefer_ipv6: bool) -> Result None => return Err(anyhow!("Failed to lookup the host")), // fallback to IPv4 Some(socket_addr_ipv4) => { - socket_addr = socket_addr_ipv4.clone(); + socket_addr = *socket_addr_ipv4; bind_addr = "0.0.0.0:0"; } }