Tue Jul 7 12:50:57 CDT 2009 Pekka Pessi <first.last@nokia.com>

* nta.c: handling of forked transactions getting CANCELed and timing out
  Ignore-this: 30a92f1c8721f9d3fa22b9611ac9e7b1
  
  Fixes freeswitch.org bug FSCORE-338.
  
  Thanks for Gabriel Kuri for reporting the problem.



git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@14167 d0543943-73ff-0310-b7d9-9358b9ac24b2
This commit is contained in:
Michael Jerris 2009-07-09 16:00:36 +00:00
parent 915fa372d8
commit 5dabbe8635
2 changed files with 92 additions and 44 deletions

View File

@ -1 +1 @@
Fri Jun 26 14:54:58 CDT 2009
Thu Jul 9 10:56:15 CDT 2009

View File

@ -519,14 +519,15 @@ struct nta_outgoing_s
unsigned orq_destroyed:1;
unsigned orq_completed:1;
unsigned orq_delayed:1;
unsigned orq_stripped_uri:1;
unsigned orq_user_tport:1; /**< Application provided tport - don't retry */
unsigned orq_try_tcp_instead:1;
unsigned orq_try_udp_instead:1;
unsigned orq_reliable:1; /**< Transport is reliable */
unsigned orq_ack_error:1; /**< ACK is sent by NTA */
unsigned orq_forked:1; /**< Tagged fork */
/* Attributes */
unsigned orq_uas:1; /**< Running this transaction as UAS */
unsigned orq_user_via:1;
unsigned orq_stateless:1;
unsigned orq_pass_100:1;
@ -701,7 +702,7 @@ static void request_merge(nta_agent_t *,
msg_t *msg, sip_t *sip, tport_t *tport,
char const *to_tag);
su_inline int incoming_timestamp(nta_incoming_t *, msg_t *, sip_t *);
static void incoming_timer(nta_agent_t *);
static void _nta_incoming_timer(nta_agent_t *);
static nta_reliable_t *reliable_mreply(nta_incoming_t *,
nta_prack_f *, nta_reliable_magic_t *,
@ -736,7 +737,7 @@ static nta_outgoing_t *outgoing_find(nta_agent_t const *sa,
sip_via_t const *v);
static int outgoing_recv(nta_outgoing_t *orq, int status, msg_t *, sip_t *);
static void outgoing_default_recv(nta_outgoing_t *, int, msg_t *, sip_t *);
static void outgoing_timer(nta_agent_t *);
static void _nta_outgoing_timer(nta_agent_t *);
static int outgoing_recv_reliable(nta_outgoing_t *orq, msg_t *msg, sip_t *sip);
/* Internal message passing */
@ -1234,8 +1235,8 @@ void agent_timer(su_root_magic_t *rm, su_timer_t *timer, nta_agent_t *agent)
agent->sa_millisec = now;
agent->sa_in_timer = 1;
outgoing_timer(agent);
incoming_timer(agent);
_nta_outgoing_timer(agent);
_nta_incoming_timer(agent);
/* agent->sa_now is used only if sa_millisec != 0 */
agent->sa_millisec = 0;
@ -6792,7 +6793,8 @@ enum {
};
/** @internal Timer routine for the incoming request. */
static void incoming_timer(nta_agent_t *sa)
static void
_nta_incoming_timer(nta_agent_t *sa)
{
uint32_t now = sa->sa_millisec;
nta_incoming_t *irq, *irq_next;
@ -7083,6 +7085,7 @@ static void outgoing_trying(nta_outgoing_t *orq);
static void outgoing_timeout(nta_outgoing_t *orq, uint32_t now);
static int outgoing_complete(nta_outgoing_t *orq);
static void outgoing_terminate_invite(nta_outgoing_t *);
static void outgoing_remove_fork(nta_outgoing_t *orq);
static int outgoing_terminate(nta_outgoing_t *orq);
static size_t outgoing_mass_destroy(nta_agent_t *sa, outgoing_queue_t *q);
static void outgoing_estimate_delay(nta_outgoing_t *orq, sip_t *sip);
@ -7360,6 +7363,10 @@ nta_outgoing_t *nta_outgoing_tcancel(nta_outgoing_t *orq,
SU_DEBUG_3(("%s: trying to cancel non-INVITE request\n", __func__));
return NULL;
}
if (orq->orq_forking)
orq = orq->orq_forking;
if (orq->orq_status >= 200
/* && orq->orq_method != sip_method_invite ... !multicast */) {
SU_DEBUG_3(("%s: trying to cancel completed request\n", __func__));
@ -7735,6 +7742,7 @@ nta_outgoing_t *outgoing_create(nta_agent_t *agent,
orq->orq_stateless = stateless != 0;
orq->orq_user_via = user_via != 0 && sip->sip_via;
orq->orq_100rel = invite_100rel;
orq->orq_uas = !stateless && agent->sa_is_a_uas;
if (cc)
orq->orq_cc = nta_compartment_ref(cc);
@ -7822,9 +7830,9 @@ nta_outgoing_t *outgoing_create(nta_agent_t *agent,
else
orq->orq_branch = su_sprintf(home, "branch=%s", ack_branch);
}
else if (!stateless && agent->sa_is_a_uas) {
else if (orq->orq_uas) {
/*
* ACK redirect further 2XX messages to it.
* ACK redirects further 2XX messages to it.
*
* Use orq_branch from INVITE, but put a different branch in topmost Via.
*/
@ -8414,12 +8422,11 @@ outgoing_queue(outgoing_queue_t *queue,
return;
}
assert(!orq->orq_forked);
if (outgoing_is_queued(orq))
outgoing_remove(orq);
assert(orq->orq_next == NULL);
assert(*queue->q_tail == NULL);
orq->orq_timeout = set_timeout(orq->orq_agent, queue->q_timeout);
orq->orq_queue = queue;
@ -8525,8 +8532,8 @@ void outgoing_free(nta_outgoing_t *orq)
}
/** Remove outgoing request from hash tables */
su_inline
void outgoing_cut_off(nta_outgoing_t *orq)
su_inline void
outgoing_cut_off(nta_outgoing_t *orq)
{
nta_agent_t *agent = orq->orq_agent;
@ -8539,6 +8546,11 @@ void outgoing_cut_off(nta_outgoing_t *orq)
if (outgoing_is_queued(orq))
outgoing_remove(orq);
#if 0
if (orq->orq_forked)
outgoing_remove_fork(orq);
#endif
outgoing_reset_timer(orq);
if (orq->orq_pending) {
@ -8637,7 +8649,8 @@ void outgoing_destroy(nta_outgoing_t *orq)
/** @internal Outgoing transaction timer routine.
*
*/
static void outgoing_timer(nta_agent_t *sa)
static void
_nta_outgoing_timer(nta_agent_t *sa)
{
uint32_t now = sa->sa_millisec;
nta_outgoing_t *orq;
@ -8742,7 +8755,9 @@ void outgoing_retransmit(nta_outgoing_t *orq)
static
void outgoing_trying(nta_outgoing_t *orq)
{
if (orq->orq_method == sip_method_invite)
if (orq->orq_forked)
;
else if (orq->orq_method == sip_method_invite)
outgoing_queue(orq->orq_agent->sa_out.inv_calling, orq);
else
outgoing_queue(orq->orq_agent->sa_out.trying, orq);
@ -8847,8 +8862,14 @@ outgoing_complete(nta_outgoing_t *orq)
if (orq->orq_stateless)
return outgoing_terminate(orq);
if (orq->orq_forked) {
outgoing_remove_fork(orq);
return outgoing_terminate(orq);
}
if (orq->orq_reliable) {
if (orq->orq_method != sip_method_invite || !orq->orq_agent->sa_is_a_uas)
if (orq->orq_method != sip_method_invite || !orq->orq_uas)
return outgoing_terminate(orq);
}
@ -8908,7 +8929,7 @@ outgoing_terminate_invite(nta_outgoing_t *original)
"terminate", orq->orq_method_name, orq->orq_cseq->cs_seq,
orq->orq_tag));
orq->orq_forking = NULL;
orq->orq_forking = NULL, orq->orq_forks = NULL, orq->orq_forked = 0;
if (outgoing_terminate(orq))
continue;
@ -8930,6 +8951,25 @@ outgoing_terminate_invite(nta_outgoing_t *original)
}
}
static void
outgoing_remove_fork(nta_outgoing_t *orq)
{
nta_outgoing_t **slot;
for (slot = &orq->orq_forking->orq_forks;
*slot;
slot = &(*slot)->orq_forks) {
if (orq == *slot) {
*slot = orq->orq_forks;
orq->orq_forks = NULL;
orq->orq_forking = NULL;
orq->orq_forked = 0;
}
}
assert(orq == NULL);
}
/** Terminate a client transaction. */
static
int outgoing_terminate(nta_outgoing_t *orq)
@ -9031,7 +9071,7 @@ nta_outgoing_t *outgoing_find(nta_agent_t const *sa,
/* Get original invite when ACKing */
if (sip->sip_request && method == sip_method_ack && v == NULL)
method = sip_method_invite, method2 = sip_method_invalid;
else if (sa->sa_is_a_uas && status >= 200 && method == sip_method_invite)
else if (sa->sa_is_a_uas && 200 <= status && status < 300 && method == sip_method_invite)
method2 = sip_method_ack;
else
method2 = method;
@ -9062,10 +9102,8 @@ nta_outgoing_t *outgoing_find(nta_agent_t const *sa,
su_strcasecmp(orq->orq_to->a_tag, sip->sip_to->a_tag))
continue;
if (orq->orq_method == sip_method_ack) {
if (orq->orq_ack_error ? status < 300 : status >= 300)
continue;
}
if (orq->orq_method == sip_method_ack && 300 <= status)
continue;
if (v && !su_casematch(orq->orq_branch + strlen("branch="), v->v_branch))
continue;
@ -9085,7 +9123,6 @@ int outgoing_recv(nta_outgoing_t *_orq,
nta_outgoing_t *orq = _orq->orq_forking ? _orq->orq_forking : _orq;
nta_agent_t *sa = orq->orq_agent;
int internal = sip == NULL || (sip->sip_flags & NTA_INTERNAL_MSG) != 0;
int uas = sa->sa_is_a_uas;
assert(!internal || status >= 300);
assert(orq == _orq || orq->orq_method == sip_method_invite);
@ -9126,7 +9163,7 @@ int outgoing_recv(nta_outgoing_t *_orq,
orq = _orq;
if (orq->orq_destroyed && 200 <= status && status < 300) {
if (uas && su_strcasecmp(sip->sip_to->a_tag, orq->orq_tag) != 0) {
if (orq->orq_uas && su_strcasecmp(sip->sip_to->a_tag, orq->orq_tag) != 0) {
/* Orphan 200 Ok to INVITE. ACK and BYE it */
SU_DEBUG_5(("nta: Orphan 200 Ok send ACK&BYE\n"));
return nta_msg_ackbye(sa, msg);
@ -9134,11 +9171,14 @@ int outgoing_recv(nta_outgoing_t *_orq,
return -1; /* Proxy statelessly (RFC3261 section 16.11) */
}
outgoing_reset_timer(original);
outgoing_reset_timer(original); /* Retransmission */
if (status < 200) {
original->orq_status = status;
orq->orq_status = status;
if (original->orq_status < 200)
original->orq_status = status;
if (orq->orq_status < 200)
orq->orq_status = status;
if (original->orq_queue == sa->sa_out.inv_calling) {
outgoing_queue(sa->sa_out.inv_proceeding, original);
}
@ -9148,9 +9188,7 @@ int outgoing_recv(nta_outgoing_t *_orq,
outgoing_queue(sa->sa_out.inv_proceeding, original);
}
}
}
if (status < 200) {
/* Handle 100rel */
if (sip && sip->sip_rseq) {
if (outgoing_recv_reliable(orq, msg, sip) < 0) {
@ -9168,7 +9206,7 @@ int outgoing_recv(nta_outgoing_t *_orq,
if (outgoing_complete(original))
return 0;
if (uas && sip && orq == original) {
if (orq->orq_uas && sip && orq == original) {
/*
* We silently discard duplicate final responses to INVITE below
* with outgoing_duplicate()
@ -9183,7 +9221,7 @@ int outgoing_recv(nta_outgoing_t *_orq,
if (status >= 300)
return outgoing_duplicate(orq, msg, sip);
if (uas) {
if (orq->orq_uas) {
if (su_strcasecmp(sip->sip_to->a_tag, orq->orq_tag) == 0)
/* Catch retransmission */
return outgoing_duplicate(orq, msg, sip);
@ -9243,16 +9281,21 @@ int outgoing_recv(nta_outgoing_t *_orq,
return 0;
}
if (status + orq->orq_pass_100 > 100 && !orq->orq_destroyed) {
if (orq->orq_response)
msg_destroy(orq->orq_response);
orq->orq_response = msg;
/* Call callback */
orq->orq_callback(orq->orq_magic, orq, sip);
}
else
if (100 >= status + orq->orq_pass_100) {
msg_destroy(msg);
return 0;
}
if (orq->orq_destroyed) {
msg_destroy(msg);
return 0;
}
if (orq->orq_response)
msg_destroy(orq->orq_response);
orq->orq_response = msg;
/* Call callback */
orq->orq_callback(orq->orq_magic, orq, sip);
return 0;
}
@ -11233,13 +11276,18 @@ nta_outgoing_t *nta_outgoing_tagged(nta_outgoing_t *orq,
sip_to_tag(home, to = sip_to_copy(home, orq->orq_to), to_tag);
tagged->orq_to = to;
tagged->orq_tag = to->a_tag;
tagged->orq_tport = tport_ref(orq->orq_tport);
tagged->orq_request = msg_ref_create(orq->orq_request);
tagged->orq_response = msg_ref_create(orq->orq_response);
tagged->orq_cancel = NULL;
tagged->orq_forking = orq;
tagged->orq_forks = orq->orq_forks;
orq->orq_forks = tagged;
if ((tagged->orq_uas = orq->orq_uas)) {
tagged->orq_forking = orq;
tagged->orq_forks = orq->orq_forks;
tagged->orq_forked = 1;
orq->orq_forks = tagged;
}
tagged->orq_rseq = 0;