Merge changes from sofia-sip tree:

Mon Feb 12 21:22:39 EET 2007  Maxim Zaikin <Maxim at amsd.com>
  * su_pthread_port.c: destroying condition variable used when destroying the thread.

Tue Feb 13 01:10:38 EET 2007  kai.vehmanen@nokia.com
  * nua: Ignore CANCEL of incoming request if we have already sent a final response, part 2.

Tue Feb 13 01:02:00 EET 2007  kai.vehmanen@nokia.com
  * nua: Ignore CANCEL of incoming request if we have already sent a final response. Problem reported by Mike Jerris.

nua: fixed problems in state machines.
Pekka Pessi <first.lastname@nokia.com>**20070214201847
   
 These problems are mostly introduced in 1.12.5:
 - assert failed if INVITE was received when calling
   (reported by Michael Jerris)
 - assert failed if nua_invite() was called second time before receiving
   final response to first INVITE (ditto)
 - ACK is now always sent if session was terminated after receiving 2XX
 - if nua has a pending incoming INVITE request, return 491 to
   nua_invite()

nta.c: return 481 response to CANCEL if INVITE transaction was successful
According to the RFC 3261 state diagram the INVITE transaction is destroyed
 immediately when a final 2XX series response is sent. Now nta returns a 481
 response to CANCEL if it finds an INVITE server transaction that was
 responded with a 2XX success response.




git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@4284 d0543943-73ff-0310-b7d9-9358b9ac24b2
This commit is contained in:
Michael Jerris committed 2007-02-15 05:18:37 +00:00
1 parent 6e12fb32cd
commit 85c8bcd5ed
17 files changed
+1053 -289

No files matched your search

+21 -7
View File
@@ -5056,10 +5056,16 @@ nta_incoming_t *incoming_find(nta_agent_t const *agent,
if (irq->irq_method == rq->rq_method)
break; /* found */
if (return_ack && rq->rq_method == sip_method_cancel)
*return_ack = irq;
else if (return_ack && rq->rq_method == sip_method_ack &&
irq->irq_method == sip_method_invite)
if (!return_ack)
continue;
if (irq->irq_method == sip_method_invite) {
if (rq->rq_method == sip_method_cancel)
*return_ack = irq;
else if (rq->rq_method == sip_method_ack)
*return_ack = irq;
}
else if (rq->rq_method == sip_method_cancel && !irq->irq_terminated)
*return_ack = irq;
}
@@ -5179,10 +5185,18 @@ int incoming_cancel(nta_incoming_t *irq, msg_t *msg, sip_t *sip,
nta_agent_t *agent = irq->irq_agent;
/* Respond to the CANCEL */
nta_msg_treply(agent, msg_ref_create(msg), SIP_200_OK,
NTATAG_TPORT(tport),
TAG_END());
if (200 <= irq->irq_status && irq->irq_status < 300) {
nta_msg_treply(agent, msg_ref_create(msg), SIP_481_NO_TRANSACTION,
NTATAG_TPORT(tport),
TAG_END());
}
else
nta_msg_treply(agent, msg_ref_create(msg), SIP_200_OK,
NTATAG_TPORT(tport),
TAG_END());
/* We have already sent final response */
if (irq->irq_completed || irq->irq_method != sip_method_invite) {
msg_destroy(msg);
return 0;
+17 -12
View File
@@ -345,6 +345,15 @@ void nua_dialog_usage_remove_at(nua_owner_t *own,
nua_client_request_t *cr, *cr_next;
nua_server_request_t *sr, *sr_next;
*at = du->du_next;
o = du->du_event;
SU_DEBUG_5(("nua(%p): removing %s usage%s%s\n",
(void *)own, nua_dialog_usage_name(du),
o ? " with event " : "", o ? o->o_type :""));
du->du_class->usage_remove(own, ds, du);
/* Destroy saved client request */
if (nua_client_is_bound(du->du_cr)) {
nua_client_bind(cr = du->du_cr, NULL);
@@ -366,26 +375,20 @@ void nua_dialog_usage_remove_at(nua_owner_t *own,
nua_server_request_destroy(sr);
}
*at = du->du_next;
o = du->du_event;
SU_DEBUG_5(("nua(%p): removing %s usage%s%s\n",
(void *)own, nua_dialog_usage_name(du),
o ? " with event " : "", o ? o->o_type :""));
du->du_class->usage_remove(own, ds, du);
su_home_unref(own);
su_free(own, du);
}
/* Zap dialog if there are no more usages */
if (ds->ds_usage == NULL) {
if (ds->ds_terminated)
;
else if (ds->ds_usage == NULL) {
nua_dialog_remove(own, ds, NULL);
ds->ds_has_events = 0;
ds->ds_terminated = 0;
return;
}
else if (!ds->ds_terminated) {
else {
nua_dialog_log_usage(own, ds);
}
}
@@ -561,6 +564,8 @@ int nua_dialog_shutdown(nua_owner_t *owner, nua_dialog_state_t *ds)
{
nua_dialog_usage_t *du;
ds->ds_terminated = 1;
do {
for (du = ds->ds_usage; du; du = du->du_next) {
if (!du->du_shutdown) {
@@ -575,8 +580,8 @@ int nua_dialog_shutdown(nua_owner_t *owner, nua_dialog_state_t *ds)
/** (Gracefully) terminate usage */
void nua_dialog_usage_shutdown(nua_owner_t *owner,
nua_dialog_state_t *ds,
nua_dialog_usage_t *du)
nua_dialog_state_t *ds,
nua_dialog_usage_t *du)
{
if (du) {
du->du_refresh = 0;
@@ -115,6 +115,8 @@ struct nua_server_request {
unsigned sr_terminating:1; /**< Terminate usage after final response */
unsigned sr_gracefully:1; /**< Terminate usage gracefully */
unsigned sr_neutral:1; /**< No effect on session or other usage */
/* Flags used with offer-answer */
unsigned sr_offer_recv:1; /**< We have received an offer */
unsigned sr_answer_sent:2; /**< We have answered (reliably, if >1) */
@@ -284,6 +286,9 @@ struct nua_client_request
unsigned cr_offer_recv:1; /**< Recv offer in a response */
unsigned cr_answer_sent:1; /**< Sent answer in (PR)ACK */
/* Flags with usage */
unsigned cr_neutral:1; /**< No effect on session or other usage */
/* Lifelong flags? */
unsigned cr_auto:1; /**< Request was generated by stack */
unsigned cr_has_contact:1; /**< Request has user Contact */
@@ -318,6 +323,7 @@ struct nua_dialog_state
unsigned ds_has_register:1; /**< We have registration */
unsigned ds_has_publish:1; /**< We have publish */
unsigned ds_got_session:1; /**< We have (or have had) session */
unsigned ds_got_referrals:1; /**< We have (or have had) referrals */
unsigned :0;
@@ -573,6 +579,10 @@ int nua_client_report(nua_client_request_t *cr,
nua_client_request_t *nua_client_request_pending(nua_client_request_t const *);
int nua_client_init_requests(nua_client_request_t *cr,
void const *cr0,
int invite);
/* ---------------------------------------------------------------------- */
extern nua_server_methods_t const
@@ -590,6 +600,7 @@ extern nua_server_methods_t const
nua_refer_server_methods, /**< REFER */
nua_publish_server_methods; /**< PUBLISH */
/** Return true if we have not sent final response to request */
static inline
int nua_server_request_is_pending(nua_server_request_t const *sr)
{
+176 -127
View File
@@ -177,6 +177,9 @@ static int nua_session_usage_shutdown(nua_owner_t *,
nua_dialog_state_t *,
nua_dialog_usage_t *);
static int nua_invite_client_ack(nua_client_request_t *cr, tagi_t const *tags);
static int nua_invite_client_deinit(nua_client_request_t *cr);
static nua_usage_class const nua_session_usage[1] = {
{
sizeof (nua_session_usage_t),
@@ -202,6 +205,7 @@ int nua_session_usage_add(nua_handle_t *nh,
if (ds->ds_has_session)
return -1;
ds->ds_has_session = 1;
ds->ds_got_session = 1;
return 0;
}
@@ -212,10 +216,44 @@ void nua_session_usage_remove(nua_handle_t *nh,
nua_dialog_usage_t *du)
{
nua_session_usage_t *ss = nua_dialog_usage_private(du);
nua_client_request_t *cr, *cr_next;
cr = du->du_cr;
if (cr && cr->cr_orq && cr->cr_status >= 200) {
ss->ss_reporting = 1;
nua_invite_client_ack(cr, NULL);
ss->ss_reporting = 0;
}
/* Destroy queued INVITE transactions */
for (cr = ds->ds_cr; cr; cr = cr_next) {
cr_next = cr->cr_next;
if (cr->cr_method != sip_method_invite)
continue;
if (cr == du->du_cr)
continue;
nua_stack_event(nh->nh_nua, nh,
NULL,
cr->cr_event,
SIP_481_NO_TRANSACTION,
NULL);
nua_client_request_destroy(cr);
cr_next = ds->ds_cr;
}
ds->ds_has_session = 0;
(void)ss;
ds->ds_has_session = 0;
nh->nh_has_invite = 0;
nh->nh_active_call = 0;
nh->nh_hold_remote = 0;
if (nh->nh_soa)
soa_destroy(nh->nh_soa), nh->nh_soa = NULL;
}
static
@@ -228,7 +266,7 @@ nua_dialog_usage_t *nua_dialog_usage_for_session(nua_dialog_state_t const *ds)
}
static
nua_session_usage_t *nua_session_usage_get(nua_dialog_state_t const *ds)
nua_session_usage_t *nua_session_usage_for_dialog(nua_dialog_state_t const *ds)
{
nua_dialog_usage_t *du;
@@ -245,13 +283,6 @@ static
void nua_session_usage_destroy(nua_handle_t *nh,
nua_session_usage_t *ss)
{
nh->nh_has_invite = 0;
nh->nh_active_call = 0;
nh->nh_hold_remote = 0;
if (nh->nh_soa)
soa_destroy(nh->nh_soa), nh->nh_soa = NULL;
/* Remove usage */
nua_dialog_usage_remove(nh, nh->nh_ds, nua_dialog_usage_public(ss));
@@ -482,13 +513,6 @@ static int nua_invite_client_report(nua_client_request_t *cr,
nta_outgoing_t *orq,
tagi_t const *tags);
static int nua_invite_client_ack(nua_client_request_t *cr, tagi_t const *tags);
static int nua_invite_client_ack_msg(nua_client_request_t *cr,
msg_t *msg, sip_t *sip,
tagi_t const *tags);
static int nua_invite_client_deinit(nua_client_request_t *cr);
nua_client_methods_t const nua_invite_client_methods = {
SIP_METHOD_INVITE,
0,
@@ -527,6 +551,9 @@ static int nua_invite_client_init(nua_client_request_t *cr,
nua_dialog_usage_t *du;
cr->cr_usage = du = nua_dialog_usage_for_session(nh->nh_ds);
/* Errors returned by nua_invite_client_init()
are neutral to session state */
cr->cr_neutral = 1;
if (nh_is_special(nh) ||
nua_stack_set_handle_special(nh, nh_has_invite, nua_i_error))
@@ -534,7 +561,15 @@ static int nua_invite_client_init(nua_client_request_t *cr,
else if (nh_referral_check(nh, tags) < 0)
return nua_client_return(cr, 900, "Invalid referral", msg);
if (!du)
if (du) {
nua_server_request_t *sr;
for (sr = nh->nh_ds->ds_sr; sr; sr = sr->sr_next)
/* INVITE in progress? */
if (sr->sr_usage == du && sr->sr_method == sip_method_invite &&
nua_server_request_is_pending(sr))
return nua_client_return(cr, SIP_491_REQUEST_PENDING, msg);
}
else
du = nua_dialog_usage_add(nh, nh->nh_ds, nua_session_usage, NULL);
if (!du)
return -1;
@@ -547,6 +582,8 @@ static int nua_invite_client_init(nua_client_request_t *cr,
NH_PGET(nh, min_se),
NH_PGET(nh, refresher));
cr->cr_neutral = 0;
return 0;
}
@@ -568,7 +605,7 @@ static int nua_invite_client_request(nua_client_request_t *cr,
invite_timeout = NH_PGET(nh, invite_timeout);
if (invite_timeout == 0)
invite_timeout = UINT_MAX;
/* Cancel if we don't get response within timeout*/
/* Send CANCEL if we don't get response within timeout*/
nua_dialog_usage_set_expires(du, invite_timeout);
nua_dialog_usage_set_refresh(du, 0);
@@ -808,12 +845,20 @@ static int nua_invite_client_report(nua_client_request_t *cr,
if (orq != cr->cr_orq && status != 100)
return 1;
if (ss == NULL) {
signal_call_state_change(nh, ss, status, phrase, nua_callstate_terminated);
return 1;
}
ss->ss_reporting = 1;
if (ss == NULL) {
next_state = nua_callstate_terminated;
if (cr->cr_neutral) {
signal_call_state_change(nh, ss, status, phrase, ss->ss_state);
ss->ss_reporting = 0;
return 1;
}
else if (status == 100) {
if (status == 100) {
next_state = nua_callstate_calling;
}
else if (status < 300 && cr->cr_graceful) {
@@ -927,11 +972,10 @@ int nua_stack_ack(nua_t *nua, nua_handle_t *nh, nua_event_t e,
{
nua_dialog_usage_t *du = nua_dialog_usage_for_session(nh->nh_ds);
nua_session_usage_t *ss = nua_dialog_usage_private(du);
nua_client_request_t *cr = du ? du->du_cr : NULL;
int error;
if (!du ||
!du->du_cr ||
du->du_cr->cr_orq == NULL ||
du->du_cr->cr_status < 200) {
if (!cr || cr->cr_orq == NULL || cr->cr_status < 200) {
UA_EVENT2(nua_i_error, 900, "No response to ACK");
return 1;
}
@@ -942,8 +986,9 @@ int nua_stack_ack(nua_t *nua, nua_handle_t *nh, nua_event_t e,
soa_set_params(nh->nh_soa, TAG_NEXT(tags));
}
if (nua_invite_client_ack(du->du_cr, tags) < 0) {
int error;
error = nua_invite_client_ack(cr, tags);
if (error < 0) {
ss->ss_reason = "SIP;cause=500;text=\"Internal Error\"";
ss->ss_reporting = 1; /* We report state here if BYE fails */
error = nua_client_create(nh, nua_r_bye, &nua_bye_client_methods, NULL);
@@ -953,6 +998,12 @@ int nua_stack_ack(nua_t *nua, nua_handle_t *nh, nua_event_t e,
? nua_callstate_terminated
: nua_callstate_terminating);
}
else {
if (!nua_client_is_queued(cr) && !nua_client_is_bound(cr))
nua_client_request_destroy(cr);
nua_client_init_requests(nh->nh_ds->ds_cr, cr, 1);
}
return 0;
}
@@ -967,6 +1018,7 @@ int nua_invite_client_ack(nua_client_request_t *cr, tagi_t const *tags)
{
nua_handle_t *nh = cr->cr_owner;
nua_dialog_state_t *ds = nh->nh_ds;
nua_session_usage_t *ss = nua_dialog_usage_private(cr->cr_usage);
msg_t *msg;
sip_t *sip;
@@ -974,6 +1026,9 @@ int nua_invite_client_ack(nua_client_request_t *cr, tagi_t const *tags)
sip_authorization_t *wa;
sip_proxy_authorization_t *pa;
sip_cseq_t *cseq;
nta_outgoing_t *ack;
int status = 200;
char const *phrase = "OK", *reason = NULL;
assert(ds->ds_leg);
assert(cr->cr_orq);
@@ -1009,94 +1064,75 @@ int nua_invite_client_ack(nua_client_request_t *cr, tagi_t const *tags)
;
else if (nta_msg_request_complete(msg, ds->ds_leg, SIP_METHOD_ACK, NULL) < 0)
;
else
error = nua_invite_client_ack_msg(cr, msg, sip, tags);
else {
/* Remove extra headers */
while (sip->sip_allow)
sip_header_remove(msg, sip, (sip_header_t*)sip->sip_allow);
while (sip->sip_priority)
sip_header_remove(msg, sip, (sip_header_t*)sip->sip_priority);
while (sip->sip_proxy_require)
sip_header_remove(msg, sip, (sip_header_t*)sip->sip_proxy_require);
while (sip->sip_require)
sip_header_remove(msg, sip, (sip_header_t*)sip->sip_require);
while (sip->sip_subject)
sip_header_remove(msg, sip, (sip_header_t*)sip->sip_subject);
while (sip->sip_supported)
sip_header_remove(msg, sip, (sip_header_t*)sip->sip_supported);
nta_outgoing_destroy(cr->cr_orq), cr->cr_orq = NULL;
if (error == -1)
msg_destroy(msg);
return error;
}
/** Send ACK, destroy INVITE transaction.
*
* @retval 1 if successful
* @retval -2 if an error occurred
*/
static
int nua_invite_client_ack_msg(nua_client_request_t *cr,
msg_t *msg, sip_t *sip,
tagi_t const *tags)
{
nua_handle_t *nh = cr->cr_owner;
nua_dialog_usage_t *du = cr->cr_usage;
nua_session_usage_t *ss = nua_dialog_usage_private(du);
nta_outgoing_t *ack;
int status = 200;
char const *phrase = "OK", *reason = NULL;
/* Remove extra headers */
while (sip->sip_allow)
sip_header_remove(msg, sip, (sip_header_t*)sip->sip_allow);
while (sip->sip_priority)
sip_header_remove(msg, sip, (sip_header_t*)sip->sip_priority);
while (sip->sip_proxy_require)
sip_header_remove(msg, sip, (sip_header_t*)sip->sip_proxy_require);
while (sip->sip_require)
sip_header_remove(msg, sip, (sip_header_t*)sip->sip_require);
while (sip->sip_subject)
sip_header_remove(msg, sip, (sip_header_t*)sip->sip_subject);
while (sip->sip_supported)
sip_header_remove(msg, sip, (sip_header_t*)sip->sip_supported);
if (!nh->nh_soa)
;
else if (cr->cr_offer_recv && !cr->cr_answer_sent) {
if (soa_generate_answer(nh->nh_soa, NULL) < 0 ||
session_include_description(nh->nh_soa, 1, msg, sip) < 0) {
status = 900, phrase = "Internal media error";
reason = "SIP;cause=500;text=\"Internal media error\"";
/* reason = soa_error_as_sip_reason(nh->nh_soa); */
if (!nh->nh_soa || ss == NULL)
;
else if (cr->cr_offer_recv && !cr->cr_answer_sent) {
if (soa_generate_answer(nh->nh_soa, NULL) < 0 ||
session_include_description(nh->nh_soa, 1, msg, sip) < 0) {
status = 900, phrase = "Internal media error";
reason = "SIP;cause=500;text=\"Internal media error\"";
/* reason = soa_error_as_sip_reason(nh->nh_soa); */
}
else {
cr->cr_answer_sent = 1;
soa_activate(nh->nh_soa, NULL);
/* signal that O/A round is complete */
ss->ss_oa_sent = "answer";
}
if (!reason &&
/* ss->ss_offer_sent && !ss->ss_answer_recv */
!soa_is_complete(nh->nh_soa)) {
/* No SDP answer in 2XX response -> terminate call */
status = 988, phrase = "Incomplete offer/answer";
reason = "SIP;cause=488;text=\"Incomplete offer/answer\"";
}
}
else {
cr->cr_answer_sent = 1;
soa_activate(nh->nh_soa, NULL);
/* signal that O/A round is complete */
ss->ss_oa_sent = "answer";
if ((ack = nta_outgoing_mcreate(nh->nh_nua->nua_nta, NULL, NULL, NULL,
msg,
SIPTAG_END(),
TAG_NEXT(tags)))) {
nta_outgoing_destroy(ack); /* TR engine keeps this around for T2 */
}
else if (!reason) {
status = 900, phrase = "Cannot send ACK";
reason = "SIP;cause=500;text=\"Internal Error\"";
}
if (!reason &&
/* ss->ss_offer_sent && !ss->ss_answer_recv */
!soa_is_complete(nh->nh_soa)) {
/* No SDP answer in 2XX response -> terminate call */
status = 988, phrase = "Incomplete offer/answer";
reason = "SIP;cause=488;text=\"Incomplete offer/answer\"";
}
}
if ((ack = nta_outgoing_mcreate(nh->nh_nua->nua_nta, NULL, NULL, NULL,
msg,
SIPTAG_END(),
TAG_NEXT(tags)))) {
nta_outgoing_destroy(ack); /* TR engine keeps this around for T2 */
}
else if (!reason) {
status = 900, phrase = "Cannot send ACK";
reason = "SIP;cause=500;text=\"Internal Error\"";
}
if (ss) {
if (reason)
if (ss && reason)
ss->ss_reason = reason;
if (!ss->ss_reporting && status < 300)
if (status < 300)
error = 1;
else
error = -2;
}
nta_outgoing_destroy(cr->cr_orq), cr->cr_orq = NULL;
nua_client_request_remove(cr);
if (ss) {
if (!ss->ss_reporting && error >= 0)
signal_call_state_change(nh, ss, status, phrase, nua_callstate_ready);
}
return status < 300 ? 1 : -2;
return error;
}
/** Deinitialize client request */
@@ -1220,7 +1256,7 @@ static void nua_session_usage_refresh(nua_handle_t *nh,
if (cr->cr_method == sip_method_update)
return;
/* INVITE or UPDATE in progress or being authenticated */
/* INVITE or UPDATE in progress */
for (sr = ds->ds_sr; sr; sr = sr->sr_next)
if (sr->sr_usage == du &&
(sr->sr_method == sip_method_invite ||
@@ -1258,7 +1294,7 @@ static int nua_session_usage_shutdown(nua_handle_t *nh,
nua_server_request_t *sr, *sr_next;
nua_client_request_t *cri;
assert(ss == nua_session_usage_get(nh->nh_ds));
assert(ss == nua_session_usage_for_dialog(nh->nh_ds));
/* Zap server-side transactions */
for (sr = ds->ds_sr; sr; sr = sr_next) {
@@ -1663,6 +1699,8 @@ nua_invite_server_init(nua_server_request_t *sr)
nua_handle_t *nh = sr->sr_owner;
nua_t *nua = nh->nh_nua;
sr->sr_neutral = 1;
if (!NUA_PGET(nua, nh, invite_enable))
return SR_STATUS1(sr, SIP_403_FORBIDDEN);
@@ -1688,9 +1726,10 @@ nua_invite_server_init(nua_server_request_t *sr)
break;
}
if (sr0)
if (sr0) {
/* Overlapping invites - RFC 3261 14.2 */
return nua_server_retry_after(sr, 500, "Overlapping Requests", 0, 10);
}
for (cr = nh->nh_ds->ds_cr; cr; cr = cr->cr_next) {
if (cr->cr_usage == sr->sr_usage && cr->cr_orq && cr->cr_offer_sent)
@@ -1699,6 +1738,8 @@ nua_invite_server_init(nua_server_request_t *sr)
}
}
sr->sr_neutral = 0;
return 0;
}
@@ -2024,6 +2065,7 @@ int nua_invite_server_report(nua_server_request_t *sr, tagi_t const *tags)
nua_dialog_usage_t *du = sr->sr_usage;
nua_session_usage_t *ss = nua_dialog_usage_private(sr->sr_usage);
int initial = sr->sr_initial && !sr->sr_event;
int neutral = sr->sr_neutral;
int application = sr->sr_application;
int status = sr->sr_status; char const *phrase = sr->sr_phrase;
int retval;
@@ -2036,18 +2078,18 @@ int nua_invite_server_report(nua_server_request_t *sr, tagi_t const *tags)
if (retval >= 2 || ss == NULL) {
/* Session has been terminated. */
if (!initial)
if (!initial && !neutral)
signal_call_state_change(nh, NULL, status, phrase,
nua_callstate_terminated);
return retval;
}
assert(ss);
assert(ss->ss_state != nua_callstate_calling);
assert(ss->ss_state != nua_callstate_proceeding);
/* Update session state */
if (status < 300 || application != 0)
if (status < 300 || application != 0) {
assert(ss->ss_state != nua_callstate_calling);
assert(ss->ss_state != nua_callstate_proceeding);
signal_call_state_change(nh, ss, status, phrase,
status >= 300
? nua_callstate_init
@@ -2056,6 +2098,7 @@ int nua_invite_server_report(nua_server_request_t *sr, tagi_t const *tags)
: status > 100
? nua_callstate_early
: nua_callstate_received);
}
if (status == 180)
ss->ss_alerting = 1;
@@ -2065,7 +2108,7 @@ int nua_invite_server_report(nua_server_request_t *sr, tagi_t const *tags)
if (200 <= status && status < 300) {
du->du_ready = 1;
}
else if (300 <= status) {
else if (300 <= status && !neutral) {
if (nh->nh_soa)
soa_init_offer_answer(nh->nh_soa);
}
@@ -2196,16 +2239,20 @@ int process_cancel(nua_server_request_t *sr,
{
nua_handle_t *nh = sr->sr_owner;
nua_session_usage_t *ss = nua_dialog_usage_private(sr->sr_usage);
msg_t *cancel = nta_incoming_getrequest_ackcancel(irq);
if (nta_incoming_status(irq) < 200 && nua_server_request_is_pending(sr) &&
ss && (ss == nua_session_usage_get(nh->nh_ds))) {
nua_stack_event(nh->nh_nua, nh, cancel, nua_i_cancel, SIP_200_OK, NULL);
assert(ss); assert(ss == nua_session_usage_for_dialog(nh->nh_ds)); (void)ss;
SR_STATUS1(sr, SIP_487_REQUEST_TERMINATED);
if (nua_server_request_is_pending(sr)) {
msg_t *cancel = nta_incoming_getrequest_ackcancel(irq);
nua_server_respond(sr, NULL);
nua_server_report(sr);
assert(nta_incoming_status(irq) < 200);
nua_stack_event(nh->nh_nua, nh, cancel, nua_i_cancel, SIP_200_OK, NULL);
SR_STATUS1(sr, SIP_487_REQUEST_TERMINATED);
nua_server_respond(sr, NULL);
nua_server_report(sr);
}
return 0;
@@ -2222,7 +2269,7 @@ int process_timeout(nua_server_request_t *sr,
char const *reason = "SIP;cause=408;text=\"ACK Timeout\"";
int error;
assert(ss); assert(ss == nua_session_usage_get(nh->nh_ds));
assert(ss); assert(ss == nua_session_usage_for_dialog(nh->nh_ds));
if (nua_server_request_is_pending(sr)) {
phrase = "PRACK Timeout";
@@ -3328,7 +3375,7 @@ nua_client_methods_t const nua_bye_client_methods = {
int
nua_stack_bye(nua_t *nua, nua_handle_t *nh, nua_event_t e, tagi_t const *tags)
{
nua_session_usage_t *ss = nua_session_usage_get(nh->nh_ds);
nua_session_usage_t *ss = nua_session_usage_for_dialog(nh->nh_ds);
if (ss &&
nua_callstate_calling <= ss->ss_state &&
@@ -3430,9 +3477,11 @@ static int nua_bye_client_report(nua_client_request_t *cr,
signal_call_state_change(nh, ss, status, "to BYE",
nua_callstate_terminated);
if (ss && !ss->ss_reporting && !nua_client_is_queued(du->du_cr)) {
/* Do not destroy session usage while INVITE is alive */
nua_session_usage_destroy(nh, ss);
if (ss && !ss->ss_reporting) {
if (nua_client_is_queued(du->du_cr) && du->du_cr->cr_status < 200)
/* No final response to INVITE received yet */;
else
nua_session_usage_destroy(nh, ss);
}
}
+44 -16
View File
@@ -743,14 +743,14 @@ void nh_destroy(nua_t *nua, nua_handle_t *nh)
if (nh->nh_notifier)
nea_server_destroy(nh->nh_notifier), nh->nh_notifier = NULL;
nua_dialog_deinit(nh, nh->nh_ds);
while (nh->nh_ds->ds_cr)
nua_client_request_destroy(nh->nh_ds->ds_cr);
while (nh->nh_ds->ds_sr)
nua_server_request_destroy(nh->nh_ds->ds_sr);
nua_dialog_deinit(nh, nh->nh_ds);
if (nh->nh_soa)
soa_destroy(nh->nh_soa), nh->nh_soa = NULL;
@@ -1090,6 +1090,9 @@ int nua_stack_process_request(nua_handle_t *nh,
nta_incoming_tag(irq, NULL);
if (method == sip_method_cancel)
return 481;
/* Hook to outbound */
if (method == sip_method_options) {
status = nua_registration_process_request(nua->nua_registrations,
@@ -1485,6 +1488,8 @@ int nua_server_respond(nua_server_request_t *sr, tagi_t const *tags)
else if (next.msg)
msg_destroy(next.msg);
assert(sr->sr_status >= 200 || sr->sr_response.msg);
return retval;
}
@@ -1521,7 +1526,7 @@ int nua_server_report(nua_server_request_t *sr)
if (sr)
return sr->sr_methods->sm_report(sr, NULL);
else
return 2;
return 1;
}
int nua_base_server_treport(nua_server_request_t *sr,
@@ -1763,8 +1768,11 @@ inline int nua_client_request_queue(nua_client_request_t *cr)
}
}
else {
while (*queue)
while (*queue) {
queue = &(*queue)->cr_next;
if (cr->cr_method == sip_method_invite)
queued = 1;
}
}
if ((cr->cr_next = *queue))
@@ -2543,7 +2551,7 @@ int nua_base_client_response(nua_client_request_t *cr,
tagi_t const *tags)
{
nua_handle_t *nh = cr->cr_owner;
int next;
sip_method_t method = cr->cr_method;
cr->cr_reporting = 1;
@@ -2552,14 +2560,15 @@ int nua_base_client_response(nua_client_request_t *cr,
nua_client_report(cr, status, phrase, sip, cr->cr_orq, tags);
if (status >= 200)
nua_client_request_remove(cr);
if (cr->cr_method == sip_method_invite ? status < 300 : status < 200) {
if (status < 200 ||
/* Un-ACKed 2XX response to INVITE */
(cr->cr_method == sip_method_invite && status < 300 && cr->cr_orq)) {
cr->cr_reporting = 0;
return 1;
}
nua_client_request_remove(cr);
if (cr->cr_orq)
nta_outgoing_destroy(cr->cr_orq), cr->cr_orq = NULL;
@@ -2585,16 +2594,12 @@ int nua_base_client_response(nua_client_request_t *cr,
if (nua_client_is_queued(cr))
return 1;
next = cr->cr_method != sip_method_invite && cr->cr_method != sip_method_cancel;
if (!nua_client_is_bound(cr))
nua_client_request_destroy(cr);
if (next && nh->nh_ds->ds_cr != NULL && nh->nh_ds->ds_cr != cr) {
cr = nh->nh_ds->ds_cr;
if (cr->cr_method != sip_method_invite && cr->cr_method != sip_method_cancel)
nua_client_init_request(cr);
}
if (method != sip_method_cancel)
return nua_client_init_requests(nh->nh_ds->ds_cr, cr,
method == sip_method_invite);
return 1;
}
@@ -2638,6 +2643,29 @@ int nua_client_treport(nua_client_request_t *cr,
return retval;
}
int nua_client_init_requests(nua_client_request_t *cr,
void const *cr0,
int invite)
{
if (cr0 == cr) /* already initialized! */
return 1;
for (; cr; cr = cr->cr_next) {
if (cr->cr_method == sip_method_cancel)
continue;
if (invite
? cr->cr_method == sip_method_invite
: cr->cr_method != sip_method_invite)
break;
}
if (cr)
nua_client_init_request(cr);
return 1;
}
nua_client_request_t *
nua_client_request_pending(nua_client_request_t const *cr)
{
@@ -1578,6 +1578,310 @@ int test_update_by_uas(struct context *ctx)
END();
}
int cancel_when_pracked(CONDITION_PARAMS);
int alert_call(CONDITION_PARAMS);
int test_180rel_cancel1(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;
if (print_headings)
printf("TEST NUA-10.6: CANCEL after PRACK\n");
/* Test for 100rel:
A B
|-------INVITE------>|
|<----100 Trying-----|
| |
|<-------180---------|
|-------PRACK------->|
|<-------200---------|
| |
|------CANCEL------->|
|<------200 OK-------|
| |
|<-------487---------|
|--------ACK-------->|
| |
*/
a_call->sdp = "m=audio 5008 RTP/AVP 8";
b_call->sdp = "m=audio 5010 RTP/AVP 0 8";
nua_set_params(ctx->a.nua,
NUTAG_EARLY_MEDIA(1),
NUTAG_ONLY183_100REL(0),
TAG_END());
run_a_until(ctx, nua_r_set_params, until_final_response);
nua_set_params(ctx->b.nua,
NUTAG_EARLY_MEDIA(1),
NUTAG_ONLY183_100REL(0),
TAG_END());
run_b_until(ctx, nua_r_set_params, until_final_response);
TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END()));
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),
TAG_END());
run_ab_until(ctx, -1, cancel_when_pracked, -1, alert_call);
/* Client transitions:
INIT -(C1)-> CALLING: nua_invite(), nua_i_state
CALLING -(C2)-> PROCEEDING: nua_r_invite, nua_i_state, nua_r_prack
PROCEEDING -(C3+C4)-> TERMINATED: 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(is_offer_sent(e->data->e_tags));
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);
TEST_1(is_answer_recv(e->data->e_tags));
TEST_1(!is_offer_sent(e->data->e_tags));
TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_prack);
TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_cancel);
TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
TEST(e->data->e_status, 487);
TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
TEST(callstate(e->data->e_tags), nua_callstate_terminated);
TEST_1(!e->next);
free_events_in_list(ctx, a->events);
/*
Server transitions:
INIT -(S1)-> RECEIVED: nua_i_invite, nua_i_state
RECEIVED -(S2a)-> EARLY: nua_respond(), nua_i_state
Option A:
EARLY -(S10)-> TERMINATED: nua_i_cancel, nua_i_state
Option B:
EARLY -(S3b)-> COMPLETED: nua_respond(), nua_i_state
COMPLETED -(S4)-> READY: nua_i_ack, nua_i_state
READY -(T1)-> TERMINATED: nua_i_bye, 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(is_offer_recv(e->data->e_tags));
/* Responded with 180 Ringing */
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(is_answer_sent(e->data->e_tags));
/* 180 is PRACKed */
TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_prack);
/* Does not have effect on call state */
TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_cancel);
TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
free_events_in_list(ctx, b->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-10.6: PASSED\n");
END();
}
int cancel_when_pracked(CONDITION_PARAMS)
{
if (!(check_handle(ep, call, nh, SIP_500_INTERNAL_SERVER_ERROR)))
return 0;
save_event_in_list(ctx, event, ep, call);
if (event == nua_r_prack)
CANCEL(ep, call, nh, TAG_END());
switch (callstate(tags)) {
case nua_callstate_proceeding:
return 0;
case nua_callstate_ready:
return 1;
case nua_callstate_terminated:
return 1;
default:
return 0;
}
}
int test_180rel_cancel2(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;
if (print_headings)
printf("TEST NUA-10.7: CANCEL after 100rel 180\n");
/* Test for 100rel:
A B
|-------INVITE------>|
|<----100 Trying-----|
| |
|<-------180---------|
|-------PRACK------->|
|<-------200---------|
| |
|------CANCEL------->|
|<------200 OK-------|
| |
|<-------487---------|
|--------ACK-------->|
| |
*/
a_call->sdp = "m=audio 5008 RTP/AVP 8";
b_call->sdp = "m=audio 5010 RTP/AVP 0 8";
nua_set_params(ctx->a.nua,
NUTAG_EARLY_MEDIA(1),
NUTAG_ONLY183_100REL(0),
TAG_END());
run_a_until(ctx, nua_r_set_params, until_final_response);
nua_set_params(ctx->b.nua,
NUTAG_EARLY_MEDIA(1),
NUTAG_ONLY183_100REL(0),
TAG_END());
run_b_until(ctx, nua_r_set_params, until_final_response);
TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END()));
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),
TAG_END());
run_ab_until(ctx, -1, cancel_when_ringing, -1, accept_pracked2);
/* Client transitions:
INIT -(C1)-> CALLING: nua_invite(), nua_i_state
CALLING -(C2)-> PROCEEDING: nua_r_invite, nua_i_state, nua_r_prack
PROCEEDING -(C3+C4)-> TERMINATED: 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(is_offer_sent(e->data->e_tags));
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);
TEST_1(is_answer_recv(e->data->e_tags));
TEST_1(!is_offer_sent(e->data->e_tags));
#define NEXT_SKIP_PRACK_CANCEL() \
do { TEST_1(e = e->next); } \
while (e->data->e_event == nua_r_prack || e->data->e_event == nua_r_cancel)
NEXT_SKIP_PRACK_CANCEL();
TEST_E(e->data->e_event, nua_r_invite);
if (e->data->e_status == 487) {
TEST(e->data->e_status, 487);
TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
TEST(callstate(e->data->e_tags), nua_callstate_terminated);
TEST_1(!e->next);
}
else {
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_ready);
BYE(a, a_call, a_call->nh, TAG_END());
run_ab_until(ctx, -1, until_terminated, -1, until_terminated);
NEXT_SKIP_PRACK_CANCEL(); TEST_E(e->data->e_event, nua_r_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);
/*
Server transitions:
INIT -(S1)-> RECEIVED: nua_i_invite, nua_i_state
RECEIVED -(S2a)-> EARLY: nua_respond(), nua_i_state
Option A:
EARLY -(S10)-> TERMINATED: nua_i_cancel, nua_i_state
Option B:
EARLY -(S3b)-> COMPLETED: nua_respond(), nua_i_state
COMPLETED -(S4)-> READY: nua_i_ack, nua_i_state
READY -(T1)-> TERMINATED: nua_i_bye, 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(is_offer_recv(e->data->e_tags));
/* Responded with 180 Ringing */
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(is_answer_sent(e->data->e_tags));
/* 180 is PRACKed */
TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_prack);
/* Does not have effect on call state */
if (e->next->data->e_event == nua_i_cancel) {
TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_cancel);
TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
}
else {
/* Respond with 200 OK */
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(!is_offer_answer_done(e->data->e_tags));
TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_ack);
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(!is_offer_answer_done(e->data->e_tags));
TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_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 */
}
free_events_in_list(ctx, b->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-10.7: PASSED\n");
END();
}
int test_100rel(struct context *ctx)
{
@@ -1589,6 +1893,8 @@ int test_100rel(struct context *ctx)
retval = test_preconditions(ctx); RETURN_ON_SINGLE_FAILURE(retval);
retval = test_preconditions2(ctx); RETURN_ON_SINGLE_FAILURE(retval);
retval = test_update_by_uas(ctx); RETURN_ON_SINGLE_FAILURE(retval);
retval = test_180rel_cancel1(ctx); RETURN_ON_SINGLE_FAILURE(retval);
retval = test_180rel_cancel2(ctx); RETURN_ON_SINGLE_FAILURE(retval);
nua_set_params(ctx->a.nua,
NUTAG_EARLY_MEDIA(0),
@@ -1612,3 +1918,4 @@ int test_100rel(struct context *ctx)
return retval;
}
@@ -467,7 +467,7 @@ int test_call_hold(struct context *ctx)
*/
if (print_headings)
printf("TEST NUA-7.6: re-INVITE without auto-ack\n");
printf("TEST NUA-7.6.1: re-INVITE without auto-ack\n");
/* Turn off auto-ack */
nua_set_hparams(b_call->nh, NUTAG_AUTOACK(0), TAG_END());
@@ -517,7 +517,7 @@ int test_call_hold(struct context *ctx)
free_events_in_list(ctx, a->events);
if (print_headings)
printf("TEST NUA-7.6: PASSED\n");
printf("TEST NUA-7.6.1: PASSED\n");
/* ---------------------------------------------------------------------- */
@@ -528,7 +528,7 @@ int test_call_hold(struct context *ctx)
*/
if (print_headings)
printf("TEST NUA-7.6: terminate call\n");
printf("TEST NUA-7.6.2: terminate call\n");
BYE(a, a_call, a_call->nh, TAG_END());
run_ab_until(ctx, -1, until_terminated, -1, until_terminated);
@@ -555,7 +555,7 @@ int test_call_hold(struct context *ctx)
free_events_in_list(ctx, b->events);
if (print_headings)
printf("TEST NUA-7.6: PASSED\n");
printf("TEST NUA-7.6.2: PASSED\n");
nua_handle_destroy(a_call->nh), a_call->nh = NULL;
nua_handle_destroy(b_call->nh), b_call->nh = NULL;
@@ -725,9 +725,6 @@ int test_reinvite(struct context *ctx)
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-7.8: PASSED\n");
END();
}
@@ -784,6 +781,211 @@ int ringing_until_terminated(CONDITION_PARAMS)
}
}
/* ======================================================================== */
int accept_and_attempt_reinvite(CONDITION_PARAMS);
int until_ready2(CONDITION_PARAMS);
/* test_reinvite2 message sequence looks like this:
A B
| |
|-------INVITE------>|
|<----100 Trying-----|
| |
|<----180 Ringing----|
| |
| /-INVITE-|
| \---900->|
| |
|<--------200--------|
|---------ACK------->|
: :
: queue INVITE :
: :
|-----re-INVITE----->|
|<--------200--------|
|---------ACK------->|
|-----re-INVITE----->|
|<--------200--------|
|---------ACK------->|
: :
: glare :
: :
|-----re-INVITE----->|
|<----re-INVITE------|
|<--------491--------|
|---------491------->|
|---------ACK------->|
|<--------ACK--------|
: :
|---------BYE------->|
|<--------200--------|
*/
int test_reinvite2(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;
if (print_headings)
printf("TEST NUA-7.8.1: Test re-INVITE glare\n");
a_call->sdp = "m=audio 5008 RTP/AVP 0 8\n";
b_call->sdp = "m=audio 5010 RTP/AVP 8\n";
TEST_1(a_call->nh =
nua_handle(a->nua, a_call,
SIPTAG_FROM_STR("Alice <sip:alice@example.com>"),
SIPTAG_TO(b->to),
TAG_END()));
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),
TAG_END());
run_ab_until(ctx, -1, until_ready, -1, accept_and_attempt_reinvite);
TEST_1(nua_handle_has_active_call(a_call->nh));
TEST_1(nua_handle_has_active_call(b_call->nh));
free_events_in_list(ctx, a->events);
free_events_in_list(ctx, b->events);
/* Check that we can queue INVITEs */
INVITE(a, a_call, a_call->nh, TAG_END());
INVITE(a, a_call, a_call->nh, TAG_END());
run_ab_until(ctx, -1, until_ready2, -1, until_ready2);
free_events_in_list(ctx, a->events);
free_events_in_list(ctx, b->events);
/* Check that INVITE glare works */
INVITE(a, a_call, a_call->nh, TAG_END());
INVITE(b, b_call, b_call->nh, TAG_END());
a->flags.n = 0, b->flags.n = 0;
run_ab_until(ctx, -1, until_ready2, -1, until_ready2);
free_events_in_list(ctx, a->events);
free_events_in_list(ctx, b->events);
if (print_headings)
printf("TEST NUA-7.8.1: PASSED\n");
/* ---------------------------------------------------------------------- */
/*
A B
|---------BYE------->|
|<--------200--------|
*/
if (print_headings)
printf("TEST NUA-7.8.2: terminate call\n");
BYE(a, a_call, a_call->nh, TAG_END());
run_ab_until(ctx, -1, until_terminated, -1, until_terminated);
/*
Transitions of A:
READY --(T2)--> TERMINATING: nua_bye()
TERMINATING --(T3)--> TERMINATED: nua_r_bye, nua_i_state
*/
TEST_1(e = a->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, a->events);
/* Transitions of B:
READY -(T1)-> TERMINATED: nua_i_bye, nua_i_state
*/
TEST_1(e = b->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, b->events);
if (print_headings)
printf("TEST NUA-7.8.2: PASSED\n");
nua_handle_destroy(a_call->nh), a_call->nh = NULL;
nua_handle_destroy(b_call->nh), b_call->nh = NULL;
END();
}
int accept_and_attempt_reinvite(CONDITION_PARAMS)
{
if (!(check_handle(ep, call, nh, SIP_500_INTERNAL_SERVER_ERROR)))
return 0;
save_event_in_list(ctx, event, ep, call);
if (event == nua_i_prack) {
INVITE(ep, call, nh, TAG_END());
RESPOND(ep, call, nh, SIP_200_OK,
TAG_IF(call->sdp, SOATAG_USER_SDP_STR(call->sdp)),
TAG_END());
}
else switch (callstate(tags)) {
case nua_callstate_received:
RESPOND(ep, call, nh, SIP_180_RINGING,
SIPTAG_REQUIRE_STR("100rel"),
TAG_END());
return 0;
case nua_callstate_early:
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;
}
return 0;
}
/*
X INVITE
| |
|-------INVITE------>|
|<--------200--------|
|---------ACK------->|
*/
int until_ready2(CONDITION_PARAMS)
{
if (!(check_handle(ep, call, nh, SIP_500_INTERNAL_SERVER_ERROR)))
return 0;
save_event_in_list(ctx, event, ep, call);
if (event == nua_r_invite && status == 491) {
if (ep == &ctx->a && ++ctx->b.flags.n >= 2) ctx->b.running = 0;
if (ep == &ctx->b && ++ctx->a.flags.n >= 2) ctx->a.running = 0;
}
switch (callstate(tags)) {
case nua_callstate_ready:
return ++ep->flags.n >= 2;
case nua_callstate_terminated:
if (call)
nua_handle_destroy(call->nh), call->nh = NULL;
return 1;
default:
return 0;
}
}
int test_reinvites(struct context *ctx)
{
int retval = 0;
@@ -794,7 +996,10 @@ int test_reinvites(struct context *ctx)
retval = test_call_hold(ctx);
if (retval == 0)
retval |= test_reinvite(ctx);
retval = test_reinvite(ctx);
if (retval == 0)
retval = test_reinvite2(ctx);
if (print_headings && retval == 0)
printf("TEST NUA-7: PASSED\n");
@@ -118,6 +118,8 @@ int cancel_when_ringing(CONDITION_PARAMS)
case nua_callstate_proceeding:
CANCEL(ep, call, nh, TAG_END());
return 0;
case nua_callstate_ready:
return 1;
case nua_callstate_terminated:
return 1;
default:
@@ -673,6 +675,82 @@ int test_call_destroy_4(struct context *ctx)
END();
}
/* Destroy when one INVITE is queued. */
int test_call_destroy_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;
if (print_headings)
printf("TEST NUA-5.7: destroy when re-INVITE is queued\n");
TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END()));
INVITE(a, a_call, a_call->nh,
TAG_IF(!ctx->proxy_tests, NUTAG_URL(b->contact->m_url)),
NUTAG_AUTOACK(0),
SOATAG_USER_SDP_STR(a_call->sdp),
TAG_END());
INVITE(a, a_call, a_call->nh, TAG_END());
run_ab_until(ctx, -1, until_terminated, -1, destroy_when_completed);
/* Client transitions:
INIT -(C1)-> CALLING: nua_invite(), ...
*/
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(is_offer_sent(e->data->e_tags));
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(e = e->next); TEST_E(e->data->e_event, nua_i_state);
TEST(callstate(e->data->e_tags), nua_callstate_completing); /* COMPLETING */
TEST_1(is_answer_recv(e->data->e_tags));
TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_bye);
TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
TEST(e->data->e_status, 481);
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;
/*
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 ... DESTROY
*/
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(is_offer_recv(e->data->e_tags));
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(is_answer_sent(e->data->e_tags));
TEST_1(!e->next);
free_events_in_list(ctx, b->events);
nua_handle_destroy(b_call->nh), b_call->nh = NULL;
if (print_headings)
printf("TEST NUA-5.7: PASSED\n");
END();
}
int test_call_destroy(struct context *ctx)
{
struct endpoint *a = &ctx->a, *b = &ctx->b;
@@ -685,10 +763,12 @@ int test_call_destroy(struct context *ctx)
test_call_destroy_1(ctx) ||
test_call_destroy_2(ctx) ||
test_call_destroy_3(ctx) ||
test_call_destroy_4(ctx);
test_call_destroy_4(ctx) ||
test_call_destroy_5(ctx);
}
/* ======================================================================== */
/* Early BYE
A B
@@ -731,12 +811,27 @@ int bye_when_ringing(CONDITION_PARAMS)
}
}
int bye_when_completing(CONDITION_PARAMS);
static int ack_sent = 0;
size_t count_acks(void *message, size_t len)
{
if (strncasecmp(message, "ACK sip:", 8) == 0)
ack_sent++;
return len;
}
int test_early_bye(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;
struct nat_filter *f = NULL;
a_call->sdp = "m=audio 5008 RTP/AVP 8";
b_call->sdp = "m=audio 5010 RTP/AVP 0 8";
if (print_headings)
printf("TEST NUA-6.1: BYE call when ringing\n");
@@ -806,6 +901,119 @@ int test_early_bye(struct context *ctx)
if (print_headings)
printf("TEST NUA-6.1: PASSED\n");
/* Early BYE 2
A B
|-------INVITE------>|
|<----100 Trying-----|
| |
|<----180 Ringing----|
|<-------200---------|
| |
|--------BYE-------->|
|<------200 OK-------|
|--------ACK-------->|
| |
| |
*/
if (print_headings)
printf("TEST NUA-6.2: BYE call when completing\n");
if (ctx->nat)
TEST_1(f = test_nat_add_filter(ctx->nat, count_acks, nat_outbound));
ack_sent = 0;
TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END()));
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, bye_when_completing, -1, accept_until_terminated);
/* Client transitions:
INIT -(C1)-> CALLING: nua_invite(), nua_i_state
CALLING -(C2)-> PROCEEDING: nua_r_invite(180, nua_i_state, nua_cancel()
PROCEEDING -(C6b)-> TERMINATED: nua_r_invite(487), 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);
TEST_1(is_offer_sent(e->data->e_tags));
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);
TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
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_completing);
TEST_1(e = e->next);
TEST_E(e->data->e_event, nua_r_bye); TEST(e->data->e_status, 200);
TEST_1(e->data->e_msg);
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);
if (ctx->nat) {
while (ack_sent == 0)
su_root_step(ctx->root, 100);
TEST_1(ack_sent > 0);
TEST_1(test_nat_remove_filter(ctx->nat, f) == 0);
}
/*
Server transitions:
INIT -(S1)-> RECEIVED: nua_i_invite, nua_i_state
RECEIVED -(S2a)-> EARLY: nua_respond(180), nua_i_state
EARLY -(S6b)--> TERMINATED: nua_i_cancel, 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(is_offer_recv(e->data->e_tags));
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); /* EARLY */
TEST_1(e = e->next); 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;
free_events_in_list(ctx, b->events);
nua_handle_destroy(b_call->nh), b_call->nh = NULL;
if (print_headings)
printf("TEST NUA-6.2: PASSED\n");
END();
}
int bye_when_completing(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_sent = 0;
BYE(ep, call, nh, TAG_END());
return 0;
case nua_callstate_terminated:
return 1;
default:
return 0;
}
}
@@ -166,12 +166,11 @@ struct context
struct eventlist specials[1];
/* State flags for complex scenarios */
union {
struct {
unsigned bit0:1, bit1:1, bit2:1, bit3:1;
unsigned bit4:1, bit5:1, bit6:1, bit7:1;
} b;
struct {
unsigned n;
unsigned bit0:1, bit1:1, bit2:1, bit3:1;
unsigned bit4:1, bit5:1, bit6:1, bit7:1;
unsigned :0;
} flags;
} a, b, c;
@@ -215,6 +214,7 @@ int save_until_special(CONDITION_PARAMS);
int until_terminated(CONDITION_PARAMS);
int until_ready(CONDITION_PARAMS);
int accept_call(CONDITION_PARAMS);
int cancel_when_ringing(CONDITION_PARAMS);
int accept_notify(CONDITION_PARAMS);
@@ -272,21 +272,21 @@ void run_abc_until(struct context *ctx,
a->last_event = -1;
a->running = a_condition != NULL && a_condition != save_events;
a->running |= a_event != -1;
a->flags.n = 0;
memset(&a->flags, 0, sizeof a->flags);
b->next_event = b_event;
b->next_condition = b_condition;
b->last_event = -1;
b->running = b_condition != NULL && b_condition != save_events;
b->running |= b_event != -1;
b->flags.n = 0;
memset(&b->flags, 0, sizeof b->flags);
c->next_event = c_event;
c->next_condition = c_condition;
c->last_event = -1;
c->running = c_condition != NULL && c_condition != save_events;
c->running |= c_event != -1;
c->flags.n = 0;
memset(&c->flags, 0, sizeof c->flags);
for (; a->running || b->running || c->running;) {
su_root_step(ctx->root, 1000);
@@ -228,6 +228,7 @@ test_proxy_init(su_root_t *root, struct proxy *proxy)
URL_STRING_MAKE("sip:0.0.0.0:*"),
NULL, NULL,
NTATAG_UA(0),
NTATAG_CANCEL_487(0),
NTATAG_SERVER_RPORT(1),
NTATAG_CLIENT_RPORT(1),
TAG_NEXT(proxy->tags));
@@ -64,15 +64,15 @@ int save_until_notified(CONDITION_PARAMS)
int save_until_notified_and_responded(CONDITION_PARAMS)
{
save_event_in_list(ctx, event, ep, call);
if (event == nua_i_notify) ep->flags.b.bit0 = 1;
if (event == nua_i_notify) ep->flags.bit0 = 1;
if (event == nua_r_subscribe || event == nua_r_unsubscribe) {
if (status >= 300)
return 1;
else if (status >= 200)
ep->flags.b.bit1 = 1;
ep->flags.bit1 = 1;
}
return ep->flags.b.bit0 && ep->flags.b.bit1;
return ep->flags.bit0 && ep->flags.bit1;
}
@@ -422,6 +422,7 @@ void su_pthread_port_wait(su_clone_r rclone)
pthread_mutex_unlock(mom->mutex);
pthread_mutex_destroy(mom->mutex);
pthread_cond_destroy(mom->cv);
}
struct su_pthread_port_execute