[Freeswitch-svn] [commit] r7109 - in freeswitch/trunk/libs/sofia-sip: . libsofia-sip-ua libsofia-sip-ua-glib libsofia-sip-ua/docs libsofia-sip-ua/nta libsofia-sip-ua/nua libsofia-sip-ua/stun libsofia-sip-ua/tport m4 scripts tests

Freeswitch SVN mikej at freeswitch.org
Sun Jan 6 15:15:11 EST 2008


Author: mikej
Date: Sun Jan  6 15:15:11 2008
New Revision: 7109

Added:
   freeswitch/trunk/libs/sofia-sip/scripts/hide_emails.sh
Removed:
   freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/docs/hide_emails.sh
Modified:
   freeswitch/trunk/libs/sofia-sip/.update
   freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua-glib/Makefile.am
   freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/Makefile.am
   freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nta/nta.c
   freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nta/test_nta.c
   freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nua/nua_notifier.c
   freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nua/nua_session.c
   freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nua/nua_subnotref.c
   freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nua/outbound.c
   freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/stun/stun.c
   freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/tport/tport_tag.c
   freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/tport/tport_type_udp.c
   freeswitch/trunk/libs/sofia-sip/m4/sac-su2.m4
   freeswitch/trunk/libs/sofia-sip/tests/check_sofia.c

Log:
Merge current sofia-sip darcs tree:

Thu Dec 13 09:15:04 EST 2007  Pekka.Pessi at nokia.com
  * libsofia-sip-ua/docs/hide_emails.sh: moved to scripts/ subdir

Thu Dec 13 09:15:34 EST 2007  Pekka.Pessi at nokia.com
  * check_sofia.c: pass xml result file as optional parameter to check_sofia

Thu Dec 20 08:13:37 EST 2007  Pekka.Pessi at nokia.com
  * stun.c: try to avoid using stun handle after returning from discovery callback
  
  Crash reported and partial patch by Daniele Rondina.

Thu Jan  3 07:11:27 EST 2008  Pekka.Pessi at nokia.com
  * tport_type_udp.c: using SO_RCVBUFFORCE and SO_SNDBUFFORCE to set rmem/wmem on udp sockets
  
  Referring reader to Linux sysctls to TPTAG_UDP_RMEM and TPTAG_UDP_WMEM
  documentation.

Thu Jan  3 07:11:47 EST 2008  Pekka.Pessi at nokia.com
  * m4/sac-su2.m4: checks for SO_RCVBUFFORCE and SO_SNDBUFFORCE

Thu Jan  3 08:19:04 EST 2008  Pekka.Pessi at nokia.com
  * nta.c: calculate next timeout only after completing current timeout
    
  Thanks to Mike Jerris for reporting this problem.

Thu Jan  3 11:02:11 EST 2008  Pekka.Pessi at nokia.com
  * sac-su2.m4: checking for IP_ADD_MEMBERSHIP and IP_MULTICAST_LOOP

Thu Jan  3 12:08:39 EST 2008  Pekka.Pessi at nokia.com
  * tport_type_udp.c: when binding to multicast address, join to the group, too.
  
  Use "canonic" IP address (from host-part of the SIP URI) to specify
  interface.

Mon Nov 19 15:01:09 EST 2007  Pekka Pessi <first.lastname at nokia.com>
  * tport_type_udp.c: made IP_ADD_MEMBERSHIP as portable

Fri Jan  4 13:19:01 EST 2008  Pekka.Pessi at nokia.com
  * test_nta.c: added check for request merging (with both 3261 and 2543 proxies)

Fri Jan  4 13:20:35 EST 2008  Pekka.Pessi at nokia.com
  * nta.c: fixed request merging with RFC 2543 proxies
    
  Updated matching of PRACKs with outstanding 100rel, too.

Fri Jan  4 15:27:01 EST 2008  Pekka.Pessi at nokia.com
  * nta.c: follow more closely RFC 3261 request matching rules

Fri Jan  4 15:31:22 EST 2008  Pekka.Pessi at nokia.com
  * nua_session.c: do not clear soa when an overlapping INVITE is received
  
Fri Jan  4 15:32:58 EST 2008  Pekka.Pessi at nokia.com
  * nua/outbound.c: reduce logging

Fri Jan  4 16:51:00 EST 2008  Pekka.Pessi at nokia.com
  * nua_subnotref.c: accept NOTIFY without Event header

Fri Jan  4 16:53:20 EST 2008  Pekka.Pessi at nokia.com
  * nua_notifier.c: fix problem handing expiration time if NOTIFY is sent before SUBSCRIBE has been responded

Fri Jan  4 16:54:08 EST 2008  Pekka.Pessi at nokia.com
  * nua_notifier.c: allow notifier handle to be shut down if SUBSCRIBE has been accpeted but no NOTIFY has been sent



Modified: freeswitch/trunk/libs/sofia-sip/.update
==============================================================================
--- freeswitch/trunk/libs/sofia-sip/.update	(original)
+++ freeswitch/trunk/libs/sofia-sip/.update	Sun Jan  6 15:15:11 2008
@@ -1 +1 @@
-Thu Oct 11 15:56:47 EDT 2007
+Sun Jan  6 15:11:42 EST 2008

Modified: freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua-glib/Makefile.am
==============================================================================
--- freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua-glib/Makefile.am	(original)
+++ freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua-glib/Makefile.am	Sun Jan  6 15:15:11 2008
@@ -40,7 +40,7 @@
           && ${DOXYGEN} \
           && popd > /dev/null ; \
 	done
-	${top_srcdir}/libsofia-sip-ua/docs/hide_emails.sh docs/html 
+	${top_srcdir}/scripts/hide_emails.sh docs/html
 
 PHONY = doxygen 
 

Modified: freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/Makefile.am
==============================================================================
--- freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/Makefile.am	(original)
+++ freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/Makefile.am	Sun Jan  6 15:15:11 2008
@@ -101,7 +101,7 @@
 	    docs/$$d.doxytags > docs/$$d.doxytags.tmp && \
 	  mv -f docs/$$d.doxytags.tmp docs/$$d.doxytags ; \
 	done 
-	${srcdir}/docs/hide_emails.sh docs/html
+	${top_srcdir}/scripts/hide_emails.sh docs/html
 
 if HAVE_LCOV
 include $(top_srcdir)/rules/lcov.am

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	Sun Jan  6 15:15:11 2008
@@ -202,11 +202,12 @@
 				  incoming_queue_t *queue, 
 				  unsigned timeout);
 
-su_inline
-nta_incoming_t *incoming_find(nta_agent_t const *agent, sip_t const *sip,
-			      sip_via_t const *v,
-			      nta_incoming_t **merge,
-			      nta_incoming_t **ack);
+static nta_incoming_t *incoming_find(nta_agent_t const *agent,
+				     sip_t const *sip,
+				     sip_via_t const *v,
+				     nta_incoming_t **merge,
+				     nta_incoming_t **ack,
+				     nta_incoming_t **cancel);
 static int incoming_reply(nta_incoming_t *irq, msg_t *msg, sip_t *sip);
 su_inline int incoming_recv(nta_incoming_t *irq, msg_t *msg, sip_t *sip,
 				tport_t *tport);
@@ -214,10 +215,11 @@
 			       tport_t *tport);
 su_inline int incoming_cancel(nta_incoming_t *irq, msg_t *msg, sip_t *sip,
 				  tport_t *tport);
-su_inline int incoming_merge(nta_incoming_t *irq, msg_t *msg, sip_t *sip,
-				 tport_t *tport);
+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 *);
-su_inline su_duration_t incoming_timer(nta_agent_t *, su_duration_t);
+static void incoming_timer(nta_agent_t *);
 
 static nta_reliable_t *reliable_mreply(nta_incoming_t *,
 				       nta_prack_f *, nta_reliable_magic_t *,
@@ -225,7 +227,8 @@
 static int reliable_send(nta_incoming_t *, nta_reliable_t *, msg_t *, sip_t *);
 static int reliable_final(nta_incoming_t *irq, msg_t *msg, sip_t *sip);
 static msg_t *reliable_response(nta_incoming_t *irq);
-static int reliable_recv(nta_incoming_t *, msg_t *, sip_t *, tport_t *);
+static nta_reliable_t *reliable_find(nta_agent_t const *, sip_t const *);
+static int reliable_recv(nta_reliable_t *rel, msg_t *, sip_t *, tport_t *);
 static void reliable_flush(nta_incoming_t *irq);
 static void reliable_timeout(nta_incoming_t *irq, int timeout);
 
@@ -251,7 +254,7 @@
 				     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 *);
-su_inline su_duration_t outgoing_timer(nta_agent_t *, su_duration_t);
+static void outgoing_timer(nta_agent_t *);
 static int outgoing_recv_reliable(nta_outgoing_t *orq, msg_t *msg, sip_t *sip);
 
 /* Internal message passing */
