Commit 1daca570 authored by Pekka Pessi's avatar Pekka Pessi

nta/nua: Fixed #1472682 sf.net bug: ACK does not find INVITE

The transaction matching logic failed on server side when the To URI was
changed between INVITE and ACK. Now it is possible to change the To/From URI
in nua_ack(), too.

Binary API was changed, too, and accessor function nta_outgoing_branch() was
added.

darcs-hash:20070502151640-55b16-73889a36bd0cb0d74413bb258e01c46193748b8f.gz
parent 6677f878
......@@ -24,6 +24,7 @@ API/ABI changes and versioning
New features in API are marked with Doxytag macro @VERSION_1_12_7.
libsofia-sip-ua:
- Added accessor function nta_outgoing_branch()
- **template**: Added foobar() function (sofia-sip/foobar.h).
- This release is ABI/API compatible with applications linked against
any 1.12.x release. However, applications built against this release won't
......@@ -71,3 +72,5 @@ Bugs fixed in this release
/>
- **template**: #9499652 sf.net bug item title
- Fixed #1472682 sf.net bug: ACK does not find INVITE when
To URI has been changed.
......@@ -16,9 +16,9 @@ AC_CONFIG_SRCDIR([libsofia-sip-ua/sip/sofia-sip/sip.h])
AC_SUBST(VER_LIBSOFIA_SIP_UA_MAJOR_MINOR, [1.12])
dnl Includedir specific to this sofia version
AC_SUBST(include_sofiadir, '${includedir}/sofia-sip-1.12')
AC_SUBST(LIBVER_SOFIA_SIP_UA_CUR, [5])
AC_SUBST(LIBVER_SOFIA_SIP_UA_CUR, [6])
AC_SUBST(LIBVER_SOFIA_SIP_UA_REV, [0])
AC_SUBST(LIBVER_SOFIA_SIP_UA_AGE, [5])
AC_SUBST(LIBVER_SOFIA_SIP_UA_AGE, [6])
AC_SUBST(LIBVER_SOFIA_SIP_UA_SOVER, [0]) # CUR-AGE
AC_SUBST(LIBVER_SOFIA_SIP_UA_GLIB_CUR, [3])
AC_SUBST(LIBVER_SOFIA_SIP_UA_GLIB_REV, [0])
......
......@@ -4436,6 +4436,7 @@ nta_incoming_t *incoming_create(nta_agent_t *agent,
/* Tag transaction */
if (tag)
sip_to_tag(home, irq->irq_to, tag);
irq->irq_tag = irq->irq_to->a_tag;
if (method != sip_method_ack) {
int *use_rport = NULL;
......@@ -5006,9 +5007,11 @@ nta_incoming_t *nta_incoming_find(nta_agent_t const *agent,
}
su_inline
int addr_match(sip_addr_t const *a, sip_addr_t const *b)
int addr_match(sip_addr_t const *a, char const *a_tag, sip_addr_t const *b)
{
if (a->a_tag && b->a_tag)
if (a_tag && b->a_tag)
return strcasecmp(a_tag, b->a_tag) == 0;
else if (a->a_tag && b->a_tag)
return strcasecmp(a->a_tag, b->a_tag) == 0;
else
return
......@@ -5059,7 +5062,7 @@ nta_incoming_t *incoming_find(nta_agent_t const *agent,
if (is_uas_ack &&
irq->irq_method == sip_method_invite &&
200 <= irq->irq_status && irq->irq_status < 300 &&
addr_match(irq->irq_to, to))
addr_match(irq->irq_to, irq->irq_tag, to))
*return_ack = irq;
/* RFC3261 - section 8.2.2.2 Merged Requests */
else if (return_merge && agent->sa_merge_482 &&
......@@ -5075,7 +5078,7 @@ nta_incoming_t *incoming_find(nta_agent_t const *agent,
}
if (is_uas_ack) {
if (!addr_match(irq->irq_to, to))
if (!addr_match(irq->irq_to, irq->irq_tag, to))
continue;
}
else if (irq->irq_tag_set || !irq->irq_tag) {
......@@ -5129,8 +5132,8 @@ nta_incoming_t *incoming_find(nta_agent_t const *agent,
continue;
if (irq->irq_cseq->cs_seq != rack->ra_cseq)
continue;
if (!addr_match(irq->irq_to, to) ||
!addr_match(irq->irq_from, from))
if (!addr_match(irq->irq_to, NULL, to) ||
!addr_match(irq->irq_from, NULL, from))
continue;
if (!irq->irq_from->a_tag != !from->a_tag)
continue;
......@@ -6620,6 +6623,14 @@ unsigned nta_outgoing_delay(nta_outgoing_t const *orq)
return orq != NULL && orq != NONE ? orq->orq_delay : UINT_MAX;
}
/** Get the branch parameter. */
char const *nta_outgoing_branch(nta_outgoing_t const *orq)
{
return orq != NULL && orq != NONE && orq->orq_branch
? orq->orq_branch + strlen("branch=")
: NULL;
}
/**Get reference to response message.
*
* Retrieve the latest incoming response message to the outgoing
......@@ -6847,7 +6858,7 @@ nta_outgoing_t *outgoing_create(nta_agent_t *agent,
}
if (branch && branch != NONE) {
if (strchr(branch, '='))
if (strncasecmp(branch, "branch=", 7) == 0)
branch = su_strdup(home, branch);
else
branch = su_sprintf(home, "branch=%s", branch);
......@@ -6864,7 +6875,10 @@ nta_outgoing_t *outgoing_create(nta_agent_t *agent,
if (orq->orq_method == sip_method_ack) {
if (ack_branch != NULL && ack_branch != NONE) {
orq->orq_branch = su_strdup(home, ack_branch);
if (strncasecmp(ack_branch, "branch=", 7) == 0)
orq->orq_branch = su_strdup(home, ack_branch);
else
orq->orq_branch = su_sprintf(home, "branch=%s", ack_branch);
}
else if (!stateless && agent->sa_is_a_uas) {
/*
......@@ -7983,9 +7997,7 @@ nta_outgoing_t *outgoing_find(nta_agent_t const *sa,
continue;
if (str0casecmp(orq->orq_from->a_tag, sip->sip_from->a_tag))
continue;
if (orq->orq_to->a_tag && sip->sip_to->a_tag
? strcasecmp(orq->orq_to->a_tag, sip->sip_to->a_tag)
: !addr_match(orq->orq_to, sip->sip_to))
if (!addr_match(orq->orq_to, NULL, sip->sip_to))
continue;
if (orq->orq_method == method ?
/* Don't match if request To has a tag and response has no To tag */
......
......@@ -379,6 +379,7 @@ SOFIAPUBFUN int nta_outgoing_status(nta_outgoing_t const *orq);
SOFIAPUBFUN sip_method_t nta_outgoing_method(nta_outgoing_t const *orq);
SOFIAPUBFUN char const *nta_outgoing_method_name(nta_outgoing_t const *orq);
SOFIAPUBFUN uint32_t nta_outgoing_cseq(nta_outgoing_t const *orq);
SOFIAPUBFUN char const *nta_outgoing_branch(nta_outgoing_t const *orq);
SOFIAPUBFUN unsigned nta_outgoing_delay(nta_outgoing_t const *orq);
......
......@@ -1072,15 +1072,17 @@ int nua_invite_client_ack(nua_client_request_t *cr, tagi_t const *tags)
nta_outgoing_t *ack;
int status = 200;
char const *phrase = "OK", *reason = NULL;
char const *invite_branch;
assert(ds->ds_leg);
assert(cr->cr_orq);
msg = nta_outgoing_getrequest(cr->cr_orq);
sip = sip_object(msg);
sip = sip_object(msg);
if (!msg)
return -1;
invite_branch = nta_outgoing_branch(cr->cr_orq);
wa = sip_authorization(sip);
pa = sip_proxy_authorization(sip);
......@@ -1157,9 +1159,11 @@ int nua_invite_client_ack(nua_client_request_t *cr, tagi_t const *tags)
if ((ack = nta_outgoing_mcreate(nh->nh_nua->nua_nta, NULL, NULL, NULL,
msg,
NTATAG_ACK_BRANCH(invite_branch),
SIPTAG_END(),
TAG_NEXT(tags)))) {
nta_outgoing_destroy(ack); /* TR engine keeps this around for T2 */
/* TR engine keeps this around for T2 so it catches all 2XX retransmissions */
nta_outgoing_destroy(ack);
if (nh->nh_soa && reason && ss && ss->ss_state <= nua_callstate_ready)
nua_stack_event(nh->nh_nua, nh, NULL,
......
......@@ -963,8 +963,156 @@ int test_basic_call_4(struct context *ctx)
END();
}
int change_uri_in_ack(CONDITION_PARAMS)
{
if (!(check_handle(ep, call, nh, SIP_500_INTERNAL_SERVER_ERROR)))
return 0;
save_event_in_list(ctx, event, ep, call);
switch (callstate(tags)) {
case nua_callstate_completing:
ACK(ep, call, nh,
SIPTAG_FROM_STR("sip:anonymous@org.invalid"),
SIPTAG_TO_STR("sip:anonymous@net.invalid"),
TAG_END());
return 0;
case nua_callstate_ready:
return 1;
case nua_callstate_terminated:
if (call)
nua_handle_destroy(call->nh), call->nh = NULL;
return 1;
default:
return 0;
}
}
/* Test changing from/to within dialog */
int test_basic_call_5(struct context *ctx)
{
BEGIN();
struct endpoint *a = &ctx->a, *b = &ctx->b;
struct call *a_call = a->call, *b_call = b->call;
struct event *e;
sip_t const *sip;
if (print_headings)
printf("TEST NUA-3.5: test changing From/To URL in ACK\n");
a_call->sdp = "m=audio 5008 RTP/AVP 8";
b_call->sdp = "m=audio 5010 RTP/AVP 0 8";
TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END()));
TEST_1(!nua_handle_has_active_call(a_call->nh));
TEST_1(!nua_handle_has_call_on_hold(a_call->nh));
INVITE(a, a_call, a_call->nh,
TAG_IF(!ctx->proxy_tests, NUTAG_URL(b->contact->m_url)),
SOATAG_USER_SDP_STR(a_call->sdp),
NUTAG_AUTOACK(0),
TAG_END());
run_ab_until(ctx, -1, change_uri_in_ack, -1, accept_call);
/* Client transitions:
INIT -(C1)-> CALLING: nua_invite(), nua_i_state
CALLING -(C2)-> PROCEEDING: nua_r_invite, nua_i_state
PROCEEDING -(C3+C4)-> READY: nua_r_invite, nua_i_state
*/
TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_state);
TEST(callstate(e->data->e_tags), nua_callstate_calling); /* CALLING */
TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
TEST(e->data->e_status, 180);
TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
TEST(callstate(e->data->e_tags), nua_callstate_proceeding); /* PROCEEDING */
TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
TEST(e->data->e_status, 200);
TEST_1(sip = sip_object(e->data->e_msg));
TEST_1(sip->sip_contact);
TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
TEST(callstate(e->data->e_tags), nua_callstate_completing); /* COMPLETING */
TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
TEST(callstate(e->data->e_tags), nua_callstate_ready); /* READY */
TEST_1(!e->next);
free_events_in_list(ctx, a->events);
TEST_1(nua_handle_has_active_call(a_call->nh));
TEST_1(!nua_handle_has_call_on_hold(a_call->nh));
/*
Server transitions:
INIT -(S1)-> RECEIVED: nua_i_invite, nua_i_state
RECEIVED -(S2a)-> EARLY: nua_respond(), nua_i_state
EARLY -(S3b)-> COMPLETED: nua_respond(), nua_i_state
COMPLETED -(S4)-> READY: nua_i_ack, nua_i_state
*/
TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_invite);
TEST(e->data->e_status, 100);
TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
TEST(callstate(e->data->e_tags), nua_callstate_received); /* RECEIVED */
TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
TEST(callstate(e->data->e_tags), nua_callstate_early); /* EARLY */
TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
TEST(callstate(e->data->e_tags), nua_callstate_completed); /* COMPLETED */
TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_ack);
TEST_1(sip = sip_object(e->data->e_msg));
TEST_S(sip->sip_to->a_url->url_user, "anonymous");
TEST_S(sip->sip_from->a_url->url_user, "anonymous");
TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
TEST(callstate(e->data->e_tags), nua_callstate_ready); /* READY */
TEST_1(!e->next);
free_events_in_list(ctx, b->events);
TEST_1(nua_handle_has_active_call(b_call->nh));
TEST_1(!nua_handle_has_call_on_hold(b_call->nh));
BYE(b, b_call, b_call->nh, TAG_END());
run_ab_until(ctx, -1, until_terminated, -1, until_terminated);
/* B transitions:
READY --(T2)--> TERMINATING: nua_bye()
TERMINATING --(T3)--> TERMINATED: nua_r_bye, nua_i_state
*/
TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_r_bye);
TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
TEST_1(!e->next);
free_events_in_list(ctx, b->events);
TEST_1(!nua_handle_has_active_call(b_call->nh));
/* A transitions:
nua_i_info
READY -(T1)-> TERMINATED: nua_i_bye, nua_i_state
*/
TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_bye);
TEST(e->data->e_status, 200);
TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
TEST_1(!e->next);
free_events_in_list(ctx, a->events);
nua_handle_destroy(a_call->nh), a_call->nh = NULL;
nua_handle_destroy(b_call->nh), b_call->nh = NULL;
if (print_headings)
printf("TEST NUA-3.4: PASSED\n");
END();
}
int test_basic_call(struct context *ctx)
{
return test_basic_call_1(ctx) || test_basic_call_2(ctx) ||
test_basic_call_3(ctx) || test_basic_call_4(ctx);
return
test_basic_call_1(ctx)
|| test_basic_call_2(ctx)
|| test_basic_call_3(ctx)
|| test_basic_call_4(ctx)
|| test_basic_call_5(ctx)
;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment