Browse Source

- e2ecancel is sent hop by hop (closes SER-68)
- delayed err reply: delay error replies from t_relay() until end of script: if
script hasn't replied or dropped the trans., send reply(tm_errno).
- automatic 100 reply generation for invites can be turned on/off via
a tm global parameter or on a per transaction basis (auto_inv_100,
t_set_auto_inv_100()) (closes SER-199)
- different error replies for too many branches, resolve or send error
- if error adding branches, reply with the lowest error
- membar_write before increasing nr_of_outgoings (branches) and membar_depends
in which_cancel (should allow lockless which_cancel, with the possible race
of not canceling a branch that is being created in the same time, OTOH
this happens even if locks are used)

Andrei Pelinescu-Onciul 18 years ago
parent
commit
b4a4494d15

+ 1 - 0
modules/tm/h_table.c

@@ -63,6 +63,7 @@
 #include "fix_lumps.h" /* free_via_clen_lump */
 #include "timer.h"
 
+
 static enum kill_reason kr;
 
 /* pointer to the big table where all the transaction data

+ 8 - 2
modules/tm/h_table.h

@@ -116,8 +116,14 @@ void unlock_hash(int i);
             from t_release_transaction
    REQ_EXIST means that this request is a retransmission which does not
             affect transactional state
+   REQ_ERR_DELAYED mean that tm wants to send  reply(ser_error) but it
+            delayed it to end-of-script to allow it to be overriden.
+            If this is set and all of the above flag are not => send reply
+            on end of script. If any of the above flags is set, do not
+            send (especially REQ_RPLD and REQ_RLSD).
 */
-enum kill_reason { REQ_FWDED=1, REQ_RPLD=2, REQ_RLSD=4, REQ_EXIST=8 };
+enum kill_reason { REQ_FWDED=1, REQ_RPLD=2, REQ_RLSD=4, REQ_EXIST=8,
+				   REQ_ERR_DELAYED=16 };
 
 
 /* #define F_RB_T_ACTIVE		0x01  (obsolete) fr or retr active */