@@ -725,9 +728,6 @@
   return -(agent->sa_timer == NULL);
 }
 
-#define NEXT_TIMEOUT(next, p, f, now) \
-  (p && p->f - (next) < 0 ? (p->f - (now) > 0 ? p->f : (now)) : (next))
-
 /**
  * Agent timer routine.
  */
@@ -739,20 +739,43 @@
 
   now += now == 0;
 
+  agent->sa_next = 0;
+
   agent->sa_now = stamp;
   agent->sa_millisec = now;
-  agent->sa_next = 0;
   agent->sa_in_timer = 1;
 
-  next = now + SU_DURATION_MAX;
-  next = outgoing_timer(agent, next);
-  next = incoming_timer(agent, next);
+  outgoing_timer(agent);
+  incoming_timer(agent);
 
+  /* agent->sa_now is used only if sa_millisec != 0 */
   agent->sa_millisec = 0;
   agent->sa_in_timer = 0;
 
+  /* Calculate next timeout */
+  next = now + SU_DURATION_MAX;
+
+#define NEXT_TIMEOUT(next, p, f, now) \
+  (void)(p && p->f - (next) < 0 && ((next) = (p->f - (now) > 0 ? p->f : (now))))
+
+  NEXT_TIMEOUT(next, agent->sa_out.re_list, orq_retry, now);
+  NEXT_TIMEOUT(next, agent->sa_out.inv_completed->q_head, orq_timeout, now);
+  NEXT_TIMEOUT(next, agent->sa_out.completed->q_head, orq_timeout, now);
+  NEXT_TIMEOUT(next, agent->sa_out.inv_calling->q_head, orq_timeout, now);
+  if (agent->sa_out.inv_proceeding->q_timeout)
+    NEXT_TIMEOUT(next, agent->sa_out.inv_proceeding->q_head, orq_timeout, now);
+  NEXT_TIMEOUT(next, agent->sa_out.trying->q_head, orq_timeout, now);
+
+  NEXT_TIMEOUT(next, agent->sa_in.preliminary->q_head, irq_timeout, now);
+  NEXT_TIMEOUT(next, agent->sa_in.inv_completed->q_head, irq_timeout, now);
+  NEXT_TIMEOUT(next, agent->sa_in.inv_confirmed->q_head, irq_timeout, now);
+  NEXT_TIMEOUT(next, agent->sa_in.completed->q_head, irq_timeout, now);
+  NEXT_TIMEOUT(next, agent->sa_in.re_list, irq_retry, now);
+
   if (agent->sa_next)
-    next = NEXT_TIMEOUT(next, agent, sa_next, now);
+    NEXT_TIMEOUT(next, agent, sa_next, now);
+
+#undef NEXT_TIMEOUT
 
   if (next == now + SU_DURATION_MAX) {
     /* Do not set timer */
@@ -2199,7 +2222,7 @@
 			tport_t *tport)
 {
   nta_leg_t *leg;
-  nta_incoming_t *irq, *merge = NULL, *ack = NULL;
+  nta_incoming_t *irq, *merge = NULL, *ack = NULL, *cancel = NULL;
   sip_method_t method = sip->sip_request->rq_method;
   char const *method_name = sip->sip_request->rq_method_name;
   url_t url[1];
@@ -2337,7 +2360,15 @@
   }
 
   /* First, try existing incoming requests */
-  irq = incoming_find(agent, sip, sip->sip_via, &merge, &ack);
+  irq = incoming_find(agent, sip, sip->sip_via, 
+		      agent->sa_merge_482 &&
+		      !sip->sip_to->a_tag &&
+		      method != sip_method_ack
+		      ? &merge
+		      : NULL,
+		      method == sip_method_ack ? &ack : NULL,
+		      method == sip_method_cancel ? &cancel : NULL);
+
   if (irq) {
     /* Match - this is a retransmission */
     SU_DEBUG_5(("nta: %s (%u) going to existing %s transaction\n",
@@ -2346,31 +2377,36 @@
       return;
   }
   else if (ack) {
-    /* Match - this is an ACK or CANCEL or PRACK */
     SU_DEBUG_5(("nta: %s (%u) is going to %s (%u)\n",
 		method_name, cseq,
-		ack->irq_rq->rq_method_name, ack->irq_cseq->cs_seq));
-    if (method == sip_method_ack) {
-      if (incoming_ack(ack, msg, sip, tport) >= 0)
-	return;
-    }
-    else if (method == sip_method_cancel) {
-      if (incoming_cancel(ack, msg, sip, tport) >= 0)
-	return;
-    }
-    else if (method == sip_method_prack) {
-      if (reliable_recv(ack, msg, sip, tport) >= 0)
-	return;
-    }
-    else {
-      assert(!method);
-    }
+		ack->irq_cseq->cs_method_name, ack->irq_cseq->cs_seq));
+    if (incoming_ack(ack, msg, sip, tport) >= 0)
+      return;
+  }
+  else if (cancel) {
+    SU_DEBUG_5(("nta: %s (%u) is going to %s (%u)\n",
+		method_name, cseq,
+		cancel->irq_cseq->cs_method_name, cancel->irq_cseq->cs_seq));
+    if (incoming_cancel(cancel, msg, sip, tport) >= 0)
+      return;
   }
   else if (merge) {
     SU_DEBUG_5(("nta: %s (%u) %s\n",
 		method_name, cseq, "is a merged request"));
-    if (incoming_merge(merge, msg, sip, tport) >= 0)
+    request_merge(agent, msg, sip, tport, merge->irq_tag);
+    return;
+  }
+
+  if (method == sip_method_prack && sip->sip_rack) {
+    nta_reliable_t *rel = reliable_find(agent, sip);
+    if (rel) {
+      SU_DEBUG_5(("nta: %s (%u) is going to %s (%u)\n",
+		  method_name, cseq,
+		  rel->rel_irq->irq_cseq->cs_method_name, 
+		  rel->rel_irq->irq_cseq->cs_seq));
+      reliable_recv(rel, msg, sip, tport);
       return;
+    }
   }
 
   *url = *sip->sip_request->rq_url;
@@ -5176,48 +5212,41 @@
 				  sip_via_t const *v)
 {
   if (agent && sip && v)
-    return incoming_find(agent, sip, v, NULL, NULL);
+    return incoming_find(agent, sip, v, NULL, NULL, NULL);
   else
     return NULL;
 }
 
-su_inline
-int addr_match(sip_addr_t const *a, char const *a_tag, sip_addr_t const *b)
-{
-  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
-      str0casecmp(a->a_host, b->a_host) == 0 &&
-      str0cmp(a->a_user, b->a_user) == 0;
-}
-
 /** Find a matching server transaction object.
  *
- *
+ * Check also for requests to merge, to ACK, or to CANCEL.
  */
