From 314c764f7fa3fe7007af9ddb9aa0d04cb4d6a966 Mon Sep 17 00:00:00 2001 From: David Aldworth Date: Sun, 22 Feb 2026 08:27:19 -0500 Subject: [PATCH] fix: IPv6 TCP connection support Three bugs prevented native IPv6 TCP connections from working: 1. TcpListenerConnection::serve_listener() assigned the remote peer address as the connection's local address (tcp_listener.rs). For IPv4-mapped addresses this was harmless, but for native IPv6 it produced an invalid local address. Fixed by capturing the listener's actual local address before the accept loop. 2. build_via_received() serialized IPv6 addresses without brackets in the Via received= parameter (connection.rs), producing received=2001:db8::1 instead of received=[2001:db8::1]. When rsip re-parsed this, it misinterpreted colons as port separators. 3. serve_connection() silently discarded the Result from serve_loop() (transport_layer.rs), making transport errors invisible. The error is now logged at warn level. Tested with production Ribbon SBC traffic over TCP/IPv6. All three fixes are required for native IPv6 TCP to work end-to-end. Relates to #94 (similar parser failures in on_received_message). --- src/transport/connection.rs | 6 +++++- src/transport/tcp_listener.rs | 9 +++++---- src/transport/transport_layer.rs | 5 ++++- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/transport/connection.rs b/src/transport/connection.rs index 8b51f813..88c0d384 100644 --- a/src/transport/connection.rs +++ b/src/transport/connection.rs @@ -347,9 +347,13 @@ impl SipConnection { typed_via.params.push(Param::Transport(transport)); } + let received_str = match addr { + SocketAddr::V6(_) => format!("[{}]", received.host), + _ => received.host.to_string(), + }; *via = typed_via .with_param(Param::Received(rsip::param::Received::new( - received.host.to_string(), + received_str, ))) .with_param(Param::Other( rsip::param::OtherParam::new("rport"), diff --git a/src/transport/tcp_listener.rs b/src/transport/tcp_listener.rs index 7aa80cf8..91508af1 100644 --- a/src/transport/tcp_listener.rs +++ b/src/transport/tcp_listener.rs @@ -36,6 +36,10 @@ impl TcpListenerConnection { transport_layer_inner: TransportLayerInnerRef, ) -> Result<()> { let listener = TcpListener::bind(self.inner.local_addr.get_socketaddr()?).await?; + let listener_local_addr = SipAddr { + r#type: Some(rsip::transport::Transport::Tcp), + addr: listener.local_addr().unwrap().into(), + }; tokio::spawn(async move { loop { let (stream, remote_addr) = match listener.accept().await { @@ -49,10 +53,7 @@ impl TcpListenerConnection { debug!(remote = %remote_addr, "tcp connection rejected by whitelist"); continue; } - let local_addr = SipAddr { - r#type: Some(rsip::transport::Transport::Tcp), - addr: remote_addr.into(), - }; + let local_addr = listener_local_addr.clone(); let tcp_connection = match TcpConnection::from_stream( stream, local_addr.clone(), diff --git a/src/transport/transport_layer.rs b/src/transport/transport_layer.rs index 4fcbb911..d1a1717f 100644 --- a/src/transport/transport_layer.rs +++ b/src/transport/transport_layer.rs @@ -431,9 +431,12 @@ impl TransportLayerInner { } select! { _ = sub_token.cancelled() => { } - _ = async { + result = async { transport.serve_loop(sender_clone.clone()).await } => { + if let Err(e) = result { + warn!(addr=%transport.get_addr(), error = %e, "serve_loop error"); + } } } info!(addr=%transport.get_addr(), "transport serve_loop exited");