[Freeswitch-svn] [commit] r12381 - in freeswitch/trunk/libs/sofia-sip: . libsofia-sip-ua/nta

FreeSWITCH SVN mikej at freeswitch.org
Tue Mar 3 09:22:36 PST 2009


Author: mikej
Date: Tue Mar  3 11:22:35 2009
New Revision: 12381

Log:
Tue Mar  3 10:47:00 CST 2009  Pekka Pessi <first.last at nokia.com>
  * nta: timeout CANCELed INVITE transactions properly
  Ignore-this: 4e7fdc56065dba617352443a9310bb28
  
  Use timer D (instead of timer C) to timeout CANCELed INVITE transactions.
  
  Also, generate 408 Request Timeout to all forks that have not received a
  final response.



Modified:
   freeswitch/trunk/libs/sofia-sip/.update
   freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nta/nta.c

Modified: freeswitch/trunk/libs/sofia-sip/.update
==============================================================================
--- freeswitch/trunk/libs/sofia-sip/.update	(original)
+++ freeswitch/trunk/libs/sofia-sip/.update	Tue Mar  3 11:22:35 2009
@@ -1 +1 @@
-Tue Mar  3 11:21:35 CST 2009
+Tue Mar  3 11:22:16 CST 2009

Modified: freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nta/nta.c
==============================================================================
--- freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nta/nta.c	(original)
+++ freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nta/nta.c	Tue Mar  3 11:22:35 2009
@@ -556,6 +556,8 @@
 
   nta_outgoing_t       *orq_cancel;     /**< CANCEL transaction */
 
+  nta_outgoing_t       *orq_forking;    /**< Untagged transaction */
+  nta_outgoing_t       *orq_forks;	/**< Tagged transactions */
   uint32_t              orq_rseq;       /**< Latest incoming rseq */
 };
 
@@ -7022,10 +7024,10 @@
 static void outgoing_destroy(nta_outgoing_t *orq);
 su_inline int outgoing_is_queued(nta_outgoing_t const *orq);
 su_inline void outgoing_queue(outgoing_queue_t *queue,
-				  nta_outgoing_t *orq);
+			      nta_outgoing_t *orq);
 su_inline void outgoing_remove(nta_outgoing_t *orq);
 su_inline void outgoing_set_timer(nta_outgoing_t *orq, uint32_t interval);
-su_inline void outgoing_reset_timer(nta_outgoing_t *orq);
+static void outgoing_reset_timer(nta_outgoing_t *orq);
 static size_t outgoing_timer_dk(outgoing_queue_t *q,
 				char const *timer,
 				uint32_t now);
@@ -7043,6 +7045,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 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);
@@ -7350,8 +7353,7 @@
 
   ta_end(ta);
 
-  if ((cancel_2543 || cancel_408) &&
-      !orq->orq_stateless && !orq->orq_destroyed)
+  if ((cancel_2543 || cancel_408) && !orq->orq_stateless)
     outgoing_reply(orq, SIP_487_REQUEST_CANCELLED, 1);
 
   if (msg) {
@@ -7369,8 +7371,11 @@
     if (delay_sending)
       orq->orq_cancel = cancel;
 
-    if (cancel)
+    if (cancel) {
+      if (!delay_sending)
+	outgoing_complete(orq);
       return cancel;
+    }
 
     msg_destroy(msg);
   }
@@ -7684,6 +7689,7 @@
     /* CANCEL or ACK to [3456]XX */
     invalid = tport_name_dup(home, orq->orq_tpn, tpn);
 #if HAVE_SOFIA_SRESOLV
+    /* We send ACK or CANCEL only if original request was really sent */
     assert(tport_name_is_resolved(orq->orq_tpn));
 #endif
     resolved = tport_name_is_resolved(orq->orq_tpn);
@@ -8318,8 +8324,8 @@
 /** @internal
  * Test if an outgoing transaction is in a queue.
  */
-su_inline
-int outgoing_is_queued(nta_outgoing_t const *orq)
+su_inline int
+outgoing_is_queued(nta_outgoing_t const *orq)
 {
   return orq && orq->orq_queue;
 }
@@ -8330,9 +8336,9 @@
  * Insert a client transaction into a queue and set the corresponding
  * timeout at the same time.
  */
-su_inline
-void outgoing_queue(outgoing_queue_t *queue,
-		    nta_outgoing_t *orq)
+static void
+outgoing_queue(outgoing_queue_t *queue,
+	       nta_outgoing_t *orq)
 {
   if (orq->orq_queue == queue) {
     assert(queue->q_timeout == 0);
@@ -8422,7 +8428,7 @@
     orq->orq_agent->sa_out.re_t1 = rq;
 }
 
-su_inline
+static
 void outgoing_reset_timer(nta_outgoing_t *orq)
 {
   if (orq->orq_rprev) {
@@ -8541,8 +8547,10 @@
   /* Application is expected to handle 200 OK statelessly
      => kill transaction immediately */
   else if (orq->orq_method == sip_method_invite && !orq->orq_completed
-	   /* (unless we the transaction has been canceled) */
-	   && !orq->orq_canceled) {
+	   /* (unless transaction has been canceled) */
+	   && !orq->orq_canceled
+	   /* or it has been forked */
+	   && !orq->orq_forking && !orq->orq_forks) {
     orq->orq_destroyed = 1;
     outgoing_terminate(orq);
   }
@@ -8717,12 +8725,11 @@
     SU_DEBUG_5(("nta: timer %s fired, %s %s (%u)\n",
 		timer, "CANCEL and timeout",
 		orq->orq_method_name, orq->orq_cseq->cs_seq));
-
+    /*
+     * If the client transaction has received a provisional response, the
+     * proxy MUST generate a CANCEL request matching that transaction.
+     */
     nta_outgoing_tcancel(orq, NULL, NULL, TAG_NULL());
-
-    outgoing_timeout(orq, now);
-
-    assert(q->q_head != orq);
   }
 
   return timeout;
@@ -8731,16 +8738,18 @@
 /** @internal Signal transaction timeout to the application. */
 void outgoing_timeout(nta_outgoing_t *orq, uint32_t now)
 {
-  nta_outgoing_t *cancel;
+  nta_outgoing_t *cancel = NULL;
 
-  if (outgoing_other_destinations(orq)) {
+  if (orq->orq_status || orq->orq_canceled)
+    ;
+  else if (outgoing_other_destinations(orq)) {
     SU_DEBUG_5(("%s(%p): %s\n", "nta", (void *)orq,
 		"try next after timeout"));
     outgoing_try_another(orq);
     return;
   }
 
-  cancel = orq->orq_cancel; orq->orq_cancel = NULL;
+  cancel = orq->orq_cancel, orq->orq_cancel = NULL;
   orq->orq_agent->sa_stats->as_tout_request++;
 
   outgoing_reply(orq, SIP_408_REQUEST_TIMEOUT, 0);
@@ -8753,15 +8762,19 @@
  *
  * @return True if transaction was free()d.
  */
-static
-int outgoing_complete(nta_outgoing_t *orq)
+static int
+outgoing_complete(nta_outgoing_t *orq)
 {
   orq->orq_completed = 1;
 
   outgoing_reset_timer(orq); /* Timer A / Timer E */
 
-  if (orq->orq_stateless || orq->orq_reliable)
+  if (orq->orq_stateless)
     return outgoing_terminate(orq);
+  if (orq->orq_reliable) {
+    if (orq->orq_method != sip_method_invite || !orq->orq_agent->sa_is_a_uas)
+      return outgoing_terminate(orq);
+  }
 
   if (orq->orq_method == sip_method_invite) {
     if (orq->orq_queue != orq->orq_agent->sa_out.inv_completed)
@@ -8793,12 +8806,52 @@
     SU_DEBUG_5(("nta: timer %s fired, %s %s (%u)\n", timer,
 		"terminate", orq->orq_method_name, orq->orq_cseq->cs_seq));
 
-    outgoing_terminate(orq);
+    if (orq->orq_method == sip_method_invite)
+      outgoing_terminate_invite(orq);
+    else
+      outgoing_terminate(orq);
   }
 
   return terminated;
 }
 
+
+/** Terminate an INVITE client transaction. */
+static void
+outgoing_terminate_invite(nta_outgoing_t *original)
+{
+  nta_outgoing_t *orq = original;
+
+  while (original->orq_forks) {
+    orq = original->orq_forks;
+    original->orq_forks = orq->orq_forks;
+
+    assert(orq->orq_forking == original);
+
+    SU_DEBUG_5(("nta: timer %s fired, %s %s (%u);tag=%s\n", "D",
+		"terminate", orq->orq_method_name, orq->orq_cseq->cs_seq,
+		orq->orq_tag));
+
+    if (outgoing_terminate(orq))
+      continue;
+
+    if (orq->orq_status < 200) {
+      /* Fork has timed out */
+      orq->orq_agent->sa_stats->as_tout_request++;
+      outgoing_reply(orq, SIP_408_REQUEST_TIMEOUT, 0);
+    }
+  }
+
+  if (outgoing_terminate(orq = original))
+    return;
+
+  if (orq->orq_status < 200) {
+    /* Original INVITE has timed out */
+    orq->orq_agent->sa_stats->as_tout_request++;
+    outgoing_reply(orq, SIP_408_REQUEST_TIMEOUT, 0);
+  }
+}
+
 /** Terminate a client transaction. */
 static
 int outgoing_terminate(nta_outgoing_t *orq)
@@ -8946,46 +8999,38 @@
 }
 
 /** Process a response message. */
-int outgoing_recv(nta_outgoing_t *orq,
+int outgoing_recv(nta_outgoing_t *_orq,
 		  int status,
 		  msg_t *msg,
 		  sip_t *sip)
 {
+  nta_outgoing_t *orq = _orq->orq_forking ? _orq->orq_forking : _orq;
   nta_agent_t *sa = orq->orq_agent;
-  short orq_status = orq->orq_status;
   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);
+
   if (status < 100) status = 100;
 
   if (!internal && orq->orq_delay == UINT_MAX)
     outgoing_estimate_delay(orq, sip);
 
-  assert(!internal || status >= 300);
-
   if (orq->orq_cc)
     agent_accept_compressed(orq->orq_agent, msg, orq->orq_cc);
 
   if (orq->orq_cancel) {
     nta_outgoing_t *cancel;
-
     cancel = orq->orq_cancel; orq->orq_cancel = NULL;
-
     cancel->orq_delayed = 0;
 
-    if (status < 200)
+    if (status < 200) {
       outgoing_send(cancel, 0);
-    else
+      outgoing_complete(orq);
+    }
+    else {
       outgoing_reply(cancel, SIP_481_NO_TRANSACTION, 0);
-
-    if (status < 300 && orq->orq_destroyed &&
-	orq->orq_method == sip_method_invite) {
-      outgoing_terminate(orq);      /* We can now kill transaction */
-      if (status == 100) {
-	msg_destroy(msg);
-	return 0;
-      }
-      return -1;
     }
   }
 
@@ -8998,9 +9043,12 @@
 
   /* The state machines */
   if (orq->orq_method == sip_method_invite) {
+    nta_outgoing_t *original = orq;
 
-    if (uas && orq->orq_destroyed && 200 <= status && status < 300) {
-      if (su_strcasecmp(sip->sip_to->a_tag, orq->orq_tag) != 0) {
+    orq = _orq;
+
+    if (orq->orq_destroyed && 200 <= status && status < 300) {
+      if (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);
@@ -9008,38 +9056,41 @@
       return -1;  /* Proxy statelessly (RFC3261 section 16.11) */
     }
 
-    outgoing_reset_timer(orq);
+    outgoing_reset_timer(original);
 
     if (status < 200) {
-      if (orq->orq_queue == sa->sa_out.inv_calling) {
-	orq->orq_status = status;
-	outgoing_queue(sa->sa_out.inv_proceeding, orq);
+      original->orq_status = status;
+      orq->orq_status = status;
+      if (original->orq_queue == sa->sa_out.inv_calling) {
+	outgoing_queue(sa->sa_out.inv_proceeding, original);
       }
-      else if (orq->orq_queue == sa->sa_out.inv_proceeding) {
-	orq->orq_status = status;
+      else if (original->orq_queue == sa->sa_out.inv_proceeding) {
 	if (sa->sa_out.inv_proceeding->q_timeout) {
-	  outgoing_remove(orq);
-	  outgoing_queue(sa->sa_out.inv_proceeding, orq);
+	  outgoing_remove(original);
+	  outgoing_queue(sa->sa_out.inv_proceeding, original);
 	}
       }
+    }
 
+    if (status < 200) {
       /* Handle 100rel */
-      if (sip && sip->sip_rseq)
+      if (sip && sip->sip_rseq) {
 	if (outgoing_recv_reliable(orq, msg, sip) < 0) {
 	  msg_destroy(msg);
 	  return 0;
 	}
+      }
     }
     else {
       /* Final response */
       if (status >= 300 && !internal)
-	outgoing_ack(orq, sip);
+	outgoing_ack(original, sip);
 
-      if (!orq->orq_completed) {
-	if (outgoing_complete(orq))
+      if (!original->orq_completed) {
+	if (outgoing_complete(original))
 	  return 0;
 
-	if (sip && uas) {
+	if (uas && sip && orq == original) {
 	  /*
 	   * We silently discard duplicate final responses to INVITE below
 	   * with outgoing_duplicate()
@@ -9049,7 +9100,7 @@
 	}
       }
       /* Retransmission or response from another fork */
-      else {
+      else if (orq->orq_status >= 200) {
 	/* Once 2xx has been received, non-2xx will not be forwarded */
 	if (status >= 300)
 	  return outgoing_duplicate(orq, msg, sip);
@@ -9072,7 +9123,7 @@
     /* Non-INVITE */
     if (orq->orq_queue == sa->sa_out.trying ||
 	orq->orq_queue == sa->sa_out.resolving) {
-      assert(orq_status < 200); (void)orq_status;
+      assert(orq->orq_status < 200);
 
       if (status < 200) {
 	/* @RFC3261 17.1.2.1:
@@ -11066,9 +11117,20 @@
 
   if (orq == NULL || to_tag == NULL)
     return NULL;
+
   if (orq->orq_to->a_tag) {
-    SU_DEBUG_1(("%s: transaction %p already in dialog\n", __func__,
-		(void *)orq));
+    SU_DEBUG_1(("%s: transaction %p (CSeq: %s %u) already in dialog\n", __func__,
+		(void *)orq, orq->orq_cseq->cs_method_name, orq->orq_cseq->cs_seq));
+    return NULL;
+  }
+  if (orq->orq_method != sip_method_invite) {
+    SU_DEBUG_1(("%s: transaction %p (CSeq: %s %u) cannot be tagged\n", __func__,
+		(void *)orq, orq->orq_cseq->cs_method_name, orq->orq_cseq->cs_seq));
+    return NULL;
+  }
+  if (orq->orq_status < 100) {
+    SU_DEBUG_1(("%s: transaction %p (CSeq: %s %u) still calling\n", __func__,
+		(void *)orq, orq->orq_cseq->cs_method_name, orq->orq_cseq->cs_seq));
     return NULL;
   }
 
@@ -11098,17 +11160,12 @@
   tagged->orq_request      = msg_ref_create(orq->orq_request);
   tagged->orq_response     = msg_ref_create(orq->orq_response);
   tagged->orq_cancel       = NULL;
-
-  tagged->orq_pending = tport_pend(orq->orq_tport,
-				   orq->orq_request,
-				   outgoing_tport_error,
-				   tagged);
-  if (tagged->orq_pending < 0)
-    tagged->orq_pending = 0;
+  tagged->orq_forking      = orq;
+  tagged->orq_forks        = orq->orq_forks;
+  orq->orq_forks = tagged;
 
   tagged->orq_rseq = 0;
 
-  outgoing_queue(orq->orq_queue, tagged);
   outgoing_insert(agent, tagged);
 
   return tagged;



More information about the Freeswitch-svn mailing list