2
0
Эх сурвалжийг харах

Merge remote branch 'origin/hpw/branch_failure_route'

* origin/hpw/branch_failure_route:
  modules/tm: Fix t_next_contact_flow return value
  modules/tmx: Add $T_reply_ruid pv to return ruid for a branch reply
  modules/tm: Make ruid available in uac structure
  modules/registrar: Allow unregister for ruids in branch_failure_route
  modules/usrloc: Fix get_urecord_by_ruid to return static aor
  modules/tm: Fix setting instance in uac branches
  modules/tm: Enable retrieving of branch instance id from uac structure
  modules/xprint: Updated to use the new get_branch()/next_branch() functions
  modules/pv: Updated to use the new get_branch()/next_branch() functions
  modules/permissions: Updated to use the new get_branch()/next_branch() functions
  modules/domain: Updated to use the new get_branch()/next_branch() functions
  core: Update get_branch() to return instance from appended branches
  modules/tm: Update t_next_contact_flows for use in branch_failure event_route
  core: Add defines required for a new branch_failure_route type
  modules/tm: Create branch-failure event route
  Revert "core: Initial revision of branch_failure_route"
  core: Initial revision of branch_failure_route
  modules/tm: Initial revision of branch_failure_route
Hugh Waite 12 жил өмнө
parent
commit
2168999d8f

+ 13 - 5
dset.c

@@ -216,7 +216,7 @@ void set_branch_iterator(int n)
 char* get_branch(unsigned int i, int* len, qvalue_t* q, str* dst_uri,
 		 str* path, unsigned int *flags,
 		 struct socket_info** force_socket,
-		 str *ruid)
+		 str *ruid, str *instance)
 {
 	if (i < nr_branches) {
 		*len = branches[i].len;
@@ -237,6 +237,10 @@ char* get_branch(unsigned int i, int* len, qvalue_t* q, str* dst_uri,
 			ruid->len = branches[i].ruid_len;
 			ruid->s = (ruid->len)?branches[i].ruid:0;
 		}
+		if (instance) {
+			instance->len = branches[i].instance_len;
+			instance->s = (instance->len)?branches[i].instance:0;
+		}
 		return branches[i].uri;
 	} else {
 		*len = 0;
@@ -257,6 +261,10 @@ char* get_branch(unsigned int i, int* len, qvalue_t* q, str* dst_uri,
 			ruid->s = 0;
 			ruid->len = 0;
 		}
+		if (instance) {
+			instance->s = 0;
+			instance->len = 0;
+		}
 		return 0;
 	}
 }
@@ -268,12 +276,12 @@ char* get_branch(unsigned int i, int* len, qvalue_t* q, str* dst_uri,
  */
 char* next_branch(int* len, qvalue_t* q, str* dst_uri, str* path,
 		  unsigned int* flags, struct socket_info** force_socket,
-		  str* ruid)
+		  str* ruid, str *instance)
 {
 	char* ret;
 	
 	ret=get_branch(branch_iterator, len, q, dst_uri, path, flags,
-		       force_socket, ruid);
+		       force_socket, ruid, instance);
 	if (likely(ret))
 		branch_iterator++;
 	return ret;
@@ -442,7 +450,7 @@ char* print_dset(struct sip_msg* msg, int* len)
 	crt_branch = get_branch_iterator();
 
 	init_branch_iterator();
