Skip to content

Commit b634c28

Browse files
alexdowadjvarruda
andcommitted
Support forking SIP calls
When a SIP call is forked, the client will send an INVITE with the same Call ID and same From tag as an earlier INVITE, but with a different Branch ID in the topmost Via header. There were two issues with sofia-sip which prevented call forking from working; first, its logic for merging SIP requests (as per RFC3261 section 8.2.2.2) did not respect the Branch ID. It would merge requests with a different Branch ID, which is contrary to RFC3261 section 17.2.3. Second, sofia-sip has some logic for checking whether incoming requests are part of an established SIP dialog or not. The matching criteria were such that incoming INVITE requests forking a call would be treated as part of the already established dialog, and then an error would be returned to the client, essentially telling the client that this new INVITE is invalid because the call is already established. Therefore, add some extra matching conditions which ensure that an INVITE forking a call will not be treated as part of the previously established call leg. The matching conditions are as specific as possible, to minimize the chances of unintentionally affecting how other types of SIP messages are handled. Implementing these new matching conditions can only be done by recording the Branch ID for established calls, so we can check whether another INVITE which comes later has the same Branch ID or a different one. This requires adding a new member to nta_leg_s. Co-Authored-By: João Arruda <[email protected]>
1 parent 563fa31 commit b634c28

File tree

2 files changed

+39
-11
lines changed

2 files changed

+39
-11
lines changed

libsofia-sip-ua/nta/nta.c

Lines changed: 38 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -399,6 +399,7 @@ struct nta_leg_s
399399

400400
url_t const *leg_url; /**< Match incoming requests. */
401401
char const *leg_method; /**< Match incoming requests. */
402+
char const *leg_branch_id; /**< Match incoming INVITE to identify forked calls */
402403

403404
uint32_t leg_seq; /**< Sequence number for next transaction */
404405
uint32_t leg_rseq; /**< Remote sequence number */
@@ -685,7 +686,8 @@ static nta_leg_t *leg_find(nta_agent_t const *sa,
685686
url_t const *request_uri,
686687
sip_call_id_t const *i,
687688
char const *from_tag,
688-
char const *to_tag);
689+
char const *to_tag,
690+
char const *branch_id);
689691
static nta_leg_t *dst_find(nta_agent_t const *sa, url_t const *u0,
690692
char const *method);
691693
static void leg_recv(nta_leg_t *, msg_t *, sip_t *, tport_t *);
@@ -3134,7 +3136,8 @@ void agent_recv_request(nta_agent_t *agent,
31343136
method_name, url,
31353137
sip->sip_call_id,
31363138
sip->sip_from->a_tag,
3137-
sip->sip_to->a_tag))) {
3139+
sip->sip_to->a_tag,
3140+
sip->sip_via ? sip->sip_via->v_branch : NULL))) {
31383141
/* Try existing dialog */
31393142
SU_DEBUG_5(("nta: %s (%u) %s\n",
31403143
method_name, cseq, "going to existing leg"));
@@ -4316,6 +4319,7 @@ nta_leg_t *nta_leg_tcreate(nta_agent_t *agent,
43164319
su_home_t *home;
43174320
url_t *url;
43184321
char const *what = NULL;
4322+
const sip_via_t *via = NULL;
43194323

43204324
if (agent == NULL)
43214325
return su_seterrno(EINVAL), NULL;
@@ -4333,6 +4337,7 @@ nta_leg_t *nta_leg_tcreate(nta_agent_t *agent,
43334337
SIPTAG_TO_REF(to),
43344338
SIPTAG_TO_STR_REF(to_str),
43354339
SIPTAG_ROUTE_REF(route),
4340+
SIPTAG_VIA_REF(via),
43364341
NTATAG_TARGET_REF(contact),
43374342
NTATAG_REMOTE_CSEQ_REF(rseq),
43384343
SIPTAG_CSEQ_REF(cs),
@@ -4353,6 +4358,13 @@ nta_leg_t *nta_leg_tcreate(nta_agent_t *agent,
43534358
return NULL;
43544359
home = leg->leg_home;
43554360

4361+
if (via) {
4362+
SU_DEBUG_9(("nta_leg_tcreate(): setting branch ID to: %s\n", via->v_branch));
4363+
leg->leg_branch_id = su_strdup(home, via->v_branch);
4364+
} else {
4365+
leg->leg_branch_id = NULL;
4366+
}
4367+
43564368
leg->leg_agent = agent;
43574369
nta_leg_bind(leg, callback, magic);
43584370

@@ -4821,12 +4833,12 @@ nta_leg_by_replaces(nta_agent_t *sa, sip_replaces_t const *rp)
48214833

48224834
id->i_hash = msg_hash_string(id->i_id = rp->rp_call_id);
48234835

4824-
leg = leg_find(sa, NULL, NULL, id, from_tag, to_tag);
4836+
leg = leg_find(sa, NULL, NULL, id, from_tag, to_tag, NULL);
48254837

48264838
if (leg == NULL && strcmp(from_tag, "0") == 0)
4827-
leg = leg_find(sa, NULL, NULL, id, NULL, to_tag);
4839+
leg = leg_find(sa, NULL, NULL, id, NULL, to_tag, NULL);
48284840
if (leg == NULL && strcmp(to_tag, "0") == 0)
4829-
leg = leg_find(sa, NULL, NULL, id, from_tag, NULL);
4841+
leg = leg_find(sa, NULL, NULL, id, from_tag, NULL, NULL);
48304842
}
48314843

48324844
return leg;
@@ -5054,7 +5066,8 @@ nta_leg_t *nta_leg_by_dialog(nta_agent_t const *agent,
50545066
NULL, url,
50555067
call_id,
50565068
remote_tag,
5057-
local_tag);
5069+
local_tag,
5070+
NULL);
50585071

50595072
if (to_be_freed) su_free(NULL, to_be_freed);
50605073

@@ -5073,7 +5086,8 @@ nta_leg_t *leg_find(nta_agent_t const *sa,
50735086
url_t const *request_uri,
50745087
sip_call_id_t const *i,
50755088
char const *from_tag,
5076-
char const *to_tag)
5089+
char const *to_tag,
5090+
char const *branch_id)
50775091
{
50785092
hash_value_t hash = i->i_hash;
50795093
leg_htable_t const *lht = sa->sa_dialogs;
@@ -5123,6 +5137,13 @@ nta_leg_t *leg_find(nta_agent_t const *sa,
51235137
if (leg_method && method_name && !su_casematch(method_name, leg_method))
51245138
continue;
51255139

5140+
/* Do not match if incoming INVITE To header has no tag AND the topmost Via
5141+
* Header branch ID in the incoming is different from the existing leg
5142+
* (this means it's a call being forked)
5143+
*/
5144+
if (!to_tag && su_casematch(method_name, "INVITE") && branch_id && leg->leg_branch_id && !su_casematch(branch_id, leg->leg_branch_id))
5145+
continue;
5146+
51265147
/* Perfect match if both local and To have tag
51275148
* or local does not have tag.
51285149
*/
@@ -6238,10 +6259,16 @@ static nta_incoming_t *incoming_find(nta_agent_t const *agent,
62386259

62396260
/* RFC3261 - section 8.2.2.2 Merged Requests */
62406261
if (return_merge) {
6241-
if (irq->irq_cseq->cs_method == cseq->cs_method &&
6242-
strcmp(irq->irq_cseq->cs_method_name,
6243-
cseq->cs_method_name) == 0)
6244-
*return_merge = irq, return_merge = NULL;
6262+
/* RFC3261 - section 17.2.3 Matching Requests to Server Transactions */
6263+
if (irq->irq_via->v_branch &&
6264+
su_casematch(irq->irq_via->v_branch, v->v_branch) &&
6265+
su_casematch(irq->irq_via->v_host, v->v_host) &&
6266+
su_strmatch(irq->irq_via->v_port, v->v_port)) {
6267+
if (irq->irq_cseq->cs_method == cseq->cs_method &&
6268+
strcmp(irq->irq_cseq->cs_method_name,
6269+
cseq->cs_method_name) == 0)
6270+
*return_merge = irq, return_merge = NULL;
6271+
}
62456272
}
62466273
}
62476274

libsofia-sip-ua/nua/nua_stack.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1084,6 +1084,7 @@ nua_handle_t *nua_stack_incoming_handle(nua_t *nua,
10841084
SIPTAG_CALL_ID(sip->sip_call_id),
10851085
SIPTAG_FROM(sip->sip_to),
10861086
SIPTAG_TO(sip->sip_from),
1087+
SIPTAG_VIA(sip->sip_via),
10871088
NTATAG_REMOTE_CSEQ(sip->sip_cseq->cs_seq),
10881089
TAG_END());
10891090

0 commit comments

Comments
 (0)