-su_inline
-nta_incoming_t *incoming_find(nta_agent_t const *agent,
-			      sip_t const *sip,
-			      sip_via_t const *v,
-			      nta_incoming_t **return_merge,
-			      nta_incoming_t **return_ack)
+static nta_incoming_t *incoming_find(nta_agent_t const *agent,
+				     sip_t const *sip,
+				     sip_via_t const *v,
+				     nta_incoming_t **return_merge,
+				     nta_incoming_t **return_ack,
+				     nta_incoming_t **return_cancel)
 {
   sip_cseq_t const *cseq = sip->sip_cseq;
   sip_call_id_t const *i = sip->sip_call_id;
   sip_to_t const *to = sip->sip_to;
   sip_from_t const *from = sip->sip_from;
   sip_request_t *rq = sip->sip_request;
-  int is_uas_ack = return_ack && 
-    agent->sa_is_a_uas && rq->rq_method == sip_method_ack;
   incoming_htable_t const *iht = agent->sa_incoming;
   hash_value_t hash = NTA_HASH(i, cseq->cs_seq);
+  char const *magic_branch;
+
+  nta_incoming_t **ii, *irq;
 
-  nta_incoming_t **ii, *irq, *maybe;
+  int is_uas_ack = return_ack && agent->sa_is_a_uas;
 
-  for (ii = incoming_htable_hash(iht, hash), maybe = NULL;
+  if (v->v_branch && strncasecmp(v->v_branch, "z9hG4bK", 7) == 0)
+    magic_branch = v->v_branch + 7;
+  else
+    magic_branch = NULL;
+
+  for (ii = incoming_htable_hash(iht, hash);
        (irq = *ii);
        ii = incoming_htable_next(iht, ii)) {
     if (hash != irq->irq_hash ||
@@ -5229,96 +5258,84 @@
     if (str0casecmp(irq->irq_from->a_tag, from->a_tag))
       continue;
 
-    if (str0casecmp(irq->irq_via->v_branch, v->v_branch) != 0 ||
-	strcasecmp(irq->irq_via->v_host, v->v_host) != 0) {
-      if (!agent->sa_is_a_uas)
-	continue;
-      
-      if (is_uas_ack &&
-	  irq->irq_method == sip_method_invite && 
-	  200 <= irq->irq_status && irq->irq_status < 300 &&
-	  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 &&
-	       irq->irq_cseq->cs_method == cseq->cs_method &&
-	       (irq->irq_cseq->cs_method != sip_method_unknown ||
-		strcmp(irq->irq_cseq->cs_method_name, 
-		       cseq->cs_method_name) == 0)) {
-	*return_merge = irq;
-	continue;
-      }
-      else
-	continue;
-    }
-
-    if (is_uas_ack) {
-      if (!addr_match(irq->irq_to, irq->irq_tag, to))
-	continue;
-    }
-    else if (irq->irq_tag_set || !irq->irq_tag) {
-      if (str0casecmp(irq->irq_to->a_host, to->a_host) != 0 ||
-	  str0cmp(irq->irq_to->a_user, to->a_user) != 0)
-	continue;
+    if (is_uas_ack &&
+	irq->irq_method == sip_method_invite && 
+	200 <= irq->irq_status && irq->irq_status < 300 &&
+	str0casecmp(irq->irq_tag, to->a_tag) == 0) {
+      *return_ack = irq;
+      return NULL;		
     }
-    else if (str0casecmp(irq->irq_to->a_tag, to->a_tag))
-      continue;
-
-    if (!is_uas_ack && url_cmp(irq->irq_rq->rq_url, rq->rq_url))
-      continue;
-
-#if 0
-    if (irq->irq_terminated)
-      continue;
-#endif
 
-    if (irq->irq_method == rq->rq_method)
-      break;		/* found */
-
-    if (!return_ack)
-      continue;
+    if (magic_branch) {
+      /* RFC3261 17.2.3: 
+       *
+       * The request matches a transaction if branch and sent-by in topmost
+       * the method of the request matches the one that created the
+       * transaction, except for ACK, where the method of the request
+       * that created the transaction is INVITE.
+       */
 
-    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;
+      if (irq->irq_via->v_branch &&
+	  strcasecmp(irq->irq_via->v_branch + 7, magic_branch) == 0 &&
+	  strcasecmp(irq->irq_via->v_host, v->v_host) == 0 &&
+	  str0cmp(irq->irq_via->v_port, v->v_port) == 0) {
+	if (irq->irq_method == cseq->cs_method && 
+	    strcmp(irq->irq_cseq->cs_method_name, 
+		   cseq->cs_method_name) == 0)
+	  return irq;
+	if (return_ack && irq->irq_method == sip_method_invite)
+	  return *return_ack = irq, NULL;
+	if (return_cancel && irq->irq_method != sip_method_ack)
+	  return *return_cancel = irq, NULL;
+      }
     }
-    else if (rq->rq_method == sip_method_cancel && !irq->irq_terminated)
-      *return_ack = irq;
-  }
-
-  if (irq)
-    return irq;
+    else {
+      /* No magic branch */
 
-  /* Check PRACKed requests */
-  if (return_ack && rq->rq_method == sip_method_prack && sip->sip_rack) {
-    sip_rack_t const *rack = sip->sip_rack;
-    hash = NTA_HASH(i, rack->ra_cseq);
-
-    for (ii = incoming_htable_hash(iht, hash);
-	 (irq = *ii);
-	 ii = incoming_htable_next(iht, ii)) {
-      if (hash != irq->irq_hash)
-	continue;
-      if (irq->irq_call_id->i_hash != i->i_hash)
-	continue;
-      if (strcmp(irq->irq_call_id->i_id, i->i_id))
-	continue;
-      if (irq->irq_cseq->cs_seq != rack->ra_cseq)
-	continue;
-      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;
-      *return_ack = irq;
+      /* INVITE request matches a transaction if 
+	 the Request-URI, To tag, From tag, Call-ID, CSeq, and 
+	 top Via header match */
+
+      /* From tag, Call-ID, and CSeq number has been matched above */
+
+      /* Match To tag  */
+      if (str0casecmp(irq->irq_to->a_tag, to->a_tag) &&
+	  /* Ignore failing match if tag has been set */
+	  /* and retransmitted request had no to tag */
+	  !(irq->irq_tag_set && to->a_tag == NULL))
+	;
+      /* Match top Via header field */
+      else if (str0casecmp(irq->irq_via->v_branch, v->v_branch) == 0 &&
+	  strcasecmp(irq->irq_via->v_host, v->v_host) == 0 &&
+	  str0cmp(irq->irq_via->v_port, v->v_port) == 0)
+	;
+      /* Match Request-URI */
+      else if (url_cmp(irq->irq_rq->rq_url, rq->rq_url))
+	;
+      else {
+	/* Match CSeq */
+	if (irq->irq_method == cseq->cs_method &&
+	    strcmp(irq->irq_cseq->cs_method_name, 
+		   cseq->cs_method_name) == 0)
+	  return irq;		/* found */
+
+	if (return_ack && irq->irq_method == sip_method_invite)
+	  *return_ack = irq;
+	else if (return_cancel && irq->irq_method != sip_method_ack)
+	  *return_cancel = irq;
+      }
+    }    
 
-      return NULL;
+    /* RFC3261 - section 8.2.2.2 Merged Requests */
+    if (return_merge) {
+      if (irq->irq_cseq->cs_method == cseq->cs_method && 
+	  strcmp(irq->irq_cseq->cs_method_name, 
+		 cseq->cs_method_name) == 0)
+	*return_merge = irq, return_merge = NULL;
     }
   }
 
-  return irq;
+  return NULL;
 }
 
 /** Process retransmitted requests. */
@@ -5397,23 +5414,26 @@
   return 0;
 }
 
+/** Respond to the CANCEL. */
 su_inline
 int incoming_cancel(nta_incoming_t *irq, msg_t *msg, sip_t *sip,
 		    tport_t *tport)
 {
   nta_agent_t *agent = irq->irq_agent;
 
-  /* Respond to the CANCEL */
-
-  if (200 <= irq->irq_status && irq->irq_status < 300) {
-    nta_msg_treply(agent, msg_ref_create(msg), SIP_481_NO_TRANSACTION, 
+  /* According to the spec, this INVITE has been destroyed */
+  if (irq->irq_method == sip_method_invite && 
+      200 <= irq->irq_status && irq->irq_status < 300) {
+    nta_msg_treply(agent, msg, SIP_481_NO_TRANSACTION, 
 		   NTATAG_TPORT(tport),
 		   TAG_END());
+    return 0;
   }
-  else
-    nta_msg_treply(agent, msg_ref_create(msg), SIP_200_OK, 
-		   NTATAG_TPORT(tport),
-		   TAG_END());
+
+  nta_msg_treply(agent, msg_ref_create(msg), SIP_200_OK, 
+		 NTATAG_TPORT(tport),
+		 SIPTAG_TO(irq->irq_to),
+		 TAG_END());
 
   /* We have already sent final response */
   if (irq->irq_completed || irq->irq_method != sip_method_invite) {
@@ -5436,29 +5456,28 @@
   return 0;
 }
 
-/** Process merged requests */
-su_inline
-int incoming_merge(nta_incoming_t *irq, msg_t *msg, sip_t *sip, tport_t *tport)
+/** Merge request */
+static
+void request_merge(nta_agent_t *agent, 
+		   msg_t *msg, sip_t *sip, tport_t *tport,
+		   char const *to_tag)
 {
-  nta_agent_t *agent = irq->irq_agent;
+  nta_incoming_t *irq;
 
   agent->sa_stats->as_merged_request++;
 
-  irq = incoming_create(irq->irq_agent, msg, sip, tport, irq->irq_tag);
+  irq = incoming_create(agent, msg, sip, tport, to_tag);
 
-  if (!irq) {
-    SU_DEBUG_3(("nta: incoming_merge(): cannot create transaction for %s\n",
+  if (irq) {
+    nta_incoming_treply(irq, 482, "Request merged", TAG_END());
+    nta_incoming_destroy(irq);
+  } else {
+    SU_DEBUG_3(("nta: request_merge(): cannot create transaction for %s\n",
 		sip->sip_request->rq_method_name));
     nta_msg_treply(agent, msg, 482, "Request merged", 
 		   NTATAG_TPORT(tport),
 		   TAG_END());
-    return 0;
   }
-
-  nta_incoming_treply(irq, 482, "Request merged", TAG_END());
-  nta_incoming_destroy(irq);
-
-  return 0;
 }
 
 /**@typedef nta_ack_cancel_f
@@ -6097,8 +6116,7 @@
 };
 
 /** @internal Timer routine for the incoming request. */
-su_inline
-su_duration_t incoming_timer(nta_agent_t *sa, su_duration_t next)
+static void incoming_timer(nta_agent_t *sa)
 {
   su_duration_t now = sa->sa_millisec;
   nta_incoming_t *irq, *irq_next;
@@ -6166,8 +6184,6 @@
     }
   }
 
-  next = NEXT_TIMEOUT(next, irq, irq_retry, now);
-
   while ((irq = sa->sa_in.final_failed->q_head)) {
     incoming_remove(irq);
     irq->irq_final_failed = 0;
@@ -6215,8 +6231,6 @@
     reliable_timeout(irq, 1);
   }
 
-  next = NEXT_TIMEOUT(next, irq, irq_timeout, now);
-
   while ((irq = sa->sa_in.inv_completed->q_head)) {
     assert(irq->irq_status >= 200);
     assert(irq->irq_timeout);
@@ -6245,8 +6259,6 @@
     }
   } 
 
-  next = NEXT_TIMEOUT(next, irq, irq_timeout, now);
-
   while ((irq = sa->sa_in.inv_confirmed->q_head)) {
     assert(irq->irq_timeout);
     assert(irq->irq_status >= 200);
@@ -6268,8 +6280,6 @@
       incoming_free_queue(rq, irq);
   }
 
-  next = NEXT_TIMEOUT(next, irq, irq_timeout, now);
-
   while ((irq = sa->sa_in.completed->q_head)) {
     assert(irq->irq_status >= 200);
     assert(irq->irq_timeout);
@@ -6292,8 +6302,6 @@
       incoming_free_queue(rq, irq);
   }
 
-  next = NEXT_TIMEOUT(next, irq, irq_timeout, now);
-
   for (irq = sa->sa_in.terminated->q_head; irq; irq = irq_next) {
     irq_next = irq->irq_next;
     if (irq->irq_destroyed)
@@ -6312,8 +6320,6 @@
 		timeout, unconfirmed,
 		terminated, unterminated, 
 		destroyed, total));
-
-  return next;
 }
 
 /** Mass destroy server transactions */
@@ -7864,8 +7870,7 @@
 /** @internal Outgoing transaction timer routine. 
  *
  */
-su_inline 
-su_duration_t outgoing_timer(nta_agent_t *sa, su_duration_t next)
+static void outgoing_timer(nta_agent_t *sa)
 {
   su_duration_t now = sa->sa_millisec;
   nta_outgoing_t *orq;
@@ -7877,7 +7882,6 @@
     sa->sa_out.inv_calling->q_length;
   size_t completed = sa->sa_out.completed->q_length + 
     sa->sa_out.inv_completed->q_length;
-  outgoing_queue_t *proceeding = sa->sa_out.inv_proceeding;
 
   outgoing_queue_init(sa->sa_out.free = rq, 0);
 
@@ -7920,25 +7924,15 @@
       su_root_yield(sa->sa_root);	/* Handle received packets */
   }
 
-  next = NEXT_TIMEOUT(next, orq, orq_retry, now);
-
   terminated
     = outgoing_timer_dk(sa->sa_out.inv_completed, "D", now)
     + outgoing_timer_dk(sa->sa_out.completed, "K", now);
 
-  next = NEXT_TIMEOUT(next, sa->sa_out.inv_completed->q_head, orq_timeout, now);
-  next = NEXT_TIMEOUT(next, sa->sa_out.completed->q_head, orq_timeout, now);
-
   timeout
     = outgoing_timer_bf(sa->sa_out.inv_calling, "B", now)
-    + outgoing_timer_c(proceeding, "C", now)
+    + outgoing_timer_c(sa->sa_out.inv_proceeding, "C", now)
     + outgoing_timer_bf(sa->sa_out.trying, "F", now);
 
-  next = NEXT_TIMEOUT(next, sa->sa_out.inv_calling->q_head, orq_timeout, now);
-  if (proceeding->q_timeout)
-    next = NEXT_TIMEOUT(next, proceeding->q_head, orq_timeout, now);
-  next = NEXT_TIMEOUT(next, sa->sa_out.trying->q_head, orq_timeout, now);
-
   destroyed = outgoing_mass_destroy(sa, rq);
 
   sa->sa_out.free = NULL;
@@ -7954,8 +7948,6 @@
 		terminated, completed, 
 		destroyed, total));
   }
-
-  return next;
 }
 
 /** @internal Retransmit the outgoing request. */
@@ -8246,13 +8238,8 @@
       continue;
     if (str0casecmp(orq->orq_from->a_tag, sip->sip_from->a_tag))
       continue;
-    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 */
-	orq->orq_to->a_tag && !sip->sip_to->a_tag :
-	/* Don't (with ACK) if request/response tag mismatch */
-	!orq->orq_to->a_tag != !sip->sip_to->a_tag)
+    if (orq->orq_to->a_tag &&
+	str0casecmp(orq->orq_to->a_tag, sip->sip_to->a_tag))
       continue;
 
     if (orq->orq_method == sip_method_ack) {
@@ -9851,7 +9838,7 @@
   nta_agent_t *agent;
 
   agent = irq->irq_agent;
-
+  
   if (callback == NULL)
     callback = nta_reliable_destroyed;
 
@@ -9976,24 +9963,54 @@
   return rel->rel_unsent;
 }
 