@@ -217,7 +223,7 @@ struct totag_elem {
 
 #define T_IN_AGONY (1<<5) /* set if waiting to die (delete timer)
                              TODO: replace it with del on unref */
-
+#define T_AUTO_INV_100 (1<<6) /* send an 100 reply automatically  to inv. */
 #define T_DONT_FORK   (T_CANCELED|T_6xx)
 
 /* unsigned short should be enough for a retr. timer: max. 65535 ticks =>

+ 4 - 2
modules/tm/t_cancel.c

@@ -64,12 +64,14 @@
 void which_cancel( struct cell *t, branch_bm_t *cancel_bm )
 {
 	int i;
+	int branches_no;
 	
 	*cancel_bm=0;
-	for( i=0 ; i<t->nr_of_outgoings ; i++ ) {
+	branches_no=t->nr_of_outgoings;
+	membar_depends(); 
+	for( i=0 ; i<branches_no ; i++ ) {
 		if (should_cancel_branch(t, i, 1)) 
 			*cancel_bm |= 1<<i ;
-
 	}
 }
 

+ 26 - 6
modules/tm/t_funcs.c

@@ -48,6 +48,9 @@
  *              t_relay_to releases the transaction if t_forward_non_ack
  *              fails and t_kill fails or this is a failed replication (andrei)
  *  2007-05-02  t_relay_to() uses now t_forward_cancel for cancels (andrei)
+ *  2007-06-05  delay t_relay() replies till script end so that they can be
+ *               overwritten by the script user; generate 100 automatically
+ *               only if T_NO_100 is not set (andrei)
  */
 
 #include <limits.h>
@@ -67,6 +70,10 @@
 #include "config.h"
 #include "t_stats.h"
 
+/* if defined t_relay* error reply generation will be delayed till script
+ * end (this allows the script writter to send its own error reply) */
+#define TM_DELAYED_REPLY
+
 /* fr_timer AVP specs */
 static int     fr_timer_avp_type = 0;
 static int_str fr_timer_avp = {0};
@@ -77,6 +84,10 @@ static int_str fr_inv_timer_avp = {0};
 static str     fr_inv_timer_str;
 static int     fr_inv_timer_index = 0;
 
+int tm_error = 0; /* delayed tm error */
+
+int tm_auto_inv_100=1; /* automatically send 100 to an INVITE, default on*/
+struct msgid_var user_auto_inv_100;
 
 /* ----------------------------------------------------- */
 int send_pr_buffer(	struct retr_buf *rb, void *buf, int len
@@ -172,7 +183,7 @@ void put_on_wait(  struct cell  *Trans  )
 
 /* WARNING: doesn't work from failure route (deadlock, uses t_reply =>
  *  tries to get the reply lock again) */
-static int kill_transaction( struct cell *trans )
+int kill_transaction( struct cell *trans, int error )
 {
 	char err_buffer[128];
 	int sip_err;
@@ -184,7 +195,7 @@ static int kill_transaction( struct cell *trans )
 		want to put the forking burden on upstream client;
 		however, it may fail too due to lack of memory */
 
-	ret=err2reason_phrase( ser_error, &sip_err,
+	ret=err2reason_phrase(error, &sip_err,
 		err_buffer, sizeof(err_buffer), "TM" );
 	if (ret>0) {
 		reply_ret=t_reply( trans, trans->uas.request, 
@@ -206,13 +217,15 @@ int t_relay_to( struct sip_msg  *p_msg , struct proxy_l *proxy, int proto,
 {
 	int ret;
 	int new_tran;
-	int reply_ret;
 	/* struct hdr_field *hdr; */
 	struct cell *t;
 	struct dest_info dst;
 	unsigned short port;
 	str host;
 	short comp;
+#ifndef TM_DELAYED_REPLY
+	int reply_ret;
+#endif
 
 	ret=0;
 	
@@ -280,7 +293,7 @@ int t_relay_to( struct sip_msg  *p_msg , struct proxy_l *proxy, int proto,
 
 	/* INVITE processing might take long, particularly because of DNS
 	   look-ups -- let upstream know we're working on it */
-	if (p_msg->REQ_METHOD==METHOD_INVITE )
+	if (p_msg->REQ_METHOD==METHOD_INVITE && (t->flags&T_AUTO_INV_100))
 	{
 		DBG( "SER: new INVITE\n");
 		if (!t_reply( t, p_msg , 100 ,
@@ -295,8 +308,14 @@ handle_ret:
 		DBG( "ERROR:tm:t_relay_to:  t_forward_nonack returned error \n");
 		/* we don't want to pass upstream any reply regarding replicating
 		 * a request; replicated branch must stop at us*/
-		if (!replicate) {
-			reply_ret=kill_transaction( t );
+		if (likely(!replicate)) {
+#ifdef TM_DELAYED_REPLY
+			/* current error in tm_error */
+			tm_error=ser_error;
+			set_kr(REQ_ERR_DELAYED);
+			DBG("%d error reply generation delayed \n", ser_error);
+#else
+			reply_ret=kill_transaction( t, ser_error );
 			if (reply_ret>0) {
 				/* we have taken care of all -- do nothing in
 			  	script */
@@ -308,6 +327,7 @@ handle_ret:
 					"on error failed\n");
 				t_release_transaction(t);
 			}
+#endif /* TM_DELAYED_REPLY */
 		}else{
 			t_release_transaction(t); /* kill it  silently */
 		}

+ 4 - 1
modules/tm/t_funcs.h

@@ -73,8 +73,10 @@ struct timer;
 struct entry;
 struct cell;
 
+extern int tm_error; /* delayed tm error */
 extern int noisy_ctimer;
-
+extern int tm_auto_inv_100; /*automatically send 100 to an INVITE, default on*/
+extern struct msgid_var user_auto_inv_100;
 
 /* default names for timer's AVPs  */
 #define FR_TIMER_AVP      "callee_fr_timer"
@@ -195,5 +197,6 @@ void put_on_wait(  struct cell  *Trans  );
 int t_relay_to( struct sip_msg  *p_msg ,
 	struct proxy_l *proxy, int proto, int replicate ) ;
 
+int kill_transaction( struct cell *trans, int error );
 #endif
 

+ 57 - 24
modules/tm/t_fwd.c

@@ -71,6 +71,7 @@
  *              incomming one. (it can be disabled with reparse_invite=0) (Miklos)
  *              t_relay_cancel() introduced -- can be used to relay CANCELs
  *              at the beginning of the script. (Miklos)
+ * 2007-06-04  running transaction are canceled hop by hop (andrei)
  */
 
 #include "defs.h"
@@ -105,6 +106,8 @@
 #include "../../dst_blacklist.h"
 #endif
 
+/* cancel hop by hop */
+#define E2E_CANCEL_HOP_BY_HOP
 
 int unmatched_cancel=UM_CANCEL_STATEFULL;
 
@@ -254,7 +257,9 @@ int add_blind_uac( /*struct cell *t*/ )
 	}
 	/* make sure it will be replied */
 	t->flags |= T_NOISY_CTIMER_FLAG;
-	t->nr_of_outgoings++;
+	membar_write(); /* to allow lockless which_cancel() we want to be sure 
+					   all the writes finished before updating branch number*/
+	t->nr_of_outgoings=(branch+1);
 	/* start FR timer -- protocol set by default to PROTO_NONE,
        which means retransmission timer will not be started
     */
@@ -271,7 +276,8 @@ int add_blind_uac( /*struct cell *t*/ )
 
 /* introduce a new uac to transaction; returns its branch id (>=0)
    or error (<0); it doesn't send a message yet -- a reply to it
-   might interfere with the processes of adding multiple branches
+   might interfere with the processes of adding multiple branches;
+   On error returns <0 & sets ser_error to the same value
 */
 int add_uac( struct cell *t, struct sip_msg *request, str *uri, str* next_hop,
 	struct proxy_l *proxy, int proto )
@@ -285,7 +291,7 @@ int add_uac( struct cell *t, struct sip_msg *request, str *uri, str* next_hop,
 	branch=t->nr_of_outgoings;
 	if (branch==MAX_BRANCHES) {
 		LOG(L_ERR, "ERROR: add_uac: maximum number of branches exceeded\n");
-		ret=E_CFG;
+		ret=ser_error=E_TOO_MANY_BRANCHES;
 		goto error;
 	}
 
@@ -316,7 +322,7 @@ int add_uac( struct cell *t, struct sip_msg *request, str *uri, str* next_hop,
 						next_hop ? next_hop: uri, proto)==0)
 #endif
 		{
-			ret=E_BAD_ADDRESS;
+			ret=ser_error=E_BAD_ADDRESS;
 			goto error;
 		}
 	}
@@ -345,7 +351,10 @@ int add_uac( struct cell *t, struct sip_msg *request, str *uri, str* next_hop,
 	t->uac[branch].uri.s=t->uac[branch].request.buffer+
 		request->first_line.u.request.method.len+1;
 	t->uac[branch].uri.len=uri->len;
-	t->nr_of_outgoings++;
+	membar_write(); /* to allow lockless ops (e.g. which_cancel()) we want
+					   to be sure everything above is fully written before
+					   updating branches no. */
+	t->nr_of_outgoings=(branch+1);
 
 	/* update stats */
 	if (proxy){
@@ -366,7 +375,8 @@ error:
  *  new ip address (if the dns name resolves to more ips). If no more
  *   ips are found => returns -1.
  *  returns its branch id (>=0)
-   or error (<0); it doesn't send a message yet -- a reply to it
+   or error (<0) and sets ser_error if needed; it doesn't send a message 
+   yet -- a reply to it
    might interfere with the processes of adding multiple branches
    if lock_replies is 1 replies will be locked for t until the new branch
    is added (to prevent add branches races). Use 0 if the reply lock is
@@ -400,7 +410,8 @@ int add_uac_dns_fallback( struct cell *t, struct sip_msg* msg,
 							"branches exceeded\n");
 				if (lock_replies)
 					UNLOCK_REPLIES(t);
-				return E_CFG;
+					ret=ser_error=E_TOO_MANY_BRANCHES;
+				return ret;
 			}
 			/* copy the dns handle into the new uac */
 			dns_srv_handle_cpy(&t->uac[t->nr_of_outgoings].dns_h,
@@ -489,7 +500,10 @@ error:
 void e2e_cancel( struct sip_msg *cancel_msg, 
 	struct cell *t_cancel, struct cell *t_invite )
 {
-	branch_bm_t cancel_bm, tmp_bm;
+	branch_bm_t cancel_bm;
+#ifndef E2E_CANCEL_HOP_BY_HOP
+	branch_bm_t tmp_bm;
+#endif
 	int i;
 	int lowest_error;
 	int ret;
@@ -515,9 +529,21 @@ void e2e_cancel( struct sip_msg *cancel_msg,
 	
 	/* determine which branches to cancel ... */
 	which_cancel( t_invite, &cancel_bm );
-	t_cancel->nr_of_outgoings=t_invite->nr_of_outgoings;
 	/* fix label -- it must be same for reply matching */
 	t_cancel->label=t_invite->label;
+#ifdef E2E_CANCEL_HOP_BY_HOP
+	for (i=0; i<t_invite->nr_of_outgoings; i++)
+		if (cancel_bm & (1<<i)) {
+			/* it's safe to get the reply lock since e2e_cancel is
+			 * called with the cancel as the "current" transaction so
+			 * at most t_cancel REPLY_LOCK is held in this process =>
+			 * no deadlock possibility */
+			ret=cancel_branch(t_invite, i, F_CANCEL_B_FAKE_REPLY);
+			if (ret<0) cancel_bm &= ~(1<<i);
+			if (ret<lowest_error) lowest_error=ret;
+		}
+#else
+	t_cancel->nr_of_outgoings=t_invite->nr_of_outgoings;
 	/* ... and install CANCEL UACs */
 	for (i=0; i<t_invite->nr_of_outgoings; i++)
 		if ((cancel_bm & (1<<i)) && (t_invite->uac[i].last_received>=100)) {
@@ -564,6 +590,7 @@ void e2e_cancel( struct sip_msg *cancel_msg,
 			}
 		}
 	}
+#endif /*E2E_CANCEL_HOP_BY_HOP */
 
 	/* if error occurred, let it know upstream (final reply
 	   will also move the transaction on wait state
@@ -571,7 +598,7 @@ void e2e_cancel( struct sip_msg *cancel_msg,
 	if (lowest_error<0) {
 		LOG(L_ERR, "ERROR: cancel error\n");
 		/* if called from failure_route, make sure that the unsafe version
-		 * is called (we are already hold the reply mutex for the cancel
+		 * is called (we are already holding the reply mutex for the cancel
 		 * transaction).
 		 */
 		if ((rmode==MODE_ONFAILURE) && (t_cancel==get_t()))
@@ -592,8 +619,8 @@ void e2e_cancel( struct sip_msg *cancel_msg,
 		else
 			t_reply( t_cancel, cancel_msg, 200, CANCELING );
 	} else {
-		/* if the transaction exists, but there is no more pending
-		   branch, tell upstream we're done
+		/* if the transaction exists, but there are no more pending
+		   branches, tell upstream we're done
 		*/
 		DBG("DEBUG: e2e_cancel: e2e cancel -- no more pending branches\n");
 		/* if called from failure_route, make sure that the unsafe version
@@ -763,24 +790,24 @@ int t_forward_nonack( struct cell *t, struct sip_msg* p_msg ,
 	/* make -Wall happy */
 	current_uri.s=0;
 
-	set_kr(REQ_FWDED);
+	if (t->flags & T_CANCELED){
+		DBG("t_forward_non_ack: no forwarding on a canceled transaction\n");
+		ser_error=E_CANCELED;
+		return -1;
+	}
 	if (p_msg->REQ_METHOD==METHOD_CANCEL) { 
 		t_invite=t_lookupOriginalT(  p_msg );
 		if (t_invite!=T_NULL_CELL) {
 			e2e_cancel( p_msg, t, t_invite );
 			UNREF(t_invite);
+			set_kr(REQ_FWDED);
 			return 1;
 		}
 	}
-	if (t->flags & T_CANCELED){
-		DBG("t_forward_non_ack: no forwarding on canceled branch\n");
-		ser_error=E_CANCELED;
-		return -1;
-	}
 
 	backup_si = p_msg->force_send_socket;
 	/* if no more specific error code is known, use this */
-	lowest_ret=E_BUG;
+	lowest_ret=E_UNSPEC;
 	/* branches added */
 	added_branches=0;
 	/* branch to begin with */
@@ -802,11 +829,12 @@ int t_forward_nonack( struct cell *t, struct sip_msg* p_msg ,
 	*/
 	if (first_branch==0) {
 		try_new=1;
-		branch_ret=add_uac( t, p_msg, GET_RURI(p_msg), GET_NEXT_HOP(p_msg), proxy, proto );
+		branch_ret=add_uac( t, p_msg, GET_RURI(p_msg), GET_NEXT_HOP(p_msg),
+							proxy, proto );
 		if (branch_ret>=0) 
 			added_branches |= 1<<branch_ret;
 		else
-			lowest_ret=branch_ret;
+			lowest_ret=MIN_int(lowest_ret, branch_ret);
 	} else try_new=0;
 
 	init_branch_iterator();
@@ -823,7 +851,7 @@ int t_forward_nonack( struct cell *t, struct sip_msg* p_msg ,
 		if (branch_ret>=0) 
 			added_branches |= 1<<branch_ret;
 		else
-			lowest_ret=branch_ret;
+			lowest_ret=MIN_int(lowest_ret, branch_ret);
 	}
 	/* consume processed branches */
 	clear_branches();
@@ -839,9 +867,10 @@ int t_forward_nonack( struct cell *t, struct sip_msg* p_msg ,
 			return -1;
 		}
 		LOG(L_ERR, "ERROR: t_forward_nonack: failure to add branches\n");
+		ser_error=lowest_ret;
 		return lowest_ret;
 	}
-
+	ser_error=0; /* clear branch adding errors */
 	/* send them out now */
 	success_branch=0;
 	lock_replies= ! ((rmode==MODE_ONFAILURE) && (t==get_t()));
@@ -858,10 +887,14 @@ int t_forward_nonack( struct cell *t, struct sip_msg* p_msg ,
 		}
 	}
 	if (success_branch<=0) {
-		ser_error=E_SEND;
+		if (ser_error==0)
+				ser_error=E_SEND;
+		/* else return the last error (?) */
 		/* the caller should take care and delete the transaction */
 		return -1;
 	}
+	ser_error=0; /* clear branch send errors, we have overall success */
+	set_kr(REQ_FWDED);
 	return 1;
 }
 

+ 27 - 2
modules/tm/t_lookup.c

@@ -90,6 +90,10 @@
  *               needed (andrei)
  * 2007-03-17  added callbacks for retransmitted request, ack to negative 
  *              replies and replies to local transactions (andrei)
+ * 2007-06-01  support for different retransmissions intervals per transaction;
+ *             added maximum inv. and non-inv. transaction life time (andrei)
+ * 2007-06-05  added delayed error reply support in t_unref;
+*              added support for turning off 100 repl. sending on inv. (andrei)
  * 2007-06-01  support for different retransmissions intervals per transaction;
  *             added maximum inv. and non-inv. transaction life time (andrei)
  */
@@ -174,6 +178,8 @@ static struct cell *T;
 */
 unsigned int     global_msg_id;
 
+
+
 struct cell *get_t() { return T; }
 void set_t(struct cell *t) { T=t; }
 void init_t() {global_msg_id=0; set_t(T_UNDEFINED);}
@@ -1071,6 +1077,7 @@ static inline void init_new_t(struct cell *new_cell, struct sip_msg *p_msg)
 	struct sip_msg *shm_msg;
 	unsigned int timeout; /* avp timeout gets stored here (in s) */
 	ticks_t lifetime;
+	int v;
 
 	shm_msg=new_cell->uas.request;
 	new_cell->from.s=shm_msg->from->name.s;
@@ -1087,6 +1094,11 @@ static inline void init_new_t(struct cell *new_cell, struct sip_msg *p_msg)
 	new_cell->method=new_cell->uas.request->first_line.u.request.method;
 	if (p_msg->REQ_METHOD==METHOD_INVITE){
 		new_cell->flags |= T_IS_INVITE_FLAG;
+		if (unlikely(v=get_msgid_val(user_auto_inv_100, p_msg->id, int)))
+			/* 1 = set, -1 = reset */
+			new_cell->flags|=T_AUTO_INV_100 & (!(v+1)-1);
+		else
+			new_cell->flags|=T_AUTO_INV_100 & ( !tm_auto_inv_100 -1);
 		lifetime=(ticks_t)get_msgid_val(user_inv_max_lifetime,
 												p_msg->id, int);
 		if (likely(lifetime==0))
@@ -1321,17 +1333,30 @@ int t_unref( struct sip_msg* p_msg  )
 		return -1;
 	if (p_msg->first_line.type==SIP_REQUEST){
 		kr=get_kr();
-		if (kr==0 
-				||(p_msg->REQ_METHOD==METHOD_ACK && !(kr & REQ_RLSD))) {
+		if (unlikely(kr == REQ_ERR_DELAYED)){
+			DBG("t_unref: delayed error reply generation(%d)\n", tm_error);
+			if (unlikely(rmode==MODE_ONFAILURE)){
+				BUG("tm: t_unref: called w/ kr=REQ_ERR_DELAYED in failure"
+						" route\n");
+			}else if (unlikely( kill_transaction(T, tm_error)<=0 )){
+				DBG("ERROR: t_unref: generation of a delayed stateful reply"
+						" failed\n");
+				t_release_transaction(T);
+			}
+		}else if ( unlikely (kr==0 ||(p_msg->REQ_METHOD==METHOD_ACK && 
+								!(kr & REQ_RLSD)))) {
 			LOG(L_WARN, "WARNING: script writer didn't release transaction\n");
 			t_release_transaction(T);
 		}
+		tm_error=0; /* clear it */
 	}
 	UNREF( T );
 	set_t(T_UNDEFINED);
 	return 1;
 }
 
+
+
 int t_get_trans_ident(struct sip_msg* p_msg, unsigned int* hash_index, unsigned int* label)
 {
     struct cell* t;

+ 2 - 0
modules/tm/t_lookup.h

@@ -51,6 +51,8 @@ extern unsigned int     global_msg_id;
 extern int ruri_matching;
 extern int via1_matching;
 
+
+
 void init_t();
 int init_rb( struct retr_buf *rb, struct sip_msg *msg );
 struct cell* t_lookupOriginalT( struct sip_msg* p_msg );

+ 34 - 0
modules/tm/tm.c

@@ -82,6 +82,8 @@
  *  2006-10-16  added a new param.: aggregate challenges (andrei)
  *  2007-05-28  two new params: reparse_invite, ac_extra_hdrs
  *              added w_t_relay_cancel() (Miklos)
+ *  2007-06-05  added t_set_auto_inv_100() and auto_inv_100 (param);
+ *               t_set_max_lifetime(), max_{non}inv_lifetime  (andrei)
  */
 
 
@@ -191,6 +193,7 @@ static int t_set_fr_inv(struct sip_msg* msg, char* fr_inv, char* foo);
 static int t_set_fr_all(struct sip_msg* msg, char* fr_inv, char* fr);
 static int w_t_set_retr(struct sip_msg* msg, char* retr_t1, char* retr_t2);
 static int w_t_set_max_lifetime(struct sip_msg* msg, char* inv, char* noninv);
+static int t_set_auto_inv_100(struct sip_msg* msg, char* on_off, char* foo);
 static int t_branch_timeout(struct sip_msg* msg, char*, char*);
 static int t_branch_replied(struct sip_msg* msg, char*, char*);
 static int t_any_timeout(struct sip_msg* msg, char*, char*);
@@ -288,6 +291,8 @@ static cmd_export_t cmds[]={
 			REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE },
 	{"t_set_max_lifetime", w_t_set_max_lifetime,      2, fixup_var_int_12,
 			REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE },
+	{"t_set_auto_inv_100", t_set_auto_inv_100,       1, fixup_var_int_1,
+													  REQUEST_ROUTE},
 	{"t_branch_timeout",  t_branch_timeout,         0, 0,  FAILURE_ROUTE},
 	{"t_branch_replied",  t_branch_replied,         0, 0,  FAILURE_ROUTE},
 	{"t_any_timeout",     t_any_timeout,            0, 0, 
@@ -339,6 +344,7 @@ static param_export_t params[]={
 	{"max_inv_lifetime",    PARAM_INT, &tm_max_inv_lifetime                  },
 	{"max_noninv_lifetime", PARAM_INT, &tm_max_noninv_lifetime               },
 	{"noisy_ctimer",        PARAM_INT, &noisy_ctimer                         },
+	{"auto_inv_100",        PARAM_INT, &tm_auto_inv_100                      },
 	{"uac_from",            PARAM_STRING, &uac_from                          },
 	{"unix_tx_timeout",     PARAM_INT, &tm_unix_tx_timeout                   },
 	{"restart_fr_on_each_reply", PARAM_INT, &restart_fr_on_each_reply        },
@@ -1214,6 +1220,34 @@ static int w_t_set_max_lifetime(struct sip_msg* msg, char* p1, char* p2)
 
 
 
+/* set automatically sending 100 replies on/off for the current or
+ * next to be created transaction */
+static int t_set_auto_inv_100(struct sip_msg* msg, char* p1, char* p2)
+{
+	int state;
+	struct cell* t;
+	
+	if (get_int_fparam(&state, msg, (fparam_t*)p1) < 0) return -1;
+	t=get_t();
+	/* in MODE_REPLY and MODE_ONFAILURE T will be set to current transaction;
+	 * in MODE_REQUEST T will be set only if the transaction was already
+	 * created; if not -> use the static variables */
+	if (!t || t==T_UNDEFINED ){
+		if (state)
+			set_msgid_val(user_auto_inv_100, msg->id, int, 1); /* set */
+		else
+			set_msgid_val(user_auto_inv_100, msg->id, int, -1); /* reset */
+	}else{
+		if (state)
+			t->flags|=T_AUTO_INV_100;
+		else
+			t->flags&=~T_AUTO_INV_100;
+	}
+	return 1;
+}
+
+
+
 /* script function, FAILURE_ROUTE only, returns true if the 
  * choosed "failure" branch failed because of a timeout, 
  * -1 otherwise */

+ 1 - 0
modules/tm/uac.c

@@ -254,6 +254,7 @@ static inline int t_uac_prepare(str* method, str* headers, str* body,
 	}
 	if (method->len==INVITE_LEN && memcmp(method->s, INVITE, INVITE_LEN)==0){
 		new_cell->flags |= T_IS_INVITE_FLAG;
+		new_cell->flags|=T_AUTO_INV_100 & (!tm_auto_inv_100 -1);
 		lifetime=tm_max_inv_lifetime;
 	}else
 		lifetime=tm_max_noninv_lifetime;