Browse code

t_drop_replies() script function is introduced. It can be used to drop the received replies in failure_route block, and disable all the previous replies to be selected again. Closes SER-301

Miklos Tirpak authored on 17/03/2008 10:39:51
Showing 7 changed files
... ...
@@ -653,4 +653,34 @@ if (method == CANCEL) {
653 653
 	</example>
654 654
     </section>
655 655
 
656
+    <section id="t_drop_replies">
657
+	<title>
658
+	    <function>t_drop_replies()</function>
659
+	</title>
660
+	<para>
661
+		Drops all the previously received replies in failure_route
662
+		block to make sure that none of them is picked up again.
663
+		Works only if a new branch is added to the transaction,
664
+		or it is explicitly replied in the script!
665
+	</para>
666
+	<example>
667
+	    <title><function>t_drop_replies()</function> usage</title>
668
+	    <programlisting>
669
+...
670
+failure_route[0]{ 
671
+	if (t_check_status("5[0-9][0-9]")){
672
+		# I do not like the 5xx responses,
673
+		# so I give another chance to "foobar.com",
674
+		# and I drop all the replies to make sure that
675
+		# they are not forwarded to the caller.
676
+		t_drop_replies();
677
+		
678
+		rewritehostport("foobar.com");
679
+		append_branch();
680
+		t_relay();
681
+	}
682
+} 
683
+	    </programlisting>
684
+	</example>
685
+    </section>
656 686
 </section>
... ...
@@ -207,6 +207,34 @@ int kill_transaction( struct cell *trans, int error )
207 207
 	}
208 208
 }
209 209
 
210
+/* unsafe version of kill_transaction() that works
211
+ * in failure route 
212
+ * WARNING: assumes that the reply lock is held!
213
+ */
214
+int kill_transaction_unsafe( struct cell *trans, int error )
215
+{
216
+	char err_buffer[128];
217
+	int sip_err;
218
+	int reply_ret;
219
+	int ret;
220
+
221
+	/*  we reply statefully and enter WAIT state since error might
222
+		have occurred in middle of forking and we do not
223
+		want to put the forking burden on upstream client;
224
+		however, it may fail too due to lack of memory */
225
+
226
+	ret=err2reason_phrase(error, &sip_err,
227
+		err_buffer, sizeof(err_buffer), "TM" );
228
+	if (ret>0) {
229
+		reply_ret=t_reply_unsafe( trans, trans->uas.request, 
230
+			sip_err, err_buffer);
231
+		/* t_release_transaction( T ); */
232
+		return reply_ret;
233
+	} else {
234
+		LOG(L_ERR, "ERROR: kill_transaction_unsafe: err2reason failed\n");
235
+		return -1;
236
+	}
237
+}
210 238
 
211 239
 
212 240
 /* WARNING: doesn't work from failure route (deadlock, uses t_reply => tries
... ...
@@ -204,5 +204,6 @@ int t_relay_to( struct sip_msg  *p_msg ,
204 204
 	struct proxy_l *proxy, int proto, int replicate ) ;
205 205
 
206 206
 int kill_transaction( struct cell *trans, int error );
207
+int kill_transaction_unsafe( struct cell *trans, int error );
207 208
 #endif
208 209
 
... ...
@@ -1444,8 +1444,8 @@ int t_unref( struct sip_msg* p_msg  )
1444 1444
 					" earlier for %p: %d (hex %x)\n",T, kr, kr);
1445 1445
 			t_release_transaction(T);
1446 1446
 		}
1447
-		tm_error=0; /* clear it */
1448 1447
 	}
1448
+	tm_error=0; /* clear it */
1449 1449
 	UNREF( T );
1450 1450
 	set_t(T_UNDEFINED);
1451 1451
 	return 1;
... ...
@@ -85,6 +85,7 @@
85 85
  * 2007-05-28: build_ack() constructs the ACK from the
86 86
  *             outgoing INVITE instead of the incomming one.
87 87
  *             (it can be disabled with reparse_invite=0) (Miklos)
88
+ * 2007-09-03: drop_replies() has been introduced (Miklos)
88 89
  * 2008-03-12  use cancel_b_method on 6xx (andrei)
89 90
  *
90 91
  */
... ...
@@ -870,6 +871,9 @@ int t_pick_branch(int inc_branch, int inc_code, struct cell *t, int *res_code)
870 870
 }
871 871
 
872 872
 
873
+/* flag indicating whether it is requested
874
+ * to drop the already saved replies or not */
875
+static unsigned char drop_replies;
873 876
 
