Skip to content

[mod_sofia] Improve outbound REFER result handling (NOTIFY + direct responses) - #3059

Open
mirkobrankovic82 wants to merge 1 commit into
signalwire:masterfrom
mirkobrankovic82:fix/refer-notify-terminated-state
Open

mirkobrankovic82 wants to merge 1 commit into
signalwire:masterfrom
mirkobrankovic82:fix/refer-notify-terminated-state

Conversation

@mirkobrankovic82

@mirkobrankovic82 mirkobrankovic82 commented Jun 24, 2026 •

Copy link
Copy Markdown

Summary

  • Handle inbound NOTIFY for the refer event package when Subscription-State is terminated but no message/sipfrag body is present
  • Fire sofia::notify_refer custom events for terminated refer NOTIFYs without payload, including subscription state headers
  • Handle direct SIP responses to outbound REFER (e.g. 400 Bad Request) by synthesizing sip_refer_reply and unblocking sofia_wait_for_reply() — peers that reject REFER without sending NOTIFY sipfrag no longer cause a timeout
  • uuid_deflect returns +OK:SIP/2.0 <status> for final direct REFER responses; 202 Accepted still waits for NOTIFY per RFC 3515
  • Make the outbound REFER NOTIFY wait configurable via Sofia profile param refer-notify-timeout (seconds, default 10) and per-call channel variable sip_refer_notify_timeout
  • Keep the original call after a failed referred INVITE: a terminated refer NOTIFY with 4xx/5xx sipfrag (e.g. 486) no longer destroys the call nua handle or sends BYE. Only a 200 sipfrag hangs up the original dialog (unless sip_refer_continue_after_reply is set)

Problem

After sending a REFER, mod_sofia waits for a NOTIFY response via sofia_wait_for_reply(). The wait was previously hardcoded to 10 seconds.

Three cases previously left the transfer outcome wrong or tore down the original call:

  1. Terminated NOTIFY without body — some SIP endpoints terminate the refer subscription with Subscription-State: terminated and no sipfrag payload. FreeSWITCH ignored these for channel variable purposes and never cleared want_event.

  2. Direct REFER failure — some peers answer REFER with a final SIP status (e.g. 400 Bad Request) instead of 202 Accepted + NOTIFY. FreeSWITCH set sip_refer_status_code but did not populate sip_refer_reply or unblock the wait.

  3. Failed sipfrag + terminated subscription — peers such as Twilio accept REFER with 202, then NOTIFY sipfrag 486 (or other >= 300) with Subscription-State: terminated. FreeSWITCH recorded sip_refer_reply but then nua_handle_destroy() sent BYE on the original dialog. The existing channel variable sip_refer_continue_after_reply already skipped the uuid_deflect hangup and the 200 sipfrag hangup; it did not skip destroy_nh on the terminated refer subscription.

The 10s wait is also too short for some transfers that still send a final NOTIFY after 202 + 100 Trying.

Config

Sofia profile (default remains 10 seconds):

<param name="refer-notify-timeout" value="60"/>

Per-call override:

sip_refer_notify_timeout=60

Existing channel variable (unchanged meaning):

sip_refer_continue_after_reply=true

That variable still skips hangup on a 200 sipfrag and skips the uuid_deflect wait/hangup. Keeping the original call after a failed sipfrag (>= 300 or terminated with no body) does not require it.

@mirkobrankovic82 mirkobrankovic82 changed the title [mod_sofia] Handle REFER NOTIFY with terminated subscription and no body [mod_sofia] Improve outbound REFER result handling (NOTIFY + direct responses) Jul 7, 2026
@mirkobrankovic82

Copy link
Copy Markdown
Author

@andywolk @morbit85 could you take a look when you have a moment?

This unblocks uuid_deflect when the peer never sends a final sipfrag (terminated refer NOTIFY with no body, or a direct non-202 REFER response such as 400), and it makes the hardcoded 10s NOTIFY wait configurable via Sofia profile param refer-notify-timeout (default remains 10) with an optional per-call override sip_refer_notify_timeout.

Happy to adjust anything you want changed.

Handle terminated refer NOTIFY without a sipfrag body, surface a direct
REFER response in uuid_deflect, make the NOTIFY wait configurable, and
keep the original call when a terminated sipfrag is 4xx/5xx.

sip_refer_continue_after_reply only skips hangup of the original leg.
uuid_deflect still waits for the sipfrag so the API returns the INVITE result.
@mirkobrankovic82
mirkobrankovic82 force-pushed the fix/refer-notify-terminated-state branch from aa21606 to 8d1ee5a Compare September 29, 2026 11:36
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.

1 participant