Repository navigation
Conversation
…t on When a 2xx arrives for an INVITE client transaction that was already cancelled, outgoing_recv() calls nta_msg_ackbye(), which routes the ACK and BYE by the Contact of the 2xx. A SIP WebSocket client (RFC 7118) puts a random ".invalid" host there, so the ACK fails with "transport ws is not supported". The client keeps retransmitting the 2xx for 64*T1 and rejects new INVITEs with 486 meanwhile. Send the ACK and BYE over the transport of the original INVITE. It is used only when it is an established connection (TCP, TLS, WS, WSS), so routing over UDP is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
FreeSWITCH (mod_sofia) calls a SIP WebSocket client (JsSIP in a browser, auto-answer) and cancels the call a few tens of milliseconds later. When the client's 200 OK crosses the CANCEL, nta treats it as an orphan 2xx and tries to ACK and BYE it, but the ACK cannot be sent (sofia-sip 1.13.18):
The client gets neither the ACK nor the BYE. It keeps retransmitting the 200 OK (RFC 3261 section 13.3.1.4: "If the server retransmits the 2xx response for 64*T1 seconds without receiving an ACK, the dialog is confirmed, but the session SHOULD be terminated.") and, still being in a call, rejects every new INVITE with 486 Busy Here for those 32 seconds.
Root cause
nta_msg_ackbye()sends the ACK and BYE as stateless requests without a transport, so they are routed by the request URI taken from the Contact of the 2xx. A SIP WebSocket client cannot learn its own transport address and puts a random.invalidhost in Via and Contact. RFC 7118 Appendix B.1: "The Contact URI provided by SIP UAs requesting (and receiving) Outbound support is not used for routing requests to those UAs, thus it is safe to set a random domain in the Contact URI hostport." Such a client is reachable only over the WebSocket connection it opened.Fix
In both orphan-2xx paths of
outgoing_recv(), give the ACK and BYE the transport of the original INVITE client transaction (NTATAG_TPORT(orq->orq_tport)).outgoing_create()sends over that transport only when it is a secondary transport, i.e. an established connection (TCP, TLS, WS, WSS). For a primary transport such as UDP it only takes the protocol name from it, so routing does not change there.outgoing_free(), soorq_tportis either valid or NULL here. When it is NULL, nothing changes.nta_msg_ackbye()is public API, so it keeps its signature and calls a static helper that takes the transport.Testing
FreeSWITCH 1.11.3 with sofia-sip 1.13.18 (the changed code is identical on master), JsSIP in Chromium over WSS, with a 120 ms one-way delay added between the browser and FreeSWITCH (the round trip where we first saw the problem was about 240 ms). 54 originates, each cancelled 20-80 ms after it was sent, then a probe originate about every 0.5 s:
With the patch, the client received the ACK and BYE in all 15 collisions. The one 486 was an INVITE that arrived while the ACK and BYE were still on the way.
We have been running this patch in production since 2026-10-11.
🤖 Generated with Claude Code