Fix IPv6 TCP tunnel display formatting (#1980)

Normalize composite tunnel display values before rendering peer and
debug output so IPv6 tunnel types no longer append `6` to the port.

- Preserve prefixes like `txt-` while converting tunnel schemes to
  their IPv6 form.
- Recover malformed values such as `txt-tcp://...:110106` into
  `txt-tcp6://...:11010`.
- Reuse the normalized remote address display in CLI debug output.
This commit is contained in:
fanyang
2026-04-05 22:12:55 +08:00
committed by GitHub
parent 2cf2b0fcac
commit cf6dcbc054
16 changed files with 377 additions and 32 deletions
+11
View File
@@ -249,6 +249,13 @@ impl TunnelListener for FakeTcpTunnelListener {
)
.into(),
),
resolved_remote_addr: Some(
crate::tunnel::build_url_from_socket_addr(
&socket.remote_addr().to_string(),
"faketcp",
)
.into(),
),
};
// We treat the fake tcp socket as a datagram tunnel directly
@@ -366,6 +373,10 @@ impl TunnelConnector for FakeTcpTunnelConnector {
.into(),
),
remote_addr: Some(self.addr.clone().into()),
resolved_remote_addr: Some(
crate::tunnel::build_url_from_socket_addr(&remote_addr.to_string(), "faketcp")
.into(),
),
};
let socket = Arc::new(socket);
+2 -2
View File
@@ -11,7 +11,7 @@ use derive_more::{From, TryInto};
use futures::{Sink, Stream};
use socket2::Protocol;
use std::fmt::Debug;
use strum::{Display, EnumString, VariantArray};
use strum::{Display, EnumString, IntoStaticStr, VariantArray};
use tokio::time::error::Elapsed;
use self::packet_def::ZCPacket;
@@ -284,7 +284,7 @@ struct IpSchemeAttributes {
port_offset: u16,
}
#[derive(Debug, Clone, Copy, PartialEq, Display, EnumString, VariantArray)]
#[derive(Debug, Clone, Copy, PartialEq, Display, EnumString, IntoStaticStr, VariantArray)]
#[strum(serialize_all = "lowercase")]
pub enum IpScheme {
Tcp,
+7
View File
@@ -175,6 +175,9 @@ impl QuicTunnelListener {
remote_addr: Some(
super::build_url_from_socket_addr(&remote_addr.to_string(), "quic").into(),
),
resolved_remote_addr: Some(
super::build_url_from_socket_addr(&remote_addr.to_string(), "quic").into(),
),
};
Ok(Box::new(TunnelWrapper::new(
@@ -280,6 +283,10 @@ impl TunnelConnector for QuicTunnelConnector {
super::build_url_from_socket_addr(&local_addr.to_string(), "quic").into(),
),
remote_addr: Some(self.addr.clone().into()),
resolved_remote_addr: Some(
super::build_url_from_socket_addr(&connection.remote_address().to_string(), "quic")
.into(),
),
};
let arc_conn = Arc::new(ConnWrapper { conn: connection });
+6
View File
@@ -214,6 +214,9 @@ fn get_tunnel_for_client(conn: Arc<Connection>) -> impl Tunnel {
tunnel_type: "ring".to_owned(),
local_addr: Some(build_url_from_socket_addr(&conn.client.id.into(), "ring").into()),
remote_addr: Some(build_url_from_socket_addr(&conn.server.id.into(), "ring").into()),
resolved_remote_addr: Some(
build_url_from_socket_addr(&conn.server.id.into(), "ring").into(),
),
}),
)
}
@@ -226,6 +229,9 @@ fn get_tunnel_for_server(conn: Arc<Connection>) -> impl Tunnel {
tunnel_type: "ring".to_owned(),
local_addr: Some(build_url_from_socket_addr(&conn.server.id.into(), "ring").into()),
remote_addr: Some(build_url_from_socket_addr(&conn.client.id.into(), "ring").into()),
resolved_remote_addr: Some(
build_url_from_socket_addr(&conn.client.id.into(), "ring").into(),
),
}),
)
}
+34
View File
@@ -41,6 +41,9 @@ impl TcpTunnelListener {
remote_addr: Some(
super::build_url_from_socket_addr(&stream.peer_addr()?.to_string(), "tcp").into(),
),
resolved_remote_addr: Some(
super::build_url_from_socket_addr(&stream.peer_addr()?.to_string(), "tcp").into(),
),
};
let (r, w) = stream.into_split();
@@ -117,6 +120,9 @@ fn get_tunnel_with_tcp_stream(
super::build_url_from_socket_addr(&stream.local_addr()?.to_string(), "tcp").into(),
),
remote_addr: Some(remote_url.into()),
resolved_remote_addr: Some(
super::build_url_from_socket_addr(&stream.peer_addr()?.to_string(), "tcp").into(),
),
};
let (r, w) = stream.into_split();
@@ -277,6 +283,34 @@ mod tests {
_tunnel_pingpong(listener, connector).await;
}
#[tokio::test]
async fn connector_keeps_source_addr_and_reports_resolved_addr() {
let mut listener = TcpTunnelListener::new("tcp://127.0.0.1:0".parse().unwrap());
listener.listen().await.unwrap();
let port = listener.local_url().port().unwrap();
let source_url: url::Url = format!("tcp://localhost:{port}").parse().unwrap();
let mut connector = TcpTunnelConnector::new(source_url.clone());
connector.set_ip_version(IpVersion::V4);
let accept_task = tokio::spawn(async move { listener.accept().await.unwrap() });
let tunnel = connector.connect().await.unwrap();
let accepted_tunnel = accept_task.await.unwrap();
let info = tunnel.info().unwrap();
assert_eq!(info.remote_addr.unwrap().url, source_url.to_string());
let resolved_remote_addr: url::Url = info.resolved_remote_addr.unwrap().into();
assert_eq!(resolved_remote_addr.host_str(), Some("127.0.0.1"));
assert_eq!(resolved_remote_addr.port(), Some(port));
let accepted_info = accepted_tunnel.info().unwrap();
assert_eq!(
accepted_info.remote_addr,
accepted_info.resolved_remote_addr,
);
}
#[tokio::test]
async fn test_alloc_port() {
// v4
+6
View File
@@ -428,6 +428,9 @@ impl UdpTunnelListenerData {
remote_addr: Some(
build_url_from_socket_addr(&remote_addr.to_string(), "udp").into(),
),
resolved_remote_addr: Some(
build_url_from_socket_addr(&remote_addr.to_string(), "udp").into(),
),
}),
));
@@ -772,6 +775,9 @@ impl UdpTunnelConnector {
build_url_from_socket_addr(&socket.local_addr()?.to_string(), "udp").into(),
),
remote_addr: Some(self.addr.clone().into()),
resolved_remote_addr: Some(
build_url_from_socket_addr(&dst_addr.to_string(), "udp").into(),
),
}),
)))
}
+3 -1
View File
@@ -43,7 +43,8 @@ impl UnixSocketTunnelListener {
let info = TunnelInfo {
tunnel_type: "unix".to_owned(),
local_addr: Some(self.local_url().into()),
remote_addr: remote_addr.map(Into::into),
remote_addr: remote_addr.clone().map(Into::into),
resolved_remote_addr: remote_addr.map(Into::into),
};
let (r, w) = stream.into_split();
@@ -122,6 +123,7 @@ impl super::TunnelConnector for UnixSocketTunnelConnector {
tunnel_type: "unix".to_owned(),
local_addr: local_addr.map(Into::into),
remote_addr: Some(self.addr.clone().into()),
resolved_remote_addr: Some(self.addr.clone().into()),
};
let (r, w) = stream.into_split();
+6 -1
View File
@@ -143,11 +143,13 @@ impl WsTunnelListener {
}
let (write, read) = stream.split();
let remote_addr: crate::proto::common::Url = remote_addr.into();
let info = TunnelInfo {
tunnel_type: self.addr.scheme().to_owned(),
local_addr: Some(self.local_url().into()),
remote_addr: Some(remote_addr.into()),
remote_addr: Some(remote_addr.clone()),
resolved_remote_addr: Some(remote_addr),
};
Ok(Box::new(TunnelWrapper::new(
@@ -235,6 +237,9 @@ impl WsTunnelConnector {
.into(),
),
remote_addr: Some(addr.clone().into()),
resolved_remote_addr: Some(
super::build_url_from_socket_addr(&socket_addr.to_string(), addr.scheme()).into(),
),
};
let c = ClientBuilder::from_uri(http::Uri::try_from(addr.to_string()).unwrap());
+6
View File
@@ -538,6 +538,9 @@ impl WgTunnelListener {
remote_addr: Some(
build_url_from_socket_addr(&addr.to_string(), "wg").into(),
),
resolved_remote_addr: Some(
build_url_from_socket_addr(&addr.to_string(), "wg").into(),
),
}),
));
if let Err(e) = conn_sender.send(tunnel) {
@@ -685,6 +688,9 @@ impl WgTunnelConnector {
tunnel_type: "wg".to_owned(),
local_addr: Some(super::build_url_from_socket_addr(&local_addr, "wg").into()),
remote_addr: Some(addr_url.into()),
resolved_remote_addr: Some(
super::build_url_from_socket_addr(&addr.to_string(), "wg").into(),
),
}),
Some(Box::new(wg_peer)),
));