874 877
 /* This is the neurological point of reply processing -- called
875 878
  * from within a REPLY_LOCK, t_should_relay_response decides
... ...
@@ -892,6 +896,7 @@ static enum rps t_should_relay_response( struct cell *Trans , int new_code,
892 892
 	int new_branch;
893 893
 	int inv_through;
894 894
 	int extra_flags;
895
+	int i;
895 896
 
896 897
 	/* note: this code never lets replies to CANCEL go through;
897 898
 	   we generate always a local 200 for CANCEL; 200s are
... ...
@@ -977,6 +982,7 @@ static enum rps t_should_relay_response( struct cell *Trans , int new_code,
977 977
 		Trans->flags&=~T_6xx; /* clear the 6xx flag , we want to 
978 978
 								 allow new branches from the failure route */
979 979
 
980
+		drop_replies = 0;
980 981
 		/* run ON_FAILURE handlers ( route and callbacks) */
981 982
 		if (unlikely(has_tran_tmcbs( Trans, TMCB_ON_FAILURE_RO|TMCB_ON_FAILURE)
982 983
 						|| Trans->on_negative )) {
... ...
@@ -987,6 +993,21 @@ static enum rps t_should_relay_response( struct cell *Trans , int new_code,
987 987
 						 	FL_REPLIED:0);
988 988
 			run_failure_handlers( Trans, Trans->uac[picked_branch].reply,
989 989
 									picked_code, extra_flags);
990
+			if (unlikely(drop_replies)) {
991
+				/* drop all the replies that we have already saved */
992
+				for (i=0; i<branch_cnt; i++) {
993
+					if (Trans->uac[i].reply &&
994
+					(Trans->uac[i].reply != FAKED_REPLY) &&
995
+					(Trans->uac[i].reply->msg_flags & FL_SHM_CLONE))
996
+						/* we have to drop the reply which is already in shm mem */
997
+						sip_msg_free(Trans->uac[i].reply);
998
+
999
+					Trans->uac[i].reply = 0;
1000
+				}
1001
+				/* make sure that the selected reply is not relayed even if
1002
+				there is not any new branch added -- should not happen */
1003
+				picked_branch = -1;
1004
+			}
990 1005
 		}
991 1006
 
992 1007
 		/* now reset it; after the failure logic, the reply may
... ...
@@ -1014,18 +1035,46 @@ static enum rps t_should_relay_response( struct cell *Trans , int new_code,
1014 1014
 		if (branch_cnt<Trans->nr_of_outgoings){
1015 1015
 			/* the new branches might be already "finished" => we
1016 1016
 			 * must use t_pick_branch again */
1017
-			new_branch=t_pick_branch(branch, new_code, Trans, &picked_code);
1017
+			new_branch=t_pick_branch((drop_replies==0)?
1018
+							branch :
1019
+							-1, /* make sure we do not pick
1020
+								the current branch */
1021
+						new_code,
1022
+						Trans,
1023
+						&picked_code);
1024
+
1018 1025
 			if (new_branch<0){
1019
-				if (new_branch==-2) { /* branches open yet */
1020
-					*should_store=1;
1021
-					*should_relay=-1;
1022
-					return RPS_STORE;
1026
+				if (likely(drop_replies==0)) {
1027
+					if (new_branch==-2) { /* branches open yet */
1028
+						*should_store=1;
1029
+						*should_relay=-1;
1030
+						return RPS_STORE;
1031
+					}
1032
+					/* error, use the old picked_branch */
1033
+				} else {
1034
+					if (new_branch==-2) { /* branches open yet */
1035
+						/* we are not allowed to relay the reply */
1036
+						*should_store=0;
1037
+						*should_relay=-1;
1038
+						return RPS_DISCARDED;
1039
+					} else {
1040
+						/* There are no open branches,
1041
+						and all the newly created branches failed
1042
+						as well. We are not allowed to send back
1043
+						the previously picked-up branch, thus,
1044
+						let us reply with an error instead. */
1045
+						goto branches_failed;
1046
+					}
1023 1047
 				}
1024
-				/* error, use the old picked_branch */
1025 1048
 			}else{
1026 1049
 				/* found a new_branch */
1027 1050
 				picked_branch=new_branch;
1028 1051
 			}
1052
+		} else if (unlikely(drop_replies)) {
1053
+			/* Either the script writer did not add new branches
1054
+			after calling t_drop_replies(), or tm was unable
1055
+			to add the new branches to the transaction. */
1056
+			goto branches_failed;
1029 1057
 		}
1030 1058
 
1031 1059
 		/* really no more pending branches -- return lowest code */
... ...
@@ -1057,6 +1106,20 @@ discard:
1057 1057
 	*should_store=0;
1058 1058
 	*should_relay=-1;
1059 1059
 	return RPS_DISCARDED;
1060
+
1061
+branches_failed:
1062
+	*should_store=0;
1063
+	*should_relay=-1;
1064
+	/* We have hopefully set tm_error in failure_route when
1065
+	the branches failed. If not, reply with E_UNSPEC */
1066
+	if ((kill_transaction_unsafe(Trans,
1067
+				    tm_error ? tm_error : E_UNSPEC)
1068
+				    ) <=0 ){
1069
+		LOG(L_ERR, "ERROR: t_should_relay_response: "
1070
+			"reply generation failed\n");
1071
+	}
1072
+	
1073
+	return RPS_COMPLETED;
1060 1074
 }