-	while ((uri.s = next_branch(&uri.len, &q, 0, 0, 0, 0, 0))) {
+	while ((uri.s = next_branch(&uri.len, &q, 0, 0, 0, 0, 0, 0))) {
 		cnt++;
 		*len += uri.len;
 		if (q != Q_UNSPECIFIED) {
@@ -483,7 +491,7 @@ char* print_dset(struct sip_msg* msg, int* len)
 	}
 
 	init_branch_iterator();
-	while ((uri.s = next_branch(&uri.len, &q, 0, 0, 0, 0, 0))) {
+	while ((uri.s = next_branch(&uri.len, &q, 0, 0, 0, 0, 0, 0))) {
 		if (i) {
 			memcpy(p, CONTACT_DELIM, CONTACT_DELIM_LEN);
 			p += CONTACT_DELIM_LEN;

+ 2 - 2
dset.h

@@ -140,12 +140,12 @@ void set_branch_iterator(int n);
  */
 char* next_branch(int* len, qvalue_t* q, str* dst_uri, str* path,
 		  unsigned int* flags, struct socket_info** force_socket,
-		  str *ruid);
+		  str *ruid, str *instance);
 
 char* get_branch( unsigned int i, int* len, qvalue_t* q, str* dst_uri,
 		  str* path, unsigned int *flags,
 		  struct socket_info** force_socket,
-		  str* ruid);
+		  str* ruid, str *instance);
 
 /*! \brief
  * Empty the array of branches

+ 1 - 1
modules/domain/domain.c

@@ -142,7 +142,7 @@ int is_uri_host_local(struct sip_msg* _msg, char* _s1, char* _s2)
 	}
 	return hash_table_lookup(&(_msg->parsed_uri.host), &did, &attrs);
     } else if (is_route_type(FAILURE_ROUTE)) {
-	branch.s = get_branch(0, &branch.len, &q, 0, 0, 0, 0, 0);
+	branch.s = get_branch(0, &branch.len, &q, 0, 0, 0, 0, 0, 0);
 	if (branch.s) {
 	    if (parse_uri(branch.s, branch.len, &puri) < 0) {
 		LM_ERR("error while parsing branch URI\n");

+ 1 - 1
modules/permissions/permissions.c

@@ -406,7 +406,7 @@ static int check_routing(struct sip_msg* msg, int idx)
 	}
 
 check_branches:
-	for( br_idx=0 ; (branch.s=get_branch(br_idx,&branch.len,&q,0,0,0,0,0))!=0 ;
+	for( br_idx=0 ; (branch.s=get_branch(br_idx,&branch.len,&q,0,0,0,0,0,0))!=0 ;
 			br_idx++ ) {
 		uri_str = get_plain_uri(&branch);
 		if (!uri_str) {

+ 1 - 1
modules/pv/pv_branch.c

@@ -49,7 +49,7 @@ int pv_get_branchx(struct sip_msg *msg, pv_param_t *param,
 		return pv_get_null(msg, param, res);
 	}
 
-	uri.s = get_branch(idx, &uri.len, &lq, &duri, &path, &fl, &fsocket, &ruid);
+	uri.s = get_branch(idx, &uri.len, &lq, &duri, &path, &fl, &fsocket, &ruid, 0);
 
 	/* branch(count) doesn't need a valid branch, everything else does */
 	if(uri.s == 0 && ( param->pvn.u.isname.name.n != 5/* count*/ ))

+ 3 - 3
modules/pv/pv_core.c

@@ -1358,7 +1358,7 @@ int pv_get_branch(struct sip_msg *msg, pv_param_t *param,
 		return pv_get_null(msg, param, res);
 
 
-	branch.s = get_branch(0, &branch.len, &q, 0, 0, 0, 0, 0);
+	branch.s = get_branch(0, &branch.len, &q, 0, 0, 0, 0, 0, 0);
 	if (!branch.s) {
 		return pv_get_null(msg, param, res);
 	}
@@ -1387,7 +1387,7 @@ int pv_get_branches(struct sip_msg *msg, pv_param_t *param,
   
 	cnt = s.len = 0;
 
-	while ((uri.s = get_branch(cnt, &uri.len, &q, 0, 0, 0, 0, 0)))
+	while ((uri.s = get_branch(cnt, &uri.len, &q, 0, 0, 0, 0, 0, 0)))
 	{
 		cnt++;
 		s.len += uri.len;
@@ -1411,7 +1411,7 @@ int pv_get_branches(struct sip_msg *msg, pv_param_t *param,
 	i = 0;
 	p = pv_local_buf;
 
-	while ((uri.s = get_branch(i, &uri.len, &q, 0, 0, 0, 0, 0)))
+	while ((uri.s = get_branch(i, &uri.len, &q, 0, 0, 0, 0, 0, 0)))
 	{
 		if (i)
 		{

+ 1 - 1
modules/registrar/reg_mod.c

@@ -188,7 +188,7 @@ static cmd_export_t cmds[] = {
 	{"unregister",   (cmd_function)w_unregister,  2,  unreg_fixup, 0,
 			REQUEST_ROUTE| FAILURE_ROUTE },
 	{"unregister",   (cmd_function)w_unregister2, 3, unreg_fixup, 0,
-			REQUEST_ROUTE| FAILURE_ROUTE },
+			REQUEST_ROUTE| FAILURE_ROUTE | BRANCH_FAILURE_ROUTE },
 	{"reg_fetch_contacts", (cmd_function)pv_fetch_contacts, 3, 
 			fetchc_fixup, 0,
 			REQUEST_ROUTE| FAILURE_ROUTE },

+ 0 - 1
modules/registrar/save.c

@@ -997,7 +997,6 @@ int unregister(struct sip_msg* _m, udomain_t* _d, str* _uri, str *_ruid)
 			LM_WARN("could not delete contact\n");
 			return -1;
 		}
-		ul.release_urecord(r);
 		ul.unlock_udomain(_d, &aor);
 	}
 

+ 2 - 0
modules/tm/h_table.h

@@ -229,6 +229,8 @@ typedef struct ua_client
 #endif
 	str uri;
 	str path;
+	str instance;
+	str ruid;
 	/* if we don't store, we at least want to know the status */
 	int             last_received;
 

+ 143 - 11
modules/tm/t_fwd.c

@@ -187,7 +187,8 @@ static int prepare_new_uac( struct cell *t, struct sip_msg *i_req,
 									str* next_hop,
 									struct socket_info* fsocket,
 									snd_flags_t snd_flags,
-									int fproto, int flags)
+									int fproto, int flags,
+									str *instance, str *ruid)
 {
 	char *shbuf;
 	struct lump* add_rm_backup, *body_lumps_backup;
@@ -200,6 +201,10 @@ static int prepare_new_uac( struct cell *t, struct sip_msg *i_req,
 	int dst_uri_backed_up;
 	str path_bak;
 	int free_path;
+	str instance_bak;
+	int free_instance;
+	str ruid_bak;
+	int free_ruid;
 	int backup_route_type;
 	snd_flags_t fwd_snd_flags_bak;
 	snd_flags_t rpl_snd_flags_bak;
@@ -219,6 +224,12 @@ static int prepare_new_uac( struct cell *t, struct sip_msg *i_req,
 	path_bak.s=0;
 	path_bak.len=0;
 	free_path=0;
+	instance_bak.s=0;
+	instance_bak.len=0;
+	free_instance=0;
+	ruid_bak.s=0;
+	ruid_bak.len=0;
+	free_ruid=0;
 	dst=&t->uac[branch].request.dst;
 
 	/* ... we calculate branch ... */	
@@ -259,6 +270,8 @@ static int prepare_new_uac( struct cell *t, struct sip_msg *i_req,
 	parsed_uri_bak=i_req->parsed_uri;
 	parsed_uri_ok_bak=i_req->parsed_uri_ok;
 	path_bak=i_req->path_vec;
+	instance_bak=i_req->instance;
+	ruid_bak=i_req->ruid;
 	
 	if (unlikely(branch_route || has_tran_tmcbs(t, TMCB_REQUEST_FWDED))){
 		/* dup uris, path a.s.o. if we have a branch route or callback */
@@ -292,6 +305,37 @@ static int prepare_new_uac( struct cell *t, struct sip_msg *i_req,
 			}
 			free_path=1;
 		}
+		/* update instance */
+		/* if instance points to msg instance, it needs to be "fixed" so that we 
+		   can change/update msg->instance */
+		if (instance==&i_req->instance)
+			instance=&instance_bak;
+		/* zero it first so that set_instance will work */
+		i_req->instance.s=0;
+		i_req->instance.len=0;
+		if (unlikely(instance)){
+			if (unlikely(set_instance(i_req, instance)<0)){
+				ret=E_OUT_OF_MEM;
+				goto error03;
+			}
+			free_instance=1;
+		}
+
+		/* update ruid */
+		/* if ruid points to msg ruid, it needs to be "fixed" so that we 
+		   can change/update msg->ruid */
+		if (ruid==&i_req->ruid)
+			ruid=&ruid_bak;
+		/* zero it first so that set_ruid will work */
+		i_req->ruid.s=0;
+		i_req->ruid.len=0;
+		if (unlikely(ruid)){
+			if (unlikely(set_ruid(i_req, ruid)<0)){
+				ret=E_OUT_OF_MEM;
+				goto error03;
+			}
+			free_ruid=1;
+		}
 	
 		/* backup dst uri  & zero it*/
 		dst_uri_bak=i_req->dst_uri;
@@ -397,6 +441,20 @@ static int prepare_new_uac( struct cell *t, struct sip_msg *i_req,
 			i_req->path_vec.s=0;
 			i_req->path_vec.len=0;
 		}
+		if (unlikely(instance && (i_req->instance.s!=instance->s ||
+							  i_req->instance.len!=instance->len))){
+			i_req->instance=*instance;
+		}else if (unlikely(instance==0 && i_req->instance.len!=0)){
+			i_req->instance.s=0;
+			i_req->instance.len=0;
+		}
+		if (unlikely(ruid && (i_req->ruid.s!=ruid->s ||
+							  i_req->ruid.len!=ruid->len))){
+			i_req->ruid=*ruid;
+		}else if (unlikely(ruid==0 && i_req->ruid.len!=0)){
+			i_req->ruid.s=0;
+			i_req->ruid.len=0;
+		}
 	}
 	
 	if (likely(next_hop!=0 || (flags & UAC_DNS_FAILOVER_F))){
@@ -463,6 +521,36 @@ static int prepare_new_uac( struct cell *t, struct sip_msg *i_req,
 		t->uac[branch].path.s[i_req->path_vec.len]=0;
 		memcpy( t->uac[branch].path.s, i_req->path_vec.s, i_req->path_vec.len);
 	}
+	if (unlikely(i_req->instance.s && i_req->instance.len)){
+		t->uac[branch].instance.s=shm_malloc(i_req->instance.len+1);
+		if (unlikely(t->uac[branch].instance.s==0)) {
+			shm_free(shbuf);
+			t->uac[branch].request.buffer=0;
+			t->uac[branch].request.buffer_len=0;
+			t->uac[branch].uri.s=0;
+			t->uac[branch].uri.len=0;
+			ret=E_OUT_OF_MEM;
+			goto error01;
+		}
+		t->uac[branch].instance.len=i_req->instance.len;
+		t->uac[branch].instance.s[i_req->instance.len]=0;
+		memcpy( t->uac[branch].instance.s, i_req->instance.s, i_req->instance.len);
+	}
+	if (unlikely(i_req->ruid.s && i_req->ruid.len)){
+		t->uac[branch].ruid.s=shm_malloc(i_req->ruid.len+1);
+		if (unlikely(t->uac[branch].ruid.s==0)) {
+			shm_free(shbuf);
+			t->uac[branch].request.buffer=0;
+			t->uac[branch].request.buffer_len=0;
+			t->uac[branch].uri.s=0;
+			t->uac[branch].uri.len=0;
+			ret=E_OUT_OF_MEM;
+			goto error01;
+		}
+		t->uac[branch].ruid.len=i_req->ruid.len;
+		t->uac[branch].ruid.s[i_req->ruid.len]=0;
+		memcpy( t->uac[branch].ruid.s, i_req->ruid.s, i_req->ruid.len);
+	}
 	ret=0;
 
 error01:
@@ -474,6 +562,12 @@ error03:
 	if (unlikely(free_path)){
 		reset_path_vector(i_req);
 	}
+	if (unlikely(free_instance)){
+		reset_instance(i_req);
+	}
+	if (unlikely(free_ruid)){
+		reset_ruid(i_req);
+	}
 	if (dst_uri_backed_up){
 		reset_dst_uri(i_req); /* free dst_uri */
 		i_req->dst_uri=dst_uri_bak;
@@ -483,6 +577,8 @@ error03:
 	i_req->parsed_uri=parsed_uri_bak;
 	i_req->parsed_uri_ok=parsed_uri_ok_bak;
 	i_req->path_vec=path_bak;
+	i_req->instance=instance_bak;
+	i_req->ruid=ruid_bak;
 	
 	/* Delete the duplicated lump lists, this will also delete
 	 * all lumps created here, such as lumps created in per-branch
@@ -646,7 +742,7 @@ int add_blind_uac( /*struct cell *t*/ )
 static int add_uac( struct cell *t, struct sip_msg *request, str *uri,
 					str* next_hop, str* path, struct proxy_l *proxy,
 					struct socket_info* fsocket, snd_flags_t snd_flags,
-					int proto, int flags)
+					int proto, int flags, str *instance, str *ruid)
 {
 
 	int ret;
@@ -691,7 +787,7 @@ static int add_uac( struct cell *t, struct sip_msg *request, str *uri,
 	/* now message printing starts ... */
 	if (unlikely( (ret=prepare_new_uac(t, request, branch, uri, path,
 										next_hop, fsocket, snd_flags,
-										proto, flags)) < 0)){
+										proto, flags, instance, ruid)) < 0)){
 		ser_error=ret;
 		goto error01;
 	}
@@ -731,7 +827,8 @@ static int add_uac_from_buf( struct cell *t, struct sip_msg *request,
 								struct socket_info* fsocket,
 								snd_flags_t send_flags,
 								int proto,
-								char *buf, short buf_len)
+								char *buf, short buf_len,
+								str *instance, str *ruid)
 {
 
 	int ret;
@@ -802,6 +899,38 @@ static int add_uac_from_buf( struct cell *t, struct sip_msg *request,
 		t->uac[branch].path.s[path->len]=0;
 		memcpy( t->uac[branch].path.s, path->s, path->len);
 	}
+	/* copy the instance */
+	if (unlikely(instance && instance->s)){
+		t->uac[branch].instance.s=shm_malloc(instance->len+1);
+		if (unlikely(t->uac[branch].instance.s==0)) {
+			shm_free(shbuf);
+			t->uac[branch].request.buffer=0;
+			t->uac[branch].request.buffer_len=0;
+			t->uac[branch].uri.s=0;
+			t->uac[branch].uri.len=0;
+			ret=ser_error=E_OUT_OF_MEM;
+			goto error;
+		}
+		t->uac[branch].instance.len=instance->len;
+		t->uac[branch].instance.s[instance->len]=0;
+		memcpy( t->uac[branch].instance.s, instance->s, instance->len);
+	}
+	/* copy the ruid */
+	if (unlikely(ruid && ruid->s)){
+		t->uac[branch].ruid.s=shm_malloc(ruid->len+1);
+		if (unlikely(t->uac[branch].ruid.s==0)) {
+			shm_free(shbuf);
+			t->uac[branch].request.buffer=0;
+			t->uac[branch].request.buffer_len=0;
+			t->uac[branch].uri.s=0;
+			t->uac[branch].uri.len=0;
+			ret=ser_error=E_OUT_OF_MEM;
+			goto error;
+		}
+		t->uac[branch].ruid.len=ruid->len;
+		t->uac[branch].ruid.s[ruid->len]=0;
+		memcpy( t->uac[branch].ruid.s, ruid->s, ruid->len);
+	}
 	membar_write(); /* to allow lockless ops (e.g. prepare_to_cancel()) we want
 					   to be sure everything above is fully written before
 					   updating branches no. */
@@ -873,7 +1002,8 @@ int add_uac_dns_fallback(struct cell *t, struct sip_msg* msg,
 							old_uac->request.dst.send_flags,
 							old_uac->request.dst.proto,
 							old_uac->request.buffer,
-							old_uac->request.buffer_len);
+							old_uac->request.buffer_len,
+							&old_uac->instance, &old_uac->ruid);
 			}else
 				/* add_uac will use dns_h => next_hop will be ignored.
 				 * Unfortunately we can't reuse the old buffer, the branch id
@@ -884,7 +1014,8 @@ int add_uac_dns_fallback(struct cell *t, struct sip_msg* msg,
 								SND_F_FORCE_SOCKET)?
 									old_uac->request.dst.send_sock:0,
 							old_uac->request.dst.send_flags,
-							old_uac->request.dst.proto, UAC_DNS_FAILOVER_F);
+							old_uac->request.dst.proto, UAC_DNS_FAILOVER_F,
+							&old_uac->instance, &old_uac->ruid);
 
 			if (ret<0){
 				/* failed, delete the copied dns_h */
@@ -959,7 +1090,8 @@ int e2e_cancel_branch( struct sip_msg *cancel_msg, struct cell *t_cancel,
 		if (unlikely((ret=prepare_new_uac( t_cancel, cancel_msg, branch,
 									&t_invite->uac[branch].uri,
 									&t_invite->uac[branch].path,
-									0, 0, snd_flags, PROTO_NONE, 0)) <0)){
+									0, 0, snd_flags, PROTO_NONE, 0,
+									NULL, NULL)) <0)){
 			ser_error=ret;
 			goto error;
 		}
@@ -1418,7 +1550,7 @@ int t_forward_nonack( struct cell *t, struct sip_msg* p_msg ,
 	int success_branch;
 	int try_new;
 	int lock_replies;
-	str dst_uri, path;
+	str dst_uri, path, instance, ruid;
 	struct socket_info* si;
 	flag_t backup_bflags = 0;
 	flag_t bflags = 0;
@@ -1484,7 +1616,7 @@ int t_forward_nonack( struct cell *t, struct sip_msg* p_msg ,
 		branch_ret=add_uac( t, p_msg, GET_RURI(p_msg), GET_NEXT_HOP(p_msg),
 							&p_msg->path_vec, proxy, p_msg->force_send_socket,
 							p_msg->fwd_send_flags, proto,
-							(p_msg->dst_uri.len)?0:UAC_SKIP_BR_DST_F);
+							(p_msg->dst_uri.len)?0:UAC_SKIP_BR_DST_F, &p_msg->instance, &p_msg->ruid);
 		if (branch_ret>=0) 
 			added_branches |= 1<<branch_ret;
 		else
@@ -1493,14 +1625,14 @@ int t_forward_nonack( struct cell *t, struct sip_msg* p_msg ,
 
 	init_branch_iterator();
 	while((current_uri.s=next_branch( &current_uri.len, &q, &dst_uri, &path,
-										&bflags, &si, 0))) {
+										&bflags, &si, &ruid, &instance))) {
 		try_new++;
 		setbflagsval(0, bflags);
 
 		branch_ret=add_uac( t, p_msg, &current_uri,
 							(dst_uri.len) ? (&dst_uri) : &current_uri,
 							&path, proxy, si, p_msg->fwd_send_flags,
-							proto, (dst_uri.len)?0:UAC_SKIP_BR_DST_F);
+							proto, (dst_uri.len)?0:UAC_SKIP_BR_DST_F, &instance, &ruid);
 		/* pick some of the errors in case things go wrong;
 		   note that picking lowest error is just as good as
 		   any other algorithm which picks any other negative

+ 5 - 1
modules/tm/t_hooks.h

@@ -82,7 +82,9 @@ struct cell;
 #endif
 #define TMCB_REQUEST_SENT_N     22
 #define TMCB_RESPONSE_SENT_N    23
-#define TMCB_MAX_N              23
+#define TMCB_ON_BRANCH_FAILURE_RO_N 24
+#define TMCB_ON_BRANCH_FAILURE_N 25
+#define TMCB_MAX_N              25
 
 
 #define TMCB_REQUEST_IN       (1<<TMCB_REQUEST_IN_N)
@@ -111,6 +113,8 @@ struct cell;
 #endif
 #define TMCB_REQUEST_SENT      (1<<TMCB_REQUEST_SENT_N)
 #define TMCB_RESPONSE_SENT     (1<<TMCB_RESPONSE_SENT_N)
+#define TMCB_ON_BRANCH_FAILURE (1<<TMCB_ON_BRANCH_FAILURE_N)
+#define TMCB_ON_BRANCH_FAILURE_RO (1<<TMCB_ON_BRANCH_FAILURE_RO_N)
 #define TMCB_MAX              ((1<<(TMCB_MAX_N+1))-1)
 
 

+ 129 - 0
modules/tm/t_reply.c

@@ -172,6 +172,8 @@ char *tm_tag_suffix;
 
 /* where to go if there is no positive reply (>=300) */
 static int goto_on_failure=0;
+/* where to go if a failure is returned on a branch */
+int goto_on_branch_failure=0;
 /* where to go on receipt of reply */
 static int goto_on_reply=0;
 /* where to go on receipt of reply without transaction context */
@@ -1039,6 +1041,62 @@ int run_failure_handlers(struct cell *t, struct sip_msg *rpl,
 }
 
 
+/* return 1 if a failure_route processes */
+int run_branch_failure_handlers(struct cell *t, struct sip_msg *rpl,
+					int code, int extra_flags)
+{
+	static struct sip_msg faked_req;
+	struct sip_msg *shmem_msg = t->uas.request;
+
+	/* failure_route for a local UAC? */
+	if (!shmem_msg) {
+		LOG(L_WARN,"Warning: run_branch_failure_handlers: no UAC support (%d, %d) \n",
+			goto_on_branch_failure, t->tmcb_hl.reg_types);
+		return 0;
+	}
+
+	/* don't start faking anything if we don't have to */
+	if (unlikely((goto_on_branch_failure < 0) && !has_tran_tmcbs( t, TMCB_ON_BRANCH_FAILURE))) {
+		LOG(L_WARN,
+			"Warning: run_failure_handlers: no branch_failure handler (%d, %d)\n",
+			goto_on_branch_failure, t->tmcb_hl.reg_types);
+		return 1;
+	}
+
+	if (!fake_req(&faked_req, shmem_msg, extra_flags, &t->uac[picked_branch])) {
+		LOG(L_ERR, "ERROR: run_branch_failure_handlers: fake_req failed\n");
+		return 0;
+	}
+	/* fake also the env. conforming to the fake msg */
+	faked_env( t, &faked_req);
+	set_route_type(BRANCH_FAILURE_ROUTE);
+	set_t(t, picked_branch);
+	/* DONE with faking ;-) -> run the branch_failure handlers */
+
+	if (unlikely(has_tran_tmcbs( t, TMCB_ON_BRANCH_FAILURE)) ) {
+		run_trans_callbacks( TMCB_ON_BRANCH_FAILURE, t, &faked_req, rpl, code);
+	}
+	if (goto_on_branch_failure >= 0) {
+		if (exec_pre_script_cb(&faked_req, BRANCH_FAILURE_CB_TYPE)>0) {
+			/* run a branch_failure_route action if some was marked */
+			if (run_top_route(event_rt.rlist[goto_on_branch_failure], &faked_req, 0)<0)
+				LOG(L_ERR, "ERROR: run_branch_failure_handlers: Error in run_top_route\n");
+			exec_post_script_cb(&faked_req, BRANCH_FAILURE_CB_TYPE);
+		}
+		/* update message flags, if changed in branch_failure route */
+		t->uas.request->flags = faked_req.flags;
+	}
+
+	/* restore original environment and free the fake msg */
+	faked_env( t, 0);
+	free_faked_req(&faked_req,t);
+
+	/* if branch_failure handler changed flag, update transaction context */
+	shmem_msg->flags = faked_req.flags;
+	return 1;
+}
+
+
 
 /* 401, 407, 415, 420, and 484 have priority over the other 4xx*/
 inline static short int get_4xx_prio(unsigned char xx)
@@ -1252,6 +1310,25 @@ static enum rps t_should_relay_response( struct cell *Trans , int new_code,
 
 		Trans->uac[branch].last_received=new_code;
 
+/* New branch failure route code */
+		/* also append the current reply to the transaction to
+		 * make it available in failure routes - a kind of "fake"
+		 * save of the final reply per branch */
+		Trans->uac[branch].reply = reply;
+		if (unlikely(goto_on_branch_failure > 0 )) {
+			extra_flags=
+				((Trans->uac[branch].request.flags & F_RB_TIMEOUT)?
+							FL_TIMEOUT:0) | 
+				((Trans->uac[branch].request.flags & F_RB_REPLIED)?
+						 	FL_REPLIED:0);
+			tm_ctx_set_branch_index(branch);
+			picked_branch = branch;
+			run_branch_failure_handlers( Trans, Trans->uac[branch].reply,
+									new_code, extra_flags);
+		}
+/* END - New branch failure route code */
+
+
 		/* if all_final return lowest */
 		picked_branch=t_pick_branch(branch,new_code, Trans, &picked_code);
 		if (picked_branch==-2) { /* branches open yet */
@@ -2530,6 +2607,58 @@ void t_drop_replies(int v)
 	drop_replies = v;
 }
 
+int t_get_this_branch_instance(struct sip_msg *msg, str *instance)
+{
+	struct cell *t;
+	if (!msg || !instance)
+	{
+		LM_ERR("Invalid params\n");
+		return -1;
+	}
+	if (get_route_type() != BRANCH_FAILURE_ROUTE)
+	{
+		LM_ERR("Called t_get_this_branch_instance not in a branch_failure_route\n");
+		return -1;
+	}
+
+	t = 0;
+	/* first get the transaction */
+	if (t_check(msg, 0 ) == -1) return -1;
+	if ((t = get_t()) == 0) {
+		LOG(L_ERR, "ERROR: t_check_status: cannot check status for a reply "
+			"which has no T-state established\n");
+		return -1;
+	}
+	*instance = t->uac[get_t_branch()].instance;
+	return 1;
+}
+
+int t_get_this_branch_ruid(struct sip_msg *msg, str *ruid)
+{
+	struct cell *t;
+	if (!msg || !ruid)
+	{
+		LM_ERR("Invalid params\n");
+		return -1;
+	}
+	if (get_route_type() != BRANCH_FAILURE_ROUTE)
+	{
+		LM_ERR("Called t_get_this_branch_ruid not in a branch_failure_route\n");
+		return -1;
+	}
+
+	t = 0;
+	/* first get the transaction */
+	if (t_check(msg, 0 ) == -1) return -1;
+	if ((t = get_t()) == 0) {
+		LOG(L_ERR, "ERROR: t_check_status: cannot check status for a reply "
+			"which has no T-state established\n");
+		return -1;
+	}
+	*ruid = t->uac[get_t_branch()].ruid;
+	return 1;
+}
+
 #if 0
 static int send_reply(struct cell *trans, unsigned int code, str* text, str* body, str* headers, str* to_tag)
 {

+ 9 - 0
modules/tm/t_reply.h

@@ -61,6 +61,7 @@ enum rps {
 extern char tm_tags[TOTAG_VALUE_LEN];
 extern char *tm_tag_suffix;
 
+extern int goto_on_branch_failure;
 extern int goto_on_sl_reply;
 
 extern int failure_reply_mode;
@@ -161,6 +162,11 @@ int run_failure_handlers(struct cell *t, struct sip_msg *rpl,
 					int code, int extra_flags);
 typedef int (*run_failure_handlers_f)(struct cell*, struct sip_msg*, int, int);
 
+/* return 1 if a branch_failure_route processes */
+int run_branch_failure_handlers(struct cell *t, struct sip_msg *rpl,
+					int code, int extra_flags);
+typedef int (*run_branch_failure_handlers_f)(struct cell*, struct sip_msg*, int, int);
+
 
 /* Retransmits the last sent inbound reply.
  * Returns  -1 - error
@@ -237,4 +243,7 @@ void free_faked_req(struct sip_msg *faked_req, struct cell *t);
 typedef int (*tget_picked_f)(void);
 int t_get_picked_branch(void);
 
+int t_get_this_branch_instance(struct sip_msg *msg, str *instance);
+int t_get_this_branch_ruid(struct sip_msg *msg, str *ruid);
+
 #endif

+ 85 - 189
modules/tm/t_serial.c

@@ -365,7 +365,7 @@ void add_contact_flows_avp(str *uri, str *dst_uri, str *path, str *sock_str,
  * Adds to request a new destination set that includes highest
  * priority class contacts in contacts_avp, but only one contact with same
  * +sip.instance value is included.  Others are added to contact_flows_avp
- * for later consumption by next_contact_flows().
+ * for later consumption by next_contact_flow().
  * Request URI is rewritten with first contact and the remaining contacts
  * (if any) are added as branches. Removes all highest priority contacts
  * from contacts_avp.
@@ -461,6 +461,7 @@ int t_next_contacts(struct sip_msg* msg, char* key, char* value)
 	il->instance.len = instance.len;
 	memcpy(il->instance.s, instance.s, instance.len);
 	il->next = (struct instance_list *)0;
+	set_instance(msg, &instance);
     }
 
     vavp = xavp_get(&ruid_name, xavp->val.v.xavp);
@@ -588,7 +589,12 @@ int t_next_contacts(struct sip_msg* msg, char* key, char* value)
         vavp = xavp_get(&ruid_name, xavp->val.v.xavp);
         ruid = vavp->val.v.s;
 
-	if (append_branch(msg, &uri, &dst_uri, &path, 0, flags, sock, 0, 0,
+	LM_DBG("Appending branch uri-'%.*s' dst-'%.*s' path-'%.*s' inst-'%.*s'\n",
+		uri.len, uri.s,
+		dst_uri.len, dst_uri.s,
+		path.len, path.s,
+		instance.len, instance.s);
+	if (append_branch(msg, &uri, &dst_uri, &path, 0, flags, sock, &instance, 0,
 			  &ruid) != 1) {
 	    LM_ERR("appending branch failed\n");
 	    free_instance_list(il);
@@ -622,15 +628,15 @@ int t_next_contacts(struct sip_msg* msg, char* key, char* value)
  * Returns 1, if contact_flows_avp was not empty and a destination set was
  * successfully added.  Returns -2, if contact_flows_avp was empty and thus
  * there was nothing to do. Returns -1 in case of an error. */
-int t_next_contact_flows(struct sip_msg* msg, char* key, char* value)
+int t_next_contact_flow(struct sip_msg* msg, char* key, char* value)
 {
     str uri, dst_uri, path, instance, host, ruid;
+	str this_instance;
     struct socket_info *sock;
     unsigned int flags;
     sr_xavp_t *xavp_list, *xavp, *next_xavp, *vavp;
     char *tmp;
     int port, proto;
-    struct instance_list *il, *ilp;
 
     /* Check if contact_flows_avp has been defined */
     if (contact_flows_avp.len == 0) {
@@ -640,208 +646,98 @@ int t_next_contact_flows(struct sip_msg* msg, char* key, char* value)
     }
 
     /* Load Request-URI and branches */
+	t_get_this_branch_instance(msg, &this_instance);
 
-    /* Find first contact_flows_avp value */
-    xavp_list = xavp_get(&contact_flows_avp, NULL);
-    if (!xavp_list) {
-	LM_DBG("no contacts in contact_flows_avp - we are done!\n");
-	return -2;
-    }
-
-    xavp = xavp_list;
-    next_xavp = xavp_get_next(xavp);
-
-    vavp = xavp_get(&uri_name, xavp->val.v.xavp);
-    uri = vavp->val.v.s;
-
-    vavp = xavp_get(&dst_uri_name, xavp->val.v.xavp);
-    if (vavp != NULL) {
-	dst_uri = vavp->val.v.s;
-    } else {
-	dst_uri.s = 0;
-	dst_uri.len = 0;
-    }
-
-    vavp = xavp_get(&path_name, xavp->val.v.xavp);
-    if (vavp != NULL) {
-	path = vavp->val.v.s;
-    } else {
-	path.s = 0;
-	path.len = 0;
-    }
-
-    vavp = xavp_get(&sock_name, xavp->val.v.xavp);
-    if (vavp != NULL) {
-	tmp = vavp->val.v.s.s;
-	if (parse_phostport(tmp, &host.s, &host.len, &port, &proto) != 0) {
-	    LM_ERR("parsing of socket info <%s> failed\n", tmp);
-	    xavp_destroy_list(&xavp_list);
-	    return -1;
+	if (this_instance.len == 0)
+	{
+		LM_DBG("No instance on this branch\n");
+		return -2;
 	}
-	sock = grep_sock_info(&host, (unsigned short)port,
-			      (unsigned short)proto);
-	if (sock == 0) {
-	    xavp_destroy_list(&xavp_list);
-	    return -1;
+	/* Find first contact_flows_avp value */
+	xavp_list = xavp_get(&contact_flows_avp, NULL);
+	if (!xavp_list) {
+		LM_DBG("no contacts in contact_flows_avp - we are done!\n");
+		return -2;
 	}
-    } else {
-	sock = NULL;
-    }
 
-    vavp = xavp_get(&flags_name, xavp->val.v.xavp);
-    flags = vavp->val.v.i;
-
-    vavp = xavp_get(&instance_name, xavp->val.v.xavp);
-    il = (struct instance_list *)0;
-    if ((vavp != NULL) && next_xavp) {
-	instance = vavp->val.v.s;
-	il = (struct instance_list *)pkg_malloc(sizeof(struct instance_list));
-	if (!il) {
-	    LM_ERR("no memory for instance list entry\n");
-	    return -1;
-	}
-	il->instance.s = pkg_malloc(instance.len);
-	if (!il->instance.s) {
-	    pkg_free(il);
-	    LM_ERR("no memory for instance list instance\n");
-	    return -1;
-	}
-	il->instance.len = instance.len;
-	memcpy(il->instance.s, instance.s, instance.len);
-	il->next = (struct instance_list *)0;
-    }
-
-    vavp = xavp_get(&ruid_name, xavp->val.v.xavp);
-    ruid = vavp->val.v.s;
-
-    /* Rewrite Request-URI */
-    rewrite_uri(msg, &uri);
-
-    if (dst_uri.len) {
-	set_dst_uri(msg, &dst_uri);
-    } else {
-	reset_dst_uri(msg);
-    }
-
-    if (path.len) {
-	set_path_vector(msg, &path);
-    } else {
-	reset_path_vector(msg);
-    }
-
-    set_force_socket(msg, sock);
-
-    set_ruid(msg, &ruid);
-
-    setbflagsval(0, flags);
+	xavp = xavp_list;
 
-    /* Append branches until out of branches. */
-    /* Do not include a branch that has same instance value as some */
-    /* previous branch. */
+	while (xavp) {
+		next_xavp = xavp_get_next(xavp);
 
-    xavp_rm(xavp, NULL);
-    xavp = next_xavp;
-
-    while (xavp) {
-	
-	next_xavp = xavp_get_next(xavp);
-
-	vavp = xavp_get(&instance_name, xavp->val.v.xavp);
-	if (vavp != NULL) {
-	    instance = vavp->val.v.s;
-	    ilp = il;
-	    while (ilp) {
-		if ((instance.len == ilp->instance.len) &&
-		    (strncmp(instance.s, ilp->instance.s, instance.len) == 0))
-		    break;
-		ilp = ilp->next;
-	    }
-	    if (ilp) {
-		/* skip already appended instance */
-		xavp = next_xavp;
-		continue;
-	    }
-	    if (next_xavp) {
-		ilp = (struct instance_list *)
-		pkg_malloc(sizeof(struct instance_list));
-		if (!ilp) {
-		    LM_ERR("no memory for new instance list entry\n");
-		    free_instance_list(il);
-		    return -1;
+	   	vavp = xavp_get(&instance_name, xavp->val.v.xavp);
+		if (vavp == NULL)
+		{
+			/* Does not match this instance */
+			goto next_xavp;
 		}
-		ilp->instance.s = pkg_malloc(instance.len);
-		if (!ilp->instance.s) {
-		    pkg_free(il);
-		    LM_ERR("no memory for instance list instance\n");
-		    return -1;
+		else
+		{
+			instance = vavp->val.v.s;
+			if ((instance.len != this_instance.len) ||
+			    (strncmp(instance.s, this_instance.s, instance.len) != 0))
+				/* Does not match this instance */
+				goto next_xavp;
 		}
-		ilp->instance.len = instance.len;
-		memcpy(ilp->instance.s, instance.s, instance.len);
-		ilp->next = il;
-		il = ilp;
-	    } else {
-		LM_ERR("instance missing from contact_flow_avp contact\n");
-		free_instance_list(il);
-		return -1;
-	    }
-	}
 
-	vavp = xavp_get(&uri_name, xavp->val.v.xavp);
-	uri = vavp->val.v.s;
+		vavp = xavp_get(&uri_name, xavp->val.v.xavp);
+		uri = vavp->val.v.s;
 
-	vavp = xavp_get(&dst_uri_name, xavp->val.v.xavp);
-	if (vavp != NULL) {
-	    dst_uri = vavp->val.v.s;
-	} else {
-	    dst_uri.len = 0;
-	}
+		vavp = xavp_get(&dst_uri_name, xavp->val.v.xavp);
+		if (vavp != NULL) {
+			dst_uri = vavp->val.v.s;
+		} else {
+			dst_uri.len = 0;
+		}
 
-	vavp = xavp_get(&path_name, xavp->val.v.xavp);
-	if (vavp != NULL) {
-	    path = vavp->val.v.s;
-	} else {
-	    path.len = 0;
-	}
+		vavp = xavp_get(&path_name, xavp->val.v.xavp);
+		if (vavp != NULL) {
+			path = vavp->val.v.s;
+		} else {
+			path.len = 0;
+		}
 
-	vavp = xavp_get(&sock_name, xavp->val.v.xavp);
-	if (vavp != NULL) {
-	    tmp = vavp->val.v.s.s;
-	    if (parse_phostport(tmp, &host.s, &host.len, &port, &proto) != 0) {
-		LM_ERR("parsing of socket info <%s> failed\n", tmp);
-		free_instance_list(il);
-		xavp_destroy_list(&xavp_list);
-		return -1;
-	    }
-	    sock = grep_sock_info(&host, (unsigned short)port,
+		vavp = xavp_get(&sock_name, xavp->val.v.xavp);
+		if (vavp != NULL) {
+			tmp = vavp->val.v.s.s;
+			if (parse_phostport(tmp, &host.s, &host.len, &port, &proto) != 0) {
+				LM_ERR("parsing of socket info <%s> failed\n", tmp);
+				xavp_rm(xavp, NULL);
+				return -1;
+			}
+			sock = grep_sock_info(&host, (unsigned short)port,
 				  (unsigned short)proto);
-	    if (sock == 0) {
-		free_instance_list(il);
-		xavp_destroy_list(&xavp_list);
-		return -1;
-	    }
-	} else {
-	    sock = NULL;
-	}
+			if (sock == 0) {
+				xavp_rm(xavp, NULL);
+				return -1;
+			}
+		} else {
+			sock = NULL;
+		}
 
-	vavp = xavp_get(&flags_name, xavp->val.v.xavp);
-	flags = vavp->val.v.i;
+		vavp = xavp_get(&flags_name, xavp->val.v.xavp);
+		flags = vavp->val.v.i;
 
-        vavp = xavp_get(&ruid_name, xavp->val.v.xavp);
-        ruid = vavp->val.v.s;
+		vavp = xavp_get(&ruid_name, xavp->val.v.xavp);
+		ruid = vavp->val.v.s;
 
-	if (append_branch(msg, &uri, &dst_uri, &path, 0, flags, sock, 0, 0,
+		LM_DBG("Appending branch uri-'%.*s' dst-'%.*s' path-'%.*s' inst-'%.*s'\n",
+			uri.len, uri.s,
+			dst_uri.len, dst_uri.s,
+			path.len, path.s,
+			instance.len, instance.s);
+		if (append_branch(msg, &uri, &dst_uri, &path, 0, flags, sock, &instance, 0,
 			  &ruid) != 1) {
-	    LM_ERR("appending branch failed\n");
-	    free_instance_list(il);
-	    xavp_destroy_list(&xavp_list);
-	    return -1;
-	}
-
-	xavp_rm(xavp, NULL);
-	xavp = next_xavp;
-    }
+			LM_ERR("appending branch failed\n");
+			xavp_destroy_list(&xavp_list);
+			return -1;
+		}
 
-    free_instance_list(il);
+		xavp_rm(xavp, NULL);
+		return 1;
+next_xavp:
+		xavp = next_xavp;
+	}
 
-    return 1;
+	return -1;
 }

+ 1 - 1
modules/tm/t_serial.h

@@ -34,4 +34,4 @@ int t_load_contacts(struct sip_msg* msg, char* key, char* value);
 
 int t_next_contacts(struct sip_msg* msg, char* key, char* value);
 
-int t_next_contact_flows(struct sip_msg* msg, char* key, char* value);
+int t_next_contact_flow(struct sip_msg* msg, char* key, char* value);

+ 12 - 8
modules/tm/tm.c

@@ -414,7 +414,7 @@ static cmd_export_t cmds[]={
 	{"t_on_branch",       w_t_on_branch,         1, fixup_on_branch,
 			REQUEST_ROUTE | FAILURE_ROUTE },
 	{"t_check_status",     t_check_status,          1, fixup_t_check_status,
-			REQUEST_ROUTE | FAILURE_ROUTE | ONREPLY_ROUTE },
+			REQUEST_ROUTE | FAILURE_ROUTE | ONREPLY_ROUTE | BRANCH_FAILURE_ROUTE},
 	{"t_write_req",       t_write_req,              2, fixup_t_write,
 			REQUEST_ROUTE | FAILURE_ROUTE },
 	{"t_write_unix",      t_write_unix,             2, fixup_t_write,
@@ -475,8 +475,8 @@ static cmd_export_t cmds[]={
 			REQUEST_ROUTE | FAILURE_ROUTE},
 	{"t_next_contacts", t_next_contacts,            0, 0,
 			REQUEST_ROUTE | FAILURE_ROUTE},
-	{"t_next_contact_flows", t_next_contact_flows,            0, 0,
-			REQUEST_ROUTE | FAILURE_ROUTE},
+	{"t_next_contact_flow", t_next_contact_flow,            0, 0,
+			REQUEST_ROUTE | BRANCH_FAILURE_ROUTE},
 
 	/* not applicable from the script */
 	{"load_tm",            (cmd_function)load_tm,           NO_SCRIPT,   0, 0},
@@ -601,7 +601,6 @@ static int fixup_on_failure(void** param, int param_no)
 }
 
 
-
 static int fixup_on_reply(void** param, int param_no)
 {
 	if (param_no==1){
@@ -855,6 +854,9 @@ static int mod_init(void)
 	    return -1;
 	}
 
+	goto_on_branch_failure = route_lookup(&event_rt, "tm:branch-failure");
+	if (goto_on_branch_failure >= 0 && event_rt.rlist[goto_on_branch_failure]==0)
+		goto_on_branch_failure = -1; /* disable */
 #ifdef WITH_EVENT_LOCAL_REQUEST
 	goto_on_local_req=route_lookup(&event_rt, "tm:local-request");
 	if (goto_on_local_req>=0 && event_rt.rlist[goto_on_local_req]==0)
@@ -894,8 +896,6 @@ static int child_init(int rank)
 
 
 
-
-
 /**************************** wrapper functions ***************************/
 static int t_check_status(struct sip_msg* msg, char *p1, char *foo)
 {
@@ -979,7 +979,10 @@ static int t_check_status(struct sip_msg* msg, char *p1, char *foo)
 		}
 		status = int2str( lowest_status , 0);
 		break;
-
+	case BRANCH_FAILURE_ROUTE:
+#warning add the status for branch failure route
+		status = int2str(t->uac[get_t_branch()].last_received, 0);
+		break;
 	default:
 		LOG(L_ERR,"ERROR:t_check_status: unsupported route type %d\n",
 				get_route_type());
@@ -1368,6 +1371,7 @@ inline static int w_t_on_failure( struct sip_msg* msg, char *go_to, char *foo)
 	return 1;
 }
 
+
 inline static int w_t_on_branch( struct sip_msg* msg, char *go_to, char *foo)
 {
 	t_on_branch( (unsigned int )(long) go_to );
@@ -1438,7 +1442,7 @@ inline static int _w_t_relay_to(struct sip_msg  *p_msg ,
 	struct cell *t;
 	int res;
 
-	if (is_route_type(FAILURE_ROUTE)) {
+	if (is_route_type(FAILURE_ROUTE|BRANCH_FAILURE_ROUTE)) {
 		t=get_t();
 		if (!t || t==T_UNDEFINED) {
 			LOG(L_CRIT, "BUG: w_t_relay_to: undefined T\n");

+ 1 - 0
modules/tm/tm_load.c

@@ -110,6 +110,7 @@ int load_tm( struct tm_binds *tmb)
 	tmb->t_uac_with_ids = t_uac_with_ids;
 	tmb->t_unref = t_unref;
 	tmb->run_failure_handlers = run_failure_handlers;
+	tmb->run_branch_failure_handlers = run_branch_failure_handlers;
 	tmb->cancel_uacs = cancel_uacs;
 	tmb->cancel_all_uacs = cancel_all_uacs;
 	tmb->prepare_request_within = prepare_req_within;

+ 1 - 0
modules/tm/tm_load.h

@@ -88,6 +88,7 @@ struct tm_binds {
 	trelease_f         t_release;
 	tunref_f           t_unref;
 	run_failure_handlers_f run_failure_handlers;
+	run_branch_failure_handlers_f run_branch_failure_handlers;
 	cancel_uacs_f      cancel_uacs;
 	cancel_all_uacs_f  cancel_all_uacs;
 	prepare_request_within_f  prepare_request_within;

+ 72 - 69
modules/tmx/README

@@ -1,3 +1,4 @@
+
 TMX Module
 
 Daniel-Constantin Mierla
@@ -11,8 +12,8 @@ Daniel-Constantin Mierla
 
    <[email protected]>
 
-   Copyright © 2009 Daniel-Constantin Mierla
-     __________________________________________________________________
+   Copyright © 2009 Daniel-Constantin Mierla
+     _________________________________________________________________
 
    Table of Contents
 
@@ -26,24 +27,24 @@ Daniel-Constantin Mierla
 
         3. Functions
 
-              3.1. t_cancel_branches(which)
-              3.2. t_cancel_callid(callid, cseq, flag)
-              3.3. t_reply_callid(callid, cseq, code, reason)
-              3.4. t_flush_flags()
-              3.5. t_is_failure_route()
-              3.6. t_is_branch_route()
-              3.7. t_is_reply_route()
-              3.8. t_suspend()
-              3.9. t_continue(tindex, tlabel, rtname)
+              3.1. t_cancel_branches(which) 
+              3.2. t_cancel_callid(callid, cseq, flag) 
+              3.3. t_reply_callid(callid, cseq, code, reason) 
+              3.4. t_flush_flags() 
+              3.5. t_is_failure_route() 
+              3.6. t_is_branch_route() 
+              3.7. t_is_reply_route() 
+              3.8. t_suspend() 
+              3.9. t_continue(tindex, tlabel, rtname) 
 
         4. Exported pseudo-variables
         5. MI Commands
 
-              5.1. t_uac_dlg
-              5.2. t_uac_cancel
-              5.3. t_hash
-              5.4. t_reply
-              5.5. t_reply_callid
+              5.1. t_uac_dlg 
+              5.2. t_uac_cancel 
+              5.3. t_hash 
+              5.4. t_reply 
+              5.5. t_reply_callid 
 
         6. Statistics
 
@@ -83,24 +84,24 @@ Chapter 1. Admin Guide
 
    3. Functions
 
-        3.1. t_cancel_branches(which)
-        3.2. t_cancel_callid(callid, cseq, flag)
-        3.3. t_reply_callid(callid, cseq, code, reason)
-        3.4. t_flush_flags()
-        3.5. t_is_failure_route()
-        3.6. t_is_branch_route()
-        3.7. t_is_reply_route()
-        3.8. t_suspend()
-        3.9. t_continue(tindex, tlabel, rtname)
+        3.1. t_cancel_branches(which) 
+        3.2. t_cancel_callid(callid, cseq, flag) 
+        3.3. t_reply_callid(callid, cseq, code, reason) 
+        3.4. t_flush_flags() 
+        3.5. t_is_failure_route() 
+        3.6. t_is_branch_route() 
+        3.7. t_is_reply_route() 
+        3.8. t_suspend() 
+        3.9. t_continue(tindex, tlabel, rtname) 
 
    4. Exported pseudo-variables
    5. MI Commands
 
-        5.1. t_uac_dlg
-        5.2. t_uac_cancel
-        5.3. t_hash
-        5.4. t_reply
-        5.5. t_reply_callid
+        5.1. t_uac_dlg 
+        5.2. t_uac_cancel 
+        5.3. t_hash 
+        5.4. t_reply 
+        5.5. t_reply_callid 
 
    6. Statistics
 
@@ -135,25 +136,25 @@ Chapter 1. Admin Guide
 
 2.2. External Libraries or Applications
 
-   The following libraries or applications must be installed before
+   The  following  libraries  or  applications  must  be installed before
    running Kamailio with this module loaded:
      * None.
 
 3. Functions
 
-   3.1. t_cancel_branches(which)
-   3.2. t_cancel_callid(callid, cseq, flag)
-   3.3. t_reply_callid(callid, cseq, code, reason)
-   3.4. t_flush_flags()
-   3.5. t_is_failure_route()
-   3.6. t_is_branch_route()
-   3.7. t_is_reply_route()
-   3.8. t_suspend()
-   3.9. t_continue(tindex, tlabel, rtname)
+   3.1. t_cancel_branches(which) 
+   3.2. t_cancel_callid(callid, cseq, flag) 
+   3.3. t_reply_callid(callid, cseq, code, reason) 
+   3.4. t_flush_flags() 
+   3.5. t_is_failure_route() 
+   3.6. t_is_branch_route() 
+   3.7. t_is_reply_route() 
+   3.8. t_suspend() 
+   3.9. t_continue(tindex, tlabel, rtname) 
 
 3.1.  t_cancel_branches(which)
 
-   Cancel branches of an active SIP transaction. The function can be
+   Cancel  branches  of  an  active  SIP transaction. The function can be
    called for a SIP reply that will identify the current branch.
 
    Parameter can be:.
@@ -210,9 +211,9 @@ if (t_reply_callid("123qaz", "5", "458", "Replied remotely")) {
 
 3.4.  t_flush_flags()
 
-   Flush the flags from current SIP message into the already created
-   transaction. It make sense only in routing block if the transaction was
-   created via t_newtran() and the flags have been altered since.
+   Flush  the  flags  from  current  SIP message into the already created
+   transaction.  It  make  sense only in routing block if the transaction
+   was created via t_newtran() and the flags have been altered since.
 
    This function can be used from ANY_ROUTE .
 
@@ -274,8 +275,8 @@ route[abc] {
 
 3.8.  t_suspend()
 
-   Suspend the execution of SIP request in a transaction. If transaction
-   was not created yet, it is created by this function. Returns true in
+   Suspend  the execution of SIP request in a transaction. If transaction
+   was  not  created yet, it is created by this function. Returns true in
    case of success and internal transaction index and label are available
    via $T(id_index) and $T(id_label).
 
@@ -292,15 +293,15 @@ if(t_suspend())
 
 3.9.  t_continue(tindex, tlabel, rtname)
 
-   Continue the execution of the transaction identified by tindex and
+   Continue  the  execution  of  the transaction identified by tindex and
    tlabel with the actions defined in route[rtname].
 
    Parameters:.
-     * tindex - internal index of transaction. Can be an integer or a
+     * tindex  -  internal  index  of transaction. Can be an integer or a
        pseudo-variable.
-     * tlabel - internal label of transaction. Can be an integer or a
+     * tlabel  -  internal  label  of transaction. Can be an integer or a
        pseudo-variable.
-     * rtname - the name of the route block to execute. Can be a static
+     * rtname  -  the name of the route block to execute. Can be a static
        string value or a dynamic string with pseudo-variables.
 
    This function can be used in ANY_ROUTE.
@@ -318,17 +319,18 @@ tcontinue('123', '456', 'MYROUTE');
      * $T_reply_last
      * $T_req(pv)
      * $T_rpl(pv)
+     * $T_reply_ruid
 
    Exported pseudo-variables are documented at
    http://www.kamailio.org/dokuwiki/.
 
 5. MI Commands
 
-   5.1. t_uac_dlg
-   5.2. t_uac_cancel
-   5.3. t_hash
-   5.4. t_reply
-   5.5. t_reply_callid
+   5.1. t_uac_dlg 
+   5.2. t_uac_cancel 
+   5.3. t_hash 
+   5.4. t_reply 
+   5.5. t_reply_callid 
 
 5.1.  t_uac_dlg
 
@@ -337,13 +339,13 @@ tcontinue('123', '456', 'MYROUTE');
    Parameters:
      * method - request method
      * RURI - request SIP URI
-     * NEXT HOP - next hop SIP URI (OBP); use “.� if no value.
-     * socket - local socket to be used for sending the request; use “.�
+     * NEXT HOP - next hop SIP URI (OBP); use "." if no value.
+     * socket  - local socket to be used for sending the request; use "."
        if no value.
      * headers - set of additional headers to be added to the request; at
-       least “From� and “To� headers must be specify)
-     * body - (optional, may not be present) request body (if present,
-       requires the “Content-Type� and “Content-length� headers)
+       least "From" and "To" headers must be specify)
+     * body  -  (optional,  may not be present) request body (if present,
+       requires the "Content-Type" and "Content-length" headers)
 
 5.2.  t_uac_cancel
 
@@ -367,12 +369,13 @@ tcontinue('123', '456', 'MYROUTE');
    Parameters:
      * code - reply code
      * reason - reason phrase.
-     * trans_id - transaction identifier (has the hash_entry:label format)
+     * trans_id   -  transaction  identifier  (has  the  hash_entry:label
+       format)
      * to_tag - To tag to be added to TO header
      * new_headers - extra headers to be appended to the reply; use a dot
-       (“.�) char only if there are no headers;
-     * body - (optional, may not be present) reply body (if present,
-       requires the “Content-Type� and “Content-length� headers)
+       (".") char only if there are no headers;
+     * body  -  (optional,  may  not  be present) reply body (if present,
+       requires the "Content-Type" and "Content-length" headers)
 
 5.5.  t_reply_callid
 
@@ -385,9 +388,9 @@ tcontinue('123', '456', 'MYROUTE');
      * cseq - SIP CSeq header
      * to_tag - To tag to be added to TO header
      * new_headers - extra headers to be appended to the reply; use a dot
-       (“.�) char only if there are no headers;
-     * body - (optional, may not be present) reply body (if present,
-       requires the “Content-Type� and “Content-length� headers)
+       (".") char only if there are no headers;
+     * body  -  (optional,  may  not  be present) reply body (if present,
+       requires the "Content-Type" and "Content-length" headers)
 
 6. Statistics
 
@@ -403,8 +406,8 @@ tcontinue('123', '456', 'MYROUTE');
    6.10. 6xx_transactions
    6.11. inuse_transactions
 
-   Exported statistics are listed in the next sections. All statistics
-   except “inuse_transactions� can be reset.
+   Exported  statistics  are  listed in the next sections. All statistics
+   except "inuse_transactions" can be reset.
 
 6.1. received_replies
 

+ 3 - 0
modules/tmx/doc/tmx_admin.xml

@@ -355,6 +355,9 @@ tcontinue('123', '456', 'MYROUTE');
 			<listitem><para>
 				<emphasis>$T_rpl(pv)</emphasis>
 			</para></listitem>
+			<listitem><para>
+				<emphasis>$T_reply_ruid</emphasis>
+			</para></listitem>
 		</itemizedlist>
 		<para>
 		Exported pseudo-variables are documented at &kamwikilink;.

+ 37 - 0
modules/tmx/t_var.c

@@ -417,6 +417,42 @@ int pv_get_tm_branch_idx(struct sip_msg *msg, pv_param_t *param,
 	return 0;
 }
 
+int pv_get_tm_reply_ruid(struct sip_msg *msg, pv_param_t *param,
+		pv_value_t *res)
+{
+	struct cell *t;
+	int branch;
+
+	if(msg==NULL || res==NULL)
+		return -1;
+
+	/* first get the transaction */
+	if (_tmx_tmb.t_check( msg , 0 )==-1) return -1;
+	if ( (t=_tmx_tmb.t_gett())==0) {
+		/* no T */
+		res->rs = _empty_str;
+	} else {
+		switch (get_route_type()) {
+			case FAILURE_ROUTE:
+			case BRANCH_FAILURE_ROUTE:
+				/* use the reason of the winning reply */
+				if ( (branch=_tmx_tmb.t_get_picked_branch())<0 ) {
+					LM_CRIT("no picked branch (%d) for a final response"
+							" in MODE_ONFAILURE\n", branch);
+					return -1;
+				}
+				res->rs = t->uac[branch].ruid;
+				break;
+			default:
+				LM_ERR("unsupported route_type %d\n", get_route_type());
+				return -1;
+		}
+	}
+	LM_DBG("reply ruid is [%.*s]\n", res->rs.len, res->rs.s);
+	res->flags = PV_VAL_STR;
+	return 0;
+}
+
 int pv_get_tm_reply_code(struct sip_msg *msg, pv_param_t *param,
 		pv_value_t *res)
 {
@@ -449,6 +485,7 @@ int pv_get_tm_reply_code(struct sip_msg *msg, pv_param_t *param,
 				code = msg->first_line.u.reply.statuscode;
 				break;
 			case FAILURE_ROUTE:
+			case BRANCH_FAILURE_ROUTE:
 				/* use the status of the winning reply */
 				if ( (branch=_tmx_tmb.t_get_picked_branch())<0 ) {
 					LM_CRIT("no picked branch (%d) for a final response"

+ 2 - 0
modules/tmx/t_var.h

@@ -38,6 +38,8 @@ int pv_parse_t_var_name(pv_spec_p sp, str *in);
 
 int pv_get_tm_branch_idx(struct sip_msg *msg, pv_param_t *param,
 		pv_value_t *res);
+int pv_get_tm_reply_ruid(struct sip_msg *msg, pv_param_t *param,
+		pv_value_t *res);
 int pv_get_tm_reply_code(struct sip_msg *msg, pv_param_t *param,
 		pv_value_t *res);
 int pv_get_tm_reply_reason(struct sip_msg *msg, pv_param_t *param,

+ 3 - 0
modules/tmx/tmx_mod.c

@@ -123,6 +123,9 @@ static pv_export_t mod_pvs[] = {
 	{ {"T_branch_idx", sizeof("T_branch_idx")-1}, PVT_OTHER,
 		pv_get_tm_branch_idx, 0,
 		 0, 0, 0, 0 },
+	{ {"T_reply_ruid", sizeof("T_reply_ruid")-1}, PVT_OTHER,
+		pv_get_tm_reply_ruid, 0,
+		 0, 0, 0, 0 },
 	{ {"T_reply_code", sizeof("T_reply_code")-1}, PVT_OTHER,
 		pv_get_tm_reply_code, 0,
 		 0, 0, 0, 0 },

+ 4 - 2
modules/usrloc/udomain.c

@@ -648,7 +648,7 @@ urecord_t* db_load_urecord_by_ruid(db1_con_t* _c, udomain_t* _d, str *_ruid)
 	db_row_t *row;
 	str contact;
 	str aor;
-	char aorbuf[512];
+	static char aorbuf[512];
 	str domain;
 
 	urecord_t* r;
@@ -1031,7 +1031,7 @@ int get_urecord(udomain_t* _d, str* _aor, struct urecord** _r)
  * \param _ruid record internal unique id
  * \param _r store pointer to location record
  * \param _c store pointer to contact structure
- * \return 0 if a record was found, 1 if nothing could be found
+ * \return 0 if a record was found, -1 if nothing could be found
  */
 int get_urecord_by_ruid(udomain_t* _d, unsigned int _aorhash,
 		str *_ruid, struct urecord** _r, struct ucontact** _c)
@@ -1057,6 +1057,7 @@ int get_urecord_by_ruid(udomain_t* _d, unsigned int _aorhash,
 						*_c = c;
 						return 0;
 					}
+					c = c->next;
 				}
 			}
 			r = r->next;
@@ -1074,6 +1075,7 @@ int get_urecord_by_ruid(udomain_t* _d, unsigned int _aorhash,
 						*_c = c;
 						return 0;
 					}
+					c = c->next;
 				}
 			}
 		}

+ 3 - 3
modules/xprint/xp_lib.c

@@ -700,7 +700,7 @@ static int xl_get_branch(struct sip_msg *msg, str *res, str *hp, int hi, int hf)
 
 
 	init_branch_iterator();
-	branch.s = next_branch(&branch.len, &q, 0, 0, 0, 0, 0);
+	branch.s = next_branch(&branch.len, &q, 0, 0, 0, 0, 0, 0);
 	if (!branch.s) {
 		return xl_get_null(msg, res, hp, hi, hf);
 	}
@@ -731,7 +731,7 @@ static int xl_get_branches(struct sip_msg *msg, str *res, str *hp, int hi, int h
 	cnt = len = 0;
 
 	init_branch_iterator();
-	while ((uri.s = next_branch(&uri.len, &q, 0, 0, 0, 0, 0)))
+	while ((uri.s = next_branch(&uri.len, &q, 0, 0, 0, 0, 0, 0)))
 	{
 		cnt++;
 		len += uri.len;
@@ -756,7 +756,7 @@ static int xl_get_branches(struct sip_msg *msg, str *res, str *hp, int hi, int h
 	p = local_buf;
 
 	init_branch_iterator();
-	while ((uri.s = next_branch(&uri.len, &q, 0, 0, 0, 0, 0)))
+	while ((uri.s = next_branch(&uri.len, &q, 0, 0, 0, 0, 0, 0)))
 	{
 		if (i)
 		{

+ 1 - 0
route.h

@@ -53,6 +53,7 @@
 #define ERROR_ROUTE   (1 << 5)
 #define LOCAL_ROUTE   (1 << 6)
 #define CORE_ONREPLY_ROUTE (1 << 7)
+#define BRANCH_FAILURE_ROUTE (1 << 8)
 #define ONREPLY_ROUTE (TM_ONREPLY_ROUTE|CORE_ONREPLY_ROUTE)
 #define EVENT_ROUTE   REQUEST_ROUTE
 #define ANY_ROUTE     (0xFFFFFFFF)

+ 1 - 1
script_cb.c

@@ -53,7 +53,7 @@
 #include "mem/mem.h"
 
 /* Number of cb types = last cb type */
-#define SCRIPT_CB_NUM	EVENT_CB_TYPE
+#define SCRIPT_CB_NUM	(MAX_CB_TYPE-1)
 
 static struct script_cb *pre_script_cb[SCRIPT_CB_NUM];
 static struct script_cb *post_script_cb[SCRIPT_CB_NUM];

+ 2 - 2
script_cb.h

@@ -49,14 +49,14 @@ typedef int (cb_function)(struct sip_msg *msg, unsigned int flags, void *param);
  */
 enum script_cb_flag { REQUEST_CB=1, FAILURE_CB=2, ONREPLY_CB=4,
 			BRANCH_CB=8, ONSEND_CB=16, ERROR_CB=32,
-			LOCAL_CB=64, EVENT_CB=128 };
+			LOCAL_CB=64, EVENT_CB=128, BRANCH_FAILURE_CB=256 };
 
 /* Callback types used for executing the callbacks.
  * Keep in sync with script_cb_flag!!!
  */
 enum script_cb_type { REQUEST_CB_TYPE=1, FAILURE_CB_TYPE, ONREPLY_CB_TYPE,
 			BRANCH_CB_TYPE, ONSEND_CB_TYPE, ERROR_CB_TYPE,
-			LOCAL_CB_TYPE, EVENT_CB_TYPE };
+			LOCAL_CB_TYPE, EVENT_CB_TYPE, BRANCH_FAILURE_CB_TYPE, MAX_CB_TYPE };
 
 struct script_cb{
 	cb_function *cbf;

+ 3 - 3
select_core.c

@@ -1621,7 +1621,7 @@ int select_branch_uri(str* res, select_t* s, struct sip_msg* msg) {
 		char *c;
 		init_branch_iterator();
 		len = 0;
-		while ((c = next_branch(&l, &q, &dst_uri, 0, 0, 0, 0))) {
+		while ((c = next_branch(&l, &q, &dst_uri, 0, 0, 0, 0, 0))) {
 
 			if (s->params[SEL_POS].v.i & SEL_BRANCH_DST_URI) {
 				l = dst_uri.len;
@@ -1645,7 +1645,7 @@ int select_branch_uri(str* res, select_t* s, struct sip_msg* msg) {
 		init_branch_iterator();
 		res->len = 0;
 		n = 0;
-		while ((c = next_branch(&l, &q, &dst_uri, 0, 0, 0, 0))) {
+		while ((c = next_branch(&l, &q, &dst_uri, 0, 0, 0, 0, 0))) {
 			if (s->params[SEL_POS].v.i & SEL_BRANCH_DST_URI) {
 				l = dst_uri.len;
 				c = dst_uri.s;
@@ -1687,7 +1687,7 @@ int select_branch_uri(str* res, select_t* s, struct sip_msg* msg) {
 		if (n < 0 || n >= nr_branches) 
 			return -1;
 		init_branch_iterator();
-		for (; (c = next_branch(&l, &q, &dst_uri, 0, 0, 0, 0)) && n; n--);
+		for (; (c = next_branch(&l, &q, &dst_uri, 0, 0, 0, 0, 0)) && n; n--);
 		if (!c) return 1;
 		
 		if (s->params[SEL_POS].v.i & SEL_BRANCH_DST_URI) {