+/* Find un-PRACKed responses */
+static
+nta_reliable_t *reliable_find(nta_agent_t const *agent,
+			      sip_t const *sip)
+{
+  incoming_htable_t const *iht = agent->sa_incoming;
+  nta_incoming_t *irq, **ii;
+  sip_call_id_t const *i = sip->sip_call_id;
+  sip_rack_t const *rack = sip->sip_rack;
+  hash_value_t hash = NTA_HASH(i, rack->ra_cseq);
+
+  /* XXX - add own hash table for 100rel */
+
+  for (ii = incoming_htable_hash(iht, hash);
+       (irq = *ii);
+       ii = incoming_htable_next(iht, ii)) {
+
+    if (hash == irq->irq_hash &&
+	irq->irq_call_id->i_hash == i->i_hash &&
+	irq->irq_cseq->cs_seq == rack->ra_cseq &&
+	irq->irq_method == sip_method_invite &&
+	strcmp(irq->irq_call_id->i_id, i->i_id) == 0 &&
+	(irq->irq_to->a_tag == NULL ||
+	 str0casecmp(irq->irq_to->a_tag, sip->sip_to->a_tag) == 0) &&
+	str0casecmp(irq->irq_from->a_tag, sip->sip_from->a_tag) == 0) {
+
+      nta_reliable_t const *rel;
+
+      /* Found matching INVITE */
+      for (rel = irq->irq_reliable; rel; rel = rel->rel_next)
+	if (rel->rel_rseq == rack->ra_response) 
+	  return (nta_reliable_t  *)rel;
+
+      return NULL;
+    }
+  }
+
+  return NULL;
+}
+
 /** Process incoming PRACK with matching @RAck field */
 static
-int reliable_recv(nta_incoming_t *irq, msg_t *msg, sip_t *sip, tport_t *tp)
+int reliable_recv(nta_reliable_t *rel, msg_t *msg, sip_t *sip, tport_t *tp)
 {
-  sip_rack_t *rack = sip->sip_rack;
-  nta_reliable_t *rel;
+  nta_incoming_t *irq = rel->rel_irq;
   nta_incoming_t *pr_irq;
   int status;
 
-  for (rel = irq->irq_reliable; rel; rel = rel->rel_next)
-    if (rel->rel_pracked)
-      return -1;
-    else if (rel->rel_rseq == rack->ra_response)
-      break;
-
-  if (!rel)
-    return -1;			/* Process normally */
-
   rel->rel_pracked = 1;
   msg_ref_destroy(rel->rel_unsent), rel->rel_unsent = NULL;
 

Modified: freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nta/test_nta.c
==============================================================================
--- freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nta/test_nta.c	(original)
+++ freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nta/test_nta.c	Sun Jan  6 15:15:11 2008
@@ -176,10 +176,13 @@
 
   msg_t          *ag_probe_msg;
 
+  su_sockaddr_t   ag_su_nta[1];
+  socklen_t       ag_su_nta_len;
 
   /* Dummy servers */
   char const     *ag_sink_port;
   su_socket_t     ag_sink_socket, ag_down_socket;
+  su_wait_t       ag_sink_wait[1];
 };
 
 static int test_init(agent_t *ag, char const *resolv_conf);
@@ -558,8 +561,11 @@
   TEST_1(bind(s, &su.su_sa, sulen) < 0 ? (perror("bind"), 0) : 1);
   TEST_1(getsockname(s, &su.su_sa, &sulen) == 0);
 
-  ag->ag_sink_port = su_sprintf(ag->ag_home, "%u", ntohs(su.su_sin.sin_port));
   ag->ag_sink_socket = s;
