Skip to content

fix: IPv6 TCP connection support#96

Merged
shenjinti merged 1 commit intorestsend:mainfrom
daldworth:fix/ipv6-tcp-support
Feb 23, 2026
Merged

fix: IPv6 TCP connection support#96
shenjinti merged 1 commit intorestsend:mainfrom
daldworth:fix/ipv6-tcp-support

Conversation

@daldworth
Copy link
Contributor

Summary

Three bugs prevent native IPv6 TCP connections from working. IPv4-mapped addresses (::ffff:x.x.x.x) work by accident, but native IPv6 addresses fail silently — the serve_loop exits after parsing the first message with no error logged.

Discovered while building a SIP redirect server that receives INVITE traffic from a Ribbon SBC over TCP/IPv6.

Bug 1: TcpListenerConnection::serve_listener() — wrong local address

tcp_listener.rs line 54 assigns the remote peer address as the connection's local address:

let local_addr = SipAddr {
    r#type: Some(rsip::transport::Transport::Tcp),
    addr: remote_addr.into(),  // BUG: should be listener's local addr
};

For IPv4-mapped addresses this is harmless, but for native IPv6 it produces an invalid local address.

Fix: Capture the listener's actual local address (listener.local_addr()) before the accept loop and use it for all accepted connections.

Bug 2: build_via_received() — bare IPv6 in Via received=

connection.rs serializes IPv6 addresses without brackets:

Via: SIP/2.0/TCP 10.0.16.21:5060;received=2600:1900:4040:181:0:13::;rport=48682

When rsip re-parses this modified Via header, it misinterprets the colons in the IPv6 address as port separators, failing with invalid digit found in string.

Fix: Wrap IPv6 addresses in brackets per RFC 3261 §18.2.1: received=[2600:1900:4040:181:0:13::]

Bug 3: serve_connection() — silent error discard

transport_layer.rs discards the Result from serve_loop():

select! {
    _ = sub_token.cancelled() => { }
    _ = async {
        transport.serve_loop(sender_clone.clone()).await  // Result IGNORED
    } => { }
}

This makes transport errors completely invisible — the only log is "transport serve_loop exited" with no error detail.

Fix: Capture and log the error at warn level.

Testing

Tested with production Ribbon SBC traffic over TCP/IPv6:

  • 9-call integration test over IPv4: 9/9 PASS
  • 6,000-call load test at 100 TPS over IPv4: PASS (>99.9%)
  • Network connectivity test over IPv6: PASS (TCP connect + SIP INVITE/300)

Note

There is also a related bug in rsip 0.4.0 — the host_with_port parser does not handle [IPv6]:port syntax in Via headers (RFC 3261 §25.1 IPv6reference). We have a fix for that but it belongs in the rsip crate, not rsipstack. Mentioning it here for awareness since it affects any SIP endpoint that receives Via headers with bracketed IPv6 addresses.

Relates to #94 (similar parser failures in on_received_message).

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 restsend#94 (similar parser failures in on_received_message).
@shenjinti
Copy link
Contributor

Thanks.

@shenjinti shenjinti merged commit ae4f3a6 into restsend:main Feb 23, 2026
2 of 3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants