Skip to content

fix(dialog): get_client_dialog_by_call_id returns only UAC dialogs - #176

Merged
shenjinti merged 1 commit into
restsend:mainfrom
tgeorge06:fix/dialog-lookups-respect-role
Oct 7, 2026
Merged

shenjinti merged 1 commit into
restsend:mainfrom
tgeorge06:fix/dialog-lookups-respect-role

Conversation

@tgeorge06

Copy link
Copy Markdown
Contributor

Fixes #175.

Problem

get_client_dialog_by_call_id (src/dialog/dialog_layer.rs:452) is documented to return the client-side (UAC) INVITE dialogs with a Call-ID. Since the unified Dialog::Invite(InviteDialog) (0.6.0) it matches every Dialog::Invite with that Call-ID (line 456), so when both legs of a call share the Call-ID (a B2BUA or proxy that keeps it), the inbound UAS leg is returned as a client dialog.

Spec

  • Doc comment (dialog_layer.rs:444): "Returns all client-side INVITE dialogs (UAC) that share the given Call-ID." In 0.5.x it returned Vec<ClientInviteDialog> and matched only Dialog::ClientInvite.
  • RFC 3261 §12: the dialog id is Call-ID plus local and remote tag, and which tag is local depends on the role. A Call-ID match alone does not tell a UAC dialog from a UAS one.

Fix

The lookup also checks client_dlg.role() == TransactionRole::Client. Nothing else changes.

Contract / coverage

get_client_dialog_by_call_id(call_id) returns every INVITE dialog with role() == TransactionRole::Client and that Call-ID: early UAC dialogs (registered by do_invite / do_invite_async under their early id), confirmed ones, and every one of several UAC dialogs with that Call-ID when more than one is registered (the forking case the doc comment describes). Dialogs restored with restore_from_snapshot keep their role (Dialog::from_inner(inner.role, ..), dialog_layer.rs:497), so a restored UAC dialog is found and a restored UAS dialog is not. UAS dialogs are still found by get_dialog / match_dialog.

Other DialogLayer lookups, checked and not changed:

  • get_or_create_server_subscription, get_or_create_server_publication, get_or_create_client_publication, get_or_create_client_subscription: they match their own typed Dialog variant, so the role is already part of the match.
  • get_dialog, get_dialog_with, match_dialog, remove_dialog: role-agnostic by contract (they return Dialog), as in 0.5.x.
  • get_or_create_server_invite, for a request with a To tag, returns whichever INVITE dialog is stored under the id derived from the request, which can be a UAC dialog. Left as is on purpose: that id is the request's Call-ID with To tag as local tag and From tag as remote tag (RFC 3261 §12.2.2), so a match on a UAC dialog means the request is an in-dialog request of that dialog (for example a re-INVITE from the callee of an outgoing call), and InviteDialog::handle handles it for both roles. Refusing it would answer 481 to a valid re-INVITE for code that sends every INVITE through get_or_create_server_invite (as src/bin/bench_ua.rs:99 does).

Behaviour change: a caller that used get_client_dialog_by_call_id to find the UAS dialog of a call no longer gets it. No caller in the repository, examples or bench_ua uses it.

Tests

src/dialog/tests/test_dialog_layer.rs:

  • test_get_client_dialog_by_call_id_returns_only_uac_dialogs: one DialogLayer with an inbound INVITE answered by get_or_create_server_invite and an outbound do_invite_async with the same Call-ID. get_client_dialog_by_call_id returns exactly the UAC dialog, and the UAS dialog is still found by get_dialog.

On main (5ef8ea6) it fails:

---- dialog::tests::test_dialog_layer::test_get_client_dialog_by_call_id_returns_only_uac_dialogs stdout ----
panicked at src/dialog/tests/test_dialog_layer.rs:413:5:
assertion `left == right` failed
  left: [(Client, DialogId { call_id: "b2bua-call-id", local_tag: "G7v7MbYH", remote_tag: "" }), (Server, DialogId { call_id: "b2bua-call-id", local_tag: "zzv2h54H", remote_tag: "caller-tag" })]
 right: [(Client, DialogId { call_id: "b2bua-call-id", local_tag: "G7v7MbYH", remote_tag: "" })]

Checks

Since the unified `Dialog::Invite(InviteDialog)` (0.6.0) the lookup
matches every INVITE dialog with the Call-ID, so a B2BUA that keeps the
Call-ID across its legs gets the inbound (UAS) leg back as a client
dialog. Check the role, as the documentation says ("client-side INVITE
dialogs (UAC)") and as `Dialog::ClientInvite` did in 0.5.x.
@shenjinti
shenjinti merged commit cdef8c3 into restsend:main Oct 7, 2026
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.

get_client_dialog_by_call_id returns UAS dialogs too since 0.6.0

2 participants