+  su_wait_init(ag->ag_sink_wait);
+  su_wait_create(ag->ag_sink_wait, ag->ag_sink_socket, SU_WAIT_IN);
+
+  ag->ag_sink_port = su_sprintf(ag->ag_home, "%u", ntohs(su.su_sin.sin_port));
 
   /* Down server */
   s = su_socket(af, SOCK_STREAM, 0); TEST_1(s != INVALID_SOCKET);
@@ -591,13 +597,38 @@
     sip_to_t to[1];
     sip_contact_t m[1];
 
+    su_sockaddr_t *su = ag->ag_su_nta;
+
     sip_from_init(from);
     sip_to_init(to);
     sip_contact_init(m);
 
+
     TEST_1(ag->ag_contact = nta_agent_contact(ag->ag_agent));
 
     *m->m_url = *ag->ag_contact->m_url;
+
+    if (host_is_ip4_address(m->m_url->url_host)) {
+      inet_pton(su->su_family = AF_INET,
+		m->m_url->url_host,
+		&su->su_sin.sin_addr);
+      ag->ag_su_nta_len = (sizeof su->su_sin);
+    }
+    else {
+      TEST_1(host_is_ip_address(m->m_url->url_host));
+      inet_pton(su->su_family = AF_INET6,
+		m->m_url->url_host,
+		&su->su_sin6.sin6_addr);
+      ag->ag_su_nta_len = (sizeof su->su_sin6);
+    }
+
+    su->su_port = htons(5060);
+    if (m->m_url->url_port && strlen(m->m_url->url_port)) {
+      unsigned long port = strtoul(m->m_url->url_port, NULL, 10);
+      su->su_port = htons(port);
+    }
+    TEST_1(su->su_port != 0);
+
     m->m_url->url_user = "bob";
     TEST_1(ag->ag_m_bob = sip_contact_dup(ag->ag_home, m));
 
@@ -651,11 +682,12 @@
 			       NTATAG_ALIASES(ag->ag_aliases),
 			       NTATAG_REL100(1),
 			       NTATAG_UA(1), 
+			       NTATAG_MERGE_482(1), 
 			       NTATAG_USE_NAPTR(1),
 			       NTATAG_USE_SRV(1),
 			       NTATAG_MAX_FORWARDS(20),
 			       TAG_END());
-    TEST(err, 6);
+    TEST(err, 7);
 
     err = nta_agent_set_params(ag->ag_agent, 
 			       NTATAG_ALIASES(ag->ag_aliases),
@@ -723,7 +755,11 @@
   nta_agent_destroy(ag->ag_agent);
   su_root_destroy(ag->ag_root);
 
-  su_free(ag->ag_home, (void *)ag->ag_sink_port), ag->ag_sink_port = NULL;
+  if (ag->ag_sink_port) {
+    su_free(ag->ag_home, (void *)ag->ag_sink_port), ag->ag_sink_port = NULL;
+    su_wait_destroy(ag->ag_sink_wait);
+    su_close(ag->ag_sink_socket);
+  }
 
   free(ag->ag_mclass), ag->ag_mclass = NULL;
 
@@ -1206,6 +1242,9 @@
     
     ctx->c_extra = &used_tport;
     
+    *url = *ag->ag_aliases->m_url;
+    url->url_user = "alice";
+
     TEST(tport_shutdown(tcp_tport, 1), 0); /* Not going to send anymore */
 
     TEST_1(pl = test_payload(ag->ag_home, 512));
@@ -2217,6 +2256,302 @@
   END();
 }
 