1061 1075
 
1062 1076
 /* Retransmits the last sent inbound reply.
... ...
@@ -1986,6 +2049,18 @@ error:
1986 1986
 	return -1;
1987 1987
 }
1988 1988
 
1989
+/* drops all the replies to make sure
1990
+ * that none of them is picked up again
1991
+ */
1992
+void t_drop_replies(void)
1993
+{
1994
+	/* It is too risky to free the replies that are in shm mem
1995
+	at the middle of failure_route block, because other functions might
1996
+	need them as well. And it can also happen that the current reply is not yet
1997
+	in shm mem, we are just going to clone it. So better to set a flag
1998
+	and check it after failure_route has ended. (Miklos) */
1999
+	drop_replies = 1;
2000
+}
1989 2001
 
1990 2002
 #if 0
1991 2003
 static int send_reply(struct cell *trans, unsigned int code, str* text, str* body, str* headers, str* to_tag)
... ...
@@ -146,6 +146,11 @@ void tm_init_tags();
146 146
 /* selects the branch for fwd-ing the reply */
147 147
 int t_pick_branch(int inc_branch, int inc_code, struct cell *t, int *res_code);
148 148
 
149
+/* drops all the replies to make sure
150
+ * that none of them is picked up again
151
+ */
152
+void t_drop_replies(void);
153
+
149 154
 extern const char* rpc_reply_doc[2];
150 155
 void rpc_reply(rpc_t* rpc, void* c);
151 156
 
... ...
@@ -208,6 +208,7 @@ static int t_any_timeout(struct sip_msg* msg, char*, char*);
208 208
 static int t_any_replied(struct sip_msg* msg, char*, char*);
209 209
 static int t_is_canceled(struct sip_msg* msg, char*, char*);
210 210
 static int t_grep_status(struct sip_msg* msg, char*, char*);
211
+static int w_t_drop_replies(struct sip_msg* msg, char* foo, char* bar);
211 212
 
212 213
 
213 214
 /* by default the fr timers avps are not set, so that the avps won't be
... ...
@@ -316,6 +317,9 @@ static cmd_export_t cmds[]={
316 316
 			REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE },
317 317
 	{"t_grep_status",     t_grep_status,            1, fixup_var_int_1, 
318 318
 			REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE },
319
+	{"t_drop_replies",    w_t_drop_replies,         0, 0,
320
+			FAILURE_ROUTE},
321
+
319 322
 
320 323
 	/* not applicable from the script */
321 324
 	{"register_tmcb",      (cmd_function)register_tmcb,     NO_SCRIPT,   0, 0},
... ...
@@ -1132,6 +1136,9 @@ inline static int _w_t_relay_to( struct sip_msg  *p_msg ,
1132 1132
 		}
1133 1133
 		if (t_forward_nonack(t, p_msg, proxy, PROTO_NONE)<=0 ) {
1134 1134
 			LOG(L_ERR, "ERROR: w_t_relay_to: t_relay_to failed\n");
1135
+			/* let us save the error code, we might need it later
1136
+			when the failure_route has finished (Miklos) */
1137
+			tm_error=ser_error;
1135 1138
 			return -1;
1136 1139
 		}
1137 1140
 		return 1;
... ...
@@ -1250,6 +1257,9 @@ inline static int w_t_relay( struct sip_msg  *p_msg ,
1250 1250
 		}
1251 1251
 		if (t_forward_nonack(t, p_msg, ( struct proxy_l *) 0, PROTO_NONE)<=0) {
1252 1252
 			LOG(L_ERR, "ERROR: w_t_relay (failure mode): forwarding failed\n");
1253
+			/* let us save the error code, we might need it later
1254
+			when the failure_route has finished (Miklos) */
1255
+			tm_error=ser_error;
1253 1256
 			return -1;
1254 1257
 		}
1255 1258
 		return 1;
... ...
@@ -1497,7 +1507,13 @@ int t_grep_status(struct sip_msg* msg, char* status, char* bar)
1497 1497
 	return -1;
1498 1498
 }
1499 1499
 
1500
-
1500
+/* drop all the existing replies in failure_route to make sure
1501
+ * that none of them is picked up again */
1502
+static int w_t_drop_replies(struct sip_msg* msg, char* foo, char* bar)
1503
+{
1504
+	t_drop_replies();
1505
+	return 1;
1506
+}
1501 1507
 
1502 1508
 static rpc_export_t tm_rpc[] = {
1503 1509
 	{"tm.cancel", rpc_cancel,   rpc_cancel_doc,   0},