+/* Test merging  */
+
+int test_merging(agent_t *ag)
+{
+  BEGIN();
+
+  /*
+   * Test merging: send two messages with same 
+   * from tag/call-id/cseq number to nta,
+   * expect 200 and 408.
+   */
+
+  char const rfc3261prefix[] = "z9hG4bK";
+
+  char const template[] = 
+    "%s " URL_PRINT_FORMAT " SIP/2.0\r\n"
+    "Via: SIP/2.0/UDP 127.0.0.1:%s;branch=%s.%p\r\n"
+    "Via: SIP/2.0/TCP fake.address.for.via.example.net;branch=z9hG4bK.%p\r\n"
+    "CSeq: %u %s\r\n"
+    "Call-ID: dfsjfhsduifhsjfsfjkfsd.%p at dfsdhfsjkhsdjk\r\n"
+    "From: Evil Forker <sip:evel at forker.com>;tag=test_nta-%s\r\n"
+    "To: Bob the Builder <sip:bob at example.net>%s\r\n"
+    "Content-Length: 0\r\n"
+    "\r\n";
+
+  url_t u1[1], u2[2];
+
+  char m1[1024], m2[1024];
+  char r1[1024], r2[1024];
+
+  size_t len, l1, l2;
+  su_sockaddr_t *su = ag->ag_su_nta;
+  socklen_t sulen = ag->ag_su_nta_len;
+  int n;
+
+  /* Empty sink socket */
+  while (su_wait(ag->ag_sink_wait, 1, 0) == 0) 
+    su_recv(ag->ag_sink_socket, m1, sizeof m1, MSG_TRUNC);
+
+  {
+    /* RFC 3261 8.2.2.2 Merged Requests:
+
+   If the request has no tag in the To header field, the UAS core MUST
+   check the request against ongoing transactions.  If the From tag,
+   Call-ID, and CSeq exactly match those associated with an ongoing
+   transaction, but the request does not match that transaction (based
+   on the matching rules in Section 17.2.3), the UAS core SHOULD
+   generate a 482 (Loop Detected) response and pass it to the server
+   transaction.
+    */
+    nta_leg_bind(ag->ag_server_leg, leg_callback_200, ag);
+    ag->ag_expect_leg = ag->ag_server_leg;
+    ag->ag_latest_leg = NULL;
+
+    *u1 = *ag->ag_m_bob->m_url;
+    snprintf(m1, sizeof m1,
+	     template, 
+	     "MESSAGE", URL_PRINT_ARGS(u1),
+	     /* Via */ ag->ag_sink_port, rfc3261prefix, (void *)m1, 
+	     /* 2nd Via */ (void *)ag,
+	     /* CSeq */ 13, "MESSAGE",
+	     /* Call-ID */ (void *)ag,
+	     /* From tag */ "2.5.1",
+	     /* To tag */ "");
+    l1 = strlen(m1);
+
+    *u2 = *ag->ag_m_bob->m_url;
+
+    snprintf(m2, sizeof m2,
+	     template, 
+	     "MESSAGE", URL_PRINT_ARGS(u2),
+	     /* Via */ ag->ag_sink_port, rfc3261prefix, (void *)m2,
+	     /* 2nd Via */ (void *)ag,
+	     /* CSeq */ 13, "MESSAGE",
+	     /* Call-ID */ (void *)ag,
+	     /* From tag */ "2.5.1",
+	     /* To tag */ "");
+    l2 = strlen(m2);
+
+    TEST_1((size_t)su_sendto(ag->ag_sink_socket, m1, l1, 0, su, sulen) == l1);
+    TEST_1((size_t)su_sendto(ag->ag_sink_socket, m2, l2, 0, su, sulen) == l2);
+
+    for (n = 0; n < 2; ) {
+      su_root_step(ag->ag_root, 10L);
+      if (su_wait(ag->ag_sink_wait, 1, 0) == 0) {
+	if (n == 0)
+	  su_recv(ag->ag_sink_socket, r1, sizeof r1, MSG_TRUNC);
+	else
+	  su_recv(ag->ag_sink_socket, r2, sizeof r2, MSG_TRUNC);
+	n++;
+      }
+    }
+    len = strlen("SIP/2.0 200 ");
+    TEST_1(memcmp(r1, "SIP/2.0 200 ", len) == 0);
+    TEST_1(memcmp(r2, "SIP/2.0 482 ", len) == 0);
+
+    TEST_P(ag->ag_latest_leg, ag->ag_server_leg);
+  }
+
+  {
+    /*
+     * Check that request with same call-id, cseq and from-tag 
+     * are not merged if the method is different.
+     */
+    nta_leg_bind(ag->ag_server_leg, leg_callback_200, ag);
+    ag->ag_expect_leg = ag->ag_server_leg;
+    ag->ag_latest_leg = NULL;
+
+    *u1 = *ag->ag_m_bob->m_url;
+    snprintf(m1, sizeof m1,
+	     template, 
+	     "MESSAGE", URL_PRINT_ARGS(u1),
+	     /* Via */ ag->ag_sink_port, rfc3261prefix, (void *)m1, 
+	     /* 2nd Via */ (void *)ag,
+	     /* CSeq */ 14, "MESSAGE",
+	     /* Call-ID */ (void *)ag,
+	     /* From tag */ "2.5.2",
+	     /* To tag */ "");
+    l1 = strlen(m1);
+
+    *u2 = *ag->ag_m_bob->m_url;
+
+    snprintf(m2, sizeof m2,
+	     template, 
+	     "OPTIONS", URL_PRINT_ARGS(u2),
+	     /* Via */ ag->ag_sink_port, rfc3261prefix, (void *)m2,
+	     /* 2nd Via */ (void *)ag,
+	     /* CSeq */ 14, "OPTIONS",
+	     /* Call-ID */ (void *)ag,
+	     /* From tag */ "2.5.2",
+	     /* To tag */ "");
+    l2 = strlen(m2);
+
+    TEST_1((size_t)su_sendto(ag->ag_sink_socket, m1, l1, 0, su, sulen) == l1);
+    TEST_1((size_t)su_sendto(ag->ag_sink_socket, m2, l2, 0, su, sulen) == l2);
+
+    for (n = 0; n < 2; ) {
+      su_root_step(ag->ag_root, 10L);
+      if (su_wait(ag->ag_sink_wait, 1, 0) == 0) {
+	if (n == 0)
+	  su_recv(ag->ag_sink_socket, r1, sizeof r1, MSG_TRUNC);
+	else
+	  su_recv(ag->ag_sink_socket, r2, sizeof r2, MSG_TRUNC);
+	n++;
+      }
+    }
+
+    len = strlen("SIP/2.0 200 ");
+    TEST_1(memcmp(r1, "SIP/2.0 200 ", len) == 0);
+    TEST_1(memcmp(r2, "SIP/2.0 482 ", len) != 0);
+
+    TEST_P(ag->ag_latest_leg, ag->ag_server_leg);
+  }
+
+  {
+    /* test with rfc2543 */
+
+    snprintf(m1, sizeof m1,
+	     template, 
+	     "MASSAGE", URL_PRINT_ARGS(u1),
+	     /* Via */ ag->ag_sink_port, "0.", (void *)0, 
+	     /* 2nd Via */ (void *)ag,
+	     /* CSeq */ 14, "MASSAGE",
+	     /* Call-ID */ (void *)(ag + 1),
+	     /* From tag */ "2.5.3",
+	     /* To tag */ "");
+    l1 = strlen(m1);
+
+    u2->url_user = "bob+2";
+
+    snprintf(m2, sizeof m2,
+	     template, 
+	     "MASSAGE", URL_PRINT_ARGS(u2),
+	     /* Via */ ag->ag_sink_port, "0.", (void *)0,
+	     /* 2nd Via */ (void *)ag,
+	     /* CSeq */ 14, "MASSAGE",
+	     /* Call-ID */ (void *)(ag + 1),
+	     /* From tag */ "2.5.3",
+	     /* To tag */ "");
+    l2 = strlen(m2);
+
+    TEST_1((size_t)su_sendto(ag->ag_sink_socket, m1, l1, 0, su, sulen) == l1);
+    TEST_1((size_t)su_sendto(ag->ag_sink_socket, m2, l2, 0, su, sulen) == l2);
+
+    for (n = 0; n < 2; ) {
+      su_root_step(ag->ag_root, 10L);
+      if (su_wait(ag->ag_sink_wait, 1, 0) == 0) {
+	if (n == 0)
+	  su_recv(ag->ag_sink_socket, r1, sizeof r1, MSG_TRUNC);
+	else
+	  su_recv(ag->ag_sink_socket, r2, sizeof r2, MSG_TRUNC);
+	n++;
+      }
+    }
+    l1 = strlen("SIP/2.0 200 ");
+    TEST_1(memcmp(r1, "SIP/2.0 200 ", l1) == 0);
+    TEST_1(memcmp(r2, "SIP/2.0 482 ", l1) == 0);
+
+    TEST_P(ag->ag_latest_leg, ag->ag_server_leg);
+  }
+
+  {
+    /* test with to-tag */
+
+    snprintf(m1, sizeof m1,
+	     template, 
+	     "MESSAGE", URL_PRINT_ARGS(u1),
+	     /* Via */ ag->ag_sink_port, rfc3261prefix, (void *)m1, 
+	     /* 2nd Via */ (void *)ag,
+	     /* CSeq */ 15, "MESSAGE",
+	     /* Call-ID */ (void *)(ag + 2),
+	     /* From tag */ "2.5.4",
+	     /* To tag */ ";tag=in-dialog");
+    l1 = strlen(m1);
+
+    u2->url_user = "bob+2";
+
+    snprintf(m2, sizeof m2,
+	     template, 
+	     "MESSAGE", URL_PRINT_ARGS(u2),
+	     /* Via */ ag->ag_sink_port, rfc3261prefix, (void *)m2,
+	     /* 2nd Via */ (void *)ag,
+	     /* CSeq */ 15, "MESSAGE",
+	     /* Call-ID */ (void *)(ag + 2),
+	     /* From tag */ "2.5.4",
+	     /* To tag */ ";tag=in-dialog");
+    l2 = strlen(m2);
+
+    TEST_1((size_t)su_sendto(ag->ag_sink_socket, m1, l1, 0, su, sulen) == l1);
+    TEST_1((size_t)su_sendto(ag->ag_sink_socket, m2, l2, 0, su, sulen) == l2);
+
+    for (n = 0; n < 2; ) {
+      su_root_step(ag->ag_root, 10L);
+      if (su_wait(ag->ag_sink_wait, 1, 0) == 0) {
+	if (n == 0)
+	  su_recv(ag->ag_sink_socket, r1, sizeof r1, MSG_TRUNC);
+	else
+	  su_recv(ag->ag_sink_socket, r2, sizeof r2, MSG_TRUNC);
+	n++;
+      }
+    }
+    l1 = strlen("SIP/2.0 200 ");
+    TEST_1(memcmp(r1, "SIP/2.0 200 ", l1) == 0);
+    TEST_1(memcmp(r2, "SIP/2.0 482 ", l1) != 0);
+
+    TEST_P(ag->ag_latest_leg, ag->ag_server_leg);
+  }
+
+  {
+    /* test with rfc2543 and to-tag */
+
+    snprintf(m1, sizeof m1,
+	     template, 
+	     "MESSAGE", URL_PRINT_ARGS(u1),
+	     /* Via */ ag->ag_sink_port, "0.", (void *)0, 
+	     /* 2nd Via */ (void *)ag,
+	     /* CSeq */ 15, "MESSAGE",
+	     /* Call-ID */ (void *)(ag + 2),
+	     /* From tag */ "2.5.5",
+	     /* To tag */ ";tag=in-dialog");
+    l1 = strlen(m1);
+
+    snprintf(m2, sizeof m2,
+	     template, 
+	     "MESSAGE", URL_PRINT_ARGS(u2),
+	     /* Via */ ag->ag_sink_port, "0.", (void *)0,
+	     /* 2nd Via */ (void *)ag,
+	     /* CSeq */ 15, "MESSAGE",
+	     /* Call-ID */ (void *)(ag + 2),
+	     /* From tag */ "2.5.5",
+	     /* To tag */ ";tag=in-dialog");
+    l2 = strlen(m2);
+
+    TEST_1((size_t)su_sendto(ag->ag_sink_socket, m1, l1, 0, su, sulen) == l1);
+    TEST_1((size_t)su_sendto(ag->ag_sink_socket, m2, l2, 0, su, sulen) == l2);
+
+    for (n = 0; n < 2; ) {
+      su_root_step(ag->ag_root, 10L);
+      if (su_wait(ag->ag_sink_wait, 1, 0) == 0) {
+	if (n == 0)
+	  su_recv(ag->ag_sink_socket, r1, sizeof r1, MSG_TRUNC);
+	else
+	  su_recv(ag->ag_sink_socket, r2, sizeof r2, MSG_TRUNC);
+	n++;
+      }
+    }
+    l1 = strlen("SIP/2.0 200 ");
+    TEST_1(memcmp(r1, "SIP/2.0 200 ", l1) == 0);
+    TEST_1(memcmp(r2, "SIP/2.0 482 ", l1) != 0);
+
+    TEST_P(ag->ag_latest_leg, ag->ag_server_leg);
+  }
+
+  END();
+}
+
 /* ---------------------------------------------------------------------- */
 /* Test INVITE, dialogs */
 
@@ -3485,6 +3820,7 @@
   if (retval == 0) {
     retval |= test_bad_messages(ag); SINGLE_FAILURE_CHECK();
     retval |= test_reinit(ag); SINGLE_FAILURE_CHECK();
+    retval |= test_merging(ag); SINGLE_FAILURE_CHECK();
     retval |= test_tports(ag); SINGLE_FAILURE_CHECK();
     retval |= test_destroy_incoming(ag); SINGLE_FAILURE_CHECK();
     retval |= test_resolv(ag, argv[i]); SINGLE_FAILURE_CHECK();

Modified: freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nua/nua_notifier.c
==============================================================================
--- freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nua/nua_notifier.c	(original)
+++ freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nua/nua_notifier.c	Sun Jan  6 15:15:11 2008
@@ -498,10 +498,18 @@
       else if (nu->nu_requested >= now + expires)
 	nu->nu_expires = nu->nu_requested = now + expires;
     }
+    else {
+      if (nu->nu_requested >= nu->nu_expires)
+	nu->nu_expires = nu->nu_requested;
+    }
+
   }
   else {
     enum nua_substate substate = nu->nu_substate;
 
+    if (nu->nu_requested >= nu->nu_expires)
+      nu->nu_expires = nu->nu_requested;
+
     if (nu->nu_expires > now) {
       tagi_t const *t = tl_find_last(tags, nutag_substate);
       if (t)
@@ -794,8 +802,11 @@
       return 0;
   }
   else {
-    if (nua_client_create(nh, nua_r_notify, 
-			  &nua_notify_client_methods, NULL) >= 0)
+    if (nua_client_tcreate(nh, nua_r_notify, 
+			   &nua_notify_client_methods, 
+			   SIPTAG_EVENT(du->du_event),
+			   NUTAG_SUBSTATE(nua_substate_terminated),
+			   TAG_END()) >= 0)
       return 0;
   }
 

Modified: freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nua/nua_session.c
==============================================================================
--- freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nua/nua_session.c	(original)
+++ freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nua/nua_session.c	Sun Jan  6 15:15:11 2008
@@ -2023,7 +2023,8 @@
     reliable = 1, early_answer = 1;
   }
   else if (!nh->nh_soa || sr->sr_status >= 300) {
-    
+    if (sr->sr_neutral)
+      return nua_base_server_respond(sr, tags);
   }
   else if (tags && 100 < sr->sr_status && sr->sr_status < 200 && 
 	   !NHP_ISSET(nh->nh_prefs, early_answer)) {

Modified: freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nua/nua_subnotref.c
==============================================================================
--- freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nua/nua_subnotref.c	(original)
+++ freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nua/nua_subnotref.c	Sun Jan  6 15:15:11 2008
@@ -581,7 +581,7 @@
   sr->sr_usage = du;
   eu = nua_dialog_usage_private(du); assert(eu);
   eu->eu_notified++;
-  if (!o->o_id) 
+  if (!o || !o->o_id) 
     eu->eu_no_id = 1;
 
   if (subs == NULL) {

Modified: freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nua/outbound.c
==============================================================================
--- freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nua/outbound.c	(original)
+++ freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nua/outbound.c	Sun Jan  6 15:15:11 2008
@@ -503,13 +503,13 @@
   }
 
   if (!nat_detected) {
-    SU_DEBUG_1(("outbound(%p): detected NAT: %s != %s\n",
+    SU_DEBUG_5(("outbound(%p): detected NAT: %s != %s\n",
 		(void *)ob->ob_owner, v->v_host, received));
     if (ob->ob_oo && ob->ob_oo->oo_status)
       ob->ob_oo->oo_status(ob->ob_owner, ob, 101, "NAT detected", TAG_END());
   }
   else {
-    SU_DEBUG_1(("outbound(%p): NAT binding changed: "
+    SU_DEBUG_5(("outbound(%p): NAT binding changed: "
 		"[%s]:%s != [%s]:%s\n",
 		(void *)ob->ob_owner, nat_detected, nat_port, received, rport));
     if (ob->ob_oo && ob->ob_oo->oo_status)
@@ -860,8 +860,6 @@
     else
       loglevel = 3, failed = 1;
       
-    loglevel = 1;		/* XXX ... for now */
-
     if (loglevel >= SU_LOG->log_level) {
       sip_contact_t const *m = ob->ob_rcontact;
 
@@ -886,7 +884,7 @@
       ob->ob_oo->oo_probe_error(ob->ob_owner, ob, status, phrase, TAG_END());
   }
   else if (status == 408) {
-    SU_DEBUG_1(("outbound(%p): keepalive timeout\n", (void *)ob->ob_owner));
+    SU_DEBUG_3(("outbound(%p): keepalive timeout\n", (void *)ob->ob_owner));
     ob->ob_oo->oo_keepalive_error(ob->ob_owner, ob, status, phrase, TAG_END());
     return 0;
   }
@@ -979,7 +977,7 @@
     return 0;
 
   if (ob->ob_keepalive.validating) {
-    SU_DEBUG_1(("outbound(%p): registration check OPTIONS received\n", 
+    SU_DEBUG_5(("outbound(%p): registration check OPTIONS received\n", 
 		(void *)ob->ob_owner));
     ob->ob_keepalive.validated = 1;
   }

Modified: freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/stun/stun.c
==============================================================================
--- freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/stun/stun.c	(original)
+++ freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/stun/stun.c	Sun Jan  6 15:15:11 2008
@@ -1895,23 +1895,23 @@
   if ((req->sr_state == stun_req_timeout) && (req->sr_from_y == -1)) {
     SU_DEBUG_0(("%s: lifetime determination failed.\n", __func__));
     sd->sd_state = stun_discovery_timeout;
+    req->sr_state = stun_req_dispose_me;
 
     /* Use per discovery specific callback */
     if (sd->sd_callback)
       sd->sd_callback(sd->sd_magic, sh, sd, action, sd->sd_state);
 
-    req->sr_state = stun_req_dispose_me;
     return 0;
   }
 
   if (abs(sd->sd_lt_cur - sd->sd_lt) <= STUN_LIFETIME_CI) {
     sd->sd_state = stun_discovery_done;
+    req->sr_state = stun_req_dispose_me;
 
     /* Use per discovery specific callback */
     if (sd->sd_callback)
       sd->sd_callback(sd->sd_magic, sh, sd, action, sd->sd_state);
 
-    req->sr_state = stun_req_dispose_me;
     return 0;
   }
 
@@ -1998,12 +1998,11 @@
   memcpy(sd->sd_addr_seen_outside, sa, sizeof(su_sockaddr_t));
 
   sd->sd_state = stun_discovery_done;
+  req->sr_state = stun_req_dispose_me;
   
   if (sd->sd_callback)
     sd->sd_callback(sd->sd_magic, sh, sd, action, sd->sd_state);
 
-  req->sr_state = stun_req_dispose_me;
-
   return 0;
 }
 
@@ -2053,9 +2052,9 @@
 				     stun_request_t *req)
 {
   sd->sd_state = stun_discovery_done;
+  req->sr_state = stun_req_dispose_me;
   if (sd->sd_callback)
     sd->sd_callback(sd->sd_magic, sh, sd, action, sd->sd_state);
-  req->sr_state = stun_req_dispose_me;
 }
 
 /**
@@ -2290,12 +2289,12 @@
     switch (action) {
     case stun_action_binding_request:
       sd->sd_state = stun_discovery_timeout;
+      req->sr_state = stun_req_dispose_me;
 
       /* Use per discovery specific callback */
       if (sd->sd_callback)
 	sd->sd_callback(sd->sd_magic, sh, sd, action, sd->sd_state);
 
-      req->sr_state = stun_req_dispose_me;
       break;
 
     case stun_action_test_nattype:

Modified: freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/tport/tport_tag.c
==============================================================================
--- freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/tport/tport_tag.c	(original)
+++ freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/tport/tport_tag.c	Sun Jan  6 15:15:11 2008
@@ -311,6 +311,10 @@
  *
  * This is a parameter suitable for tuning.
  *
+ * On Linux systems, the default value for receive buffer is set with
+ * the sysctl "net.core.rmem_default", and the maximum value is set with
+ * the sysctl "net.core.rmem_max".
+ *
  * Use with tport_tbind(), nua_create(), nta_agent_create(),
  * nta_agent_add_tport(), nth_engine_create(), or initial nth_site_create().
  */
@@ -322,6 +326,10 @@
  *
  * This is a parameter suitable for tuning.
  *
+ * On Linux systems, the default value for receive buffer is set with
+ * the sysctl "net.core.wmem_default", and the maximum value is set with
+ * the sysctl "net.core.wmem_max".
+ *
  * Use with tport_tbind(), nua_create(), nta_agent_create(),
  * nta_agent_add_tport(), nth_engine_create(), or initial nth_site_create().
  */

Modified: freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/tport/tport_type_udp.c
==============================================================================
--- freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/tport/tport_type_udp.c	(original)
+++ freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/tport/tport_type_udp.c	Sun Jan  6 15:15:11 2008
@@ -35,6 +35,7 @@
 #include "config.h"
 
 #include "tport_internal.h"
+#include "sofia-sip/hostdomain.h"
 
 #if HAVE_IP_RECVERR || HAVE_IPV6_RECVERR
 #include <linux/types.h>
@@ -113,6 +114,8 @@
   unsigned rmem = 0, wmem = 0;
   int events = SU_WAIT_IN;
   int s;
+  su_sockaddr_t *su = (su_sockaddr_t *)ai->ai_addr;
+  int const one = 1; (void)one;
 
   s = su_socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
   if (s == INVALID_SOCKET)
@@ -125,20 +128,51 @@
 
   tport_set_tos(s, ai, pri->pri_params->tpp_tos);
 
+#if HAVE_IP_ADD_MEMBERSHIP
+  if (ai->ai_family == AF_INET && 
+      IN_MULTICAST(ntohl(su->su_sin.sin_addr.s_addr))) {
+    /* Try to join to the multicast group */
+    /* Bind to the SIP address like 
+       <sip:88.77.66.55:5060;maddr=224.0.1.75;transport=udp> */
+    struct ip_mreq imr[1];
+    struct in_addr iface;
+
+    memset(imr, 0, sizeof imr);
+      
+    imr->imr_multiaddr = su->su_sin.sin_addr;
+
+    if (host_is_ip4_address(tpn->tpn_canon) &&
+	inet_pton(AF_INET, tpn->tpn_canon, &iface) > 0) {
+      imr->imr_interface = iface;
+    }
+
+    if (setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, imr, (sizeof imr)) < 0) {
+      SU_DEBUG_3(("setsockopt(%s): %s\n",
+		  "IP_ADD_MEMBERSHIP", su_strerror(su_errno())));
+    }
+#if HAVE_IP_MULTICAST_LOOP
+    else 
+      if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_LOOP, &one, sizeof one) < 0) {
+	SU_DEBUG_3(("setsockopt(%s): %s\n",
+		    "IP_MULTICAST_LOOP", su_strerror(su_errno())));
+    }
+#endif
+  }
+#endif
+
 #if HAVE_IP_RECVERR
   if (ai->ai_family == AF_INET || ai->ai_family == AF_INET6) {
-    int const one = 1;
-    if (setsockopt(s, SOL_IP, IP_RECVERR, &one, sizeof(one)) < 0) {
+    if (setsockopt(s, IPPROTO_IP, IP_RECVERR, &one, sizeof(one)) < 0) {
       if (ai->ai_family == AF_INET)
-	SU_DEBUG_3(("setsockopt(IPVRECVERR): %s\n", su_strerror(su_errno())));
+	SU_DEBUG_3(("setsockopt(%s): %s\n",
+		    "IPVRECVERR", su_strerror(su_errno())));
     }
     events |= SU_WAIT_ERR;
   }
 #endif
 #if HAVE_IPV6_RECVERR
   if (ai->ai_family == AF_INET6) {
-    int const one = 1;
-    if (setsockopt(s, SOL_IPV6, IPV6_RECVERR, &one, sizeof(one)) < 0)
+    if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVERR, &one, sizeof(one)) < 0)
       SU_DEBUG_3(("setsockopt(IPV6_RECVERR): %s\n", su_strerror(su_errno())));
     events |= SU_WAIT_ERR;
   }
@@ -150,12 +184,18 @@
 	  TAG_END());
 
   if (rmem != 0 && 
+#if HAVE_SO_RCVBUFFORCE
+      setsockopt(s, SOL_SOCKET, SO_RCVBUFFORCE, (void *)&rmem, sizeof rmem) < 0 &&
+#endif
       setsockopt(s, SOL_SOCKET, SO_RCVBUF, (void *)&rmem, sizeof rmem) < 0) {
     SU_DEBUG_3(("setsockopt(SO_RCVBUF): %s\n", 
 		su_strerror(su_errno())));
   }
 
   if (wmem != 0 && 
+#if HAVE_SO_SNDBUFFORCE
+      setsockopt(s, SOL_SOCKET, SO_SNDBUFFORCE, (void *)&wmem, sizeof wmem) < 0 &&
+#endif
       setsockopt(s, SOL_SOCKET, SO_SNDBUF, (void *)&wmem, sizeof wmem) < 0) {
     SU_DEBUG_3(("setsockopt(SO_SNDBUF): %s\n", 
 		su_strerror(su_errno())));
@@ -409,10 +449,10 @@
   for (c = CMSG_FIRSTHDR(msg); c; c = CMSG_NXTHDR(msg, c)) {
     if (0
 #if HAVE_IP_RECVERR
-	|| (c->cmsg_level == SOL_IP && c->cmsg_type == IP_RECVERR)
+	|| (c->cmsg_level == IPPROTO_IP && c->cmsg_type == IP_RECVERR)
 #endif
 #if HAVE_IPV6_RECVERR
-	|| (c->cmsg_level == SOL_IPV6 && c->cmsg_type == IPV6_RECVERR)
+	|| (c->cmsg_level == IPPROTO_IPV6 && c->cmsg_type == IPV6_RECVERR)
 #endif
 	) {
       char info[128];

Modified: freeswitch/trunk/libs/sofia-sip/m4/sac-su2.m4
==============================================================================
--- freeswitch/trunk/libs/sofia-sip/m4/sac-su2.m4	(original)
+++ freeswitch/trunk/libs/sofia-sip/m4/sac-su2.m4	Sun Jan  6 15:15:11 2008
@@ -273,6 +273,28 @@
 #include <sys/types.h>
 #include <sys/socket.h>])
 
+AC_CHECK_DECL([SO_RCVBUFFORCE],
+AC_DEFINE([HAVE_SO_RCVBUFFORCE],1,[Define to 1 if you have socket option SO_RCVBUFFORCE]),,[
+#include <sys/types.h>
+#include <sys/socket.h>])
+
+AC_CHECK_DECL([SO_SNDBUFFORCE],
+AC_DEFINE([HAVE_SO_SNDBUFFORCE],1,[Define to 1 if you have socket option SO_SNDBUFFORCE]),,[
+#include <sys/types.h>
+#include <sys/socket.h>])
+
+AC_CHECK_DECL([IP_ADD_MEMBERSHIP],
+AC_DEFINE([HAVE_IP_ADD_MEMBERSHIP],1,[Define to 1 if you have IP_ADD_MEMBERSHIP]),,[
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>])
+
+AC_CHECK_DECL([IP_MULTICAST_LOOP],
+AC_DEFINE([HAVE_IP_MULTICAST_LOOP],1,[Define to 1 if you have IP_MULTICAST_LOOP]),,[
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>])
+
 AC_CACHE_CHECK([for struct addrinfo],
 [ac_cv_struct_addrinfo],[
 ac_cv_struct_addrinfo=no

Added: freeswitch/trunk/libs/sofia-sip/scripts/hide_emails.sh
==============================================================================
--- (empty file)
+++ freeswitch/trunk/libs/sofia-sip/scripts/hide_emails.sh	Sun Jan  6 15:15:11 2008
@@ -0,0 +1,41 @@
+#!/bin/sh
+#
+# description: Mangle email addresses in the HTML documentation
+# author: Kai Vehmanen <kai.vehmanen at nokia.com>
+# version: 20050908-3
+#
+# --------------------------------------------------------------------
+#
+# This file is part of the Sofia-SIP package
+#
+# Copyright (C) 2005 Nokia Corporation.
+#
+# Contact: Pekka Pessi <pekka.pessi at nokia.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public License
+# as published by the Free Software Foundation; either version 2.1 of
+# the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+# 02110-1301 USA
+#
+# --------------------------------------------------------------------
+
+echo "Postprocessing HTML in ${1:-.}: hiding email addresses, fixing links"
+
+find ${1:-.} -name '*.html' -print0 | 
+xargs -0 \
+sed -r -i '
+# Hide e-mail addresses
+s/([:>;][a-z][-a-z.]*)(@[a-z][a-z]*)\.[a-z][a-z]*(["<\&])/\1\2-email.address.hidden\3/gi;
+# Fix cross-module links
+s!doxygen="([a-z]*).doxytags:../\1/" href="../\1/\1_index.html"!doxygen="\1.doxytags:../\1/" href="../\1/index.html"!g;
+'

Modified: freeswitch/trunk/libs/sofia-sip/tests/check_sofia.c
==============================================================================
--- freeswitch/trunk/libs/sofia-sip/tests/check_sofia.c	(original)
+++ freeswitch/trunk/libs/sofia-sip/tests/check_sofia.c	Sun Jan  6 15:15:11 2008
@@ -51,7 +51,9 @@
   suite = suite_for_nua();
   runner = srunner_create(suite);
 
-  srunner_set_xml(runner, "/tmp/result.xml");
+  if (argv[1]) {
+    srunner_set_xml(runner, argv[1]);
+  }
   srunner_run_all(runner, CK_NORMAL);
 
   failed = srunner_ntests_failed(runner);



More information about the Freeswitch-svn mailing list