소스 검색

bignag change -- lot of things primarily added in relationship with
refurbushing TM; see [sr] archive (2002-08-14) -- "ser update" and
"TM update" for a long list of details

Jiri Kuthan 23 년 전
부모
커밋
caf80ae64e
100개의 변경된 파일6091개의 추가작업 그리고 3314개의 파일을 삭제
  1. 26 0
      COPYING
  2. 1 1
      Makefile
  3. 12 23
      Makefile.defs
  4. 37 6
      README-MODULES
  5. 9 9
      TODO
  6. 21 0
      action.c
  7. 18 2
      cfg.lex
  8. 41 3
      cfg.y
  9. 14 5
      config.h
  10. 87 0
      dset.c
  11. 29 0
      dset.h
  12. 108 0
      error.c
  13. 16 0
      error.h
  14. 1 0
      etc/iptel.cfg
  15. 344 0
      fifo_server.c
  16. 34 0
      fifo_server.h
  17. 2 2
      flags.c
  18. 50 8
      forward.c
  19. 2 0
      forward.h
  20. 12 1
      globals.h
  21. 48 27
      main.c
  22. 2 3
      md5utils.c
  23. 6 0
      mem/mem.c
  24. 6 1
      mem/memtest.c
  25. 37 2
      mem/shm_mem.c
  26. 21 16
      mem/vq_malloc.c
  27. 1 0
      mem/vq_malloc.h
  28. 303 5
      modules/tm/README
  29. 0 15
      modules/tm/TODO
  30. 13 10
      modules/tm/config.h
  31. 53 0
      modules/tm/fix_lumps.h
  32. 96 63
      modules/tm/h_table.c
  33. 82 43
      modules/tm/h_table.h
  34. 0 148
      modules/tm/hash_func.c
  35. 0 17
      modules/tm/hash_func.h
  36. 25 12
      modules/tm/lock.c
  37. 1 1
      modules/tm/lock.h
  38. 0 30
      modules/tm/sh_malloc.h
  39. 59 1
      modules/tm/sip_msg.c
  40. 1 2
      modules/tm/sip_msg.h
  41. 97 0
      modules/tm/t_cancel.c
  42. 45 0
      modules/tm/t_cancel.h
  43. 20 0
      modules/tm/t_dlg.c
  44. 18 0
      modules/tm/t_dlg.h
  45. 0 95
      modules/tm/t_fork.c
  46. 0 30
      modules/tm/t_fork.h
  47. 166 171
      modules/tm/t_funcs.c
  48. 40 292
      modules/tm/t_funcs.h
  49. 336 329
      modules/tm/t_fwd.c
  50. 25 0
      modules/tm/t_fwd.h
  51. 9 7
      modules/tm/t_hooks.c
  52. 79 23
      modules/tm/t_hooks.h
  53. 305 386
      modules/tm/t_lookup.c
  54. 47 0
      modules/tm/t_lookup.h
  55. 201 212
      modules/tm/t_msgbuilder.c
  56. 58 0
      modules/tm/t_msgbuilder.h
  57. 690 401
      modules/tm/t_reply.c
  58. 97 0
      modules/tm/t_reply.h
  59. 282 65
      modules/tm/t_thandlers.c
  60. 1 1
      modules/tm/test.c
  61. 153 2
      modules/tm/timer.c
  62. 16 2
      modules/tm/timer.h
  63. 0 565
      modules/tm/tm.c
  64. 22 10
      modules/tm/tm_load.c
  65. 19 9
      modules/tm/tm_load.h
  66. 463 0
      modules/tm/tm_mod.c
  67. 300 0
      modules/tm/uac.c
  68. 53 0
      modules/tm/uac.h
  69. 221 201
      msg_translator.c
  70. 17 1
      msg_translator.h
  71. 10 0
      parser/hf.c
  72. 2 1
      parser/hf.h
  73. 17 14
      parser/msg_parser.c
  74. 49 20
      parser/msg_parser.h
  75. 1 1
      parser/parse_to.c
  76. 1 0
      proxy.c
  77. 29 19
      receive.c
  78. 20 0
      route.c
  79. 2 0
      route.h
  80. 24 0
      route_struct.c
  81. 3 1
      route_struct.h
  82. 57 0
      script_cb.c
  83. 26 0
      script_cb.h
  84. 32 0
      test/bad_uri.sip
  85. 5 0
      test/file_copyright.txt
  86. 33 0
      test/invite00.sip
  87. 33 0
      test/invite01.sip
  88. 33 0
      test/invite03.sip
  89. 33 0
      test/invite04.sip
  90. 33 0
      test/invite05.sip
  91. 8 0
      test/ms-invite-00-rpl.sip
  92. 32 0
      test/ms-invite-00.sip
  93. 10 0
      test/ms-invite-01-rpl.sip
  94. 3 0
      test/no_eom_reply.sip
  95. 74 0
      test/onr.cfg
  96. 11 0
      test/register03.sip
  97. 11 0
      test/short_nonce.sip
  98. 4 0
      test/short_reply.sip
  99. 47 0
      test/stress.cfg
  100. 50 0
      test/struas.cfg

+ 26 - 0
COPYING

@@ -1,3 +1,29 @@
+-------------------------------------------------------------------------
+IMPORTANT NOTES
+
+1) The GPL applies to this copy of SIP Express Router software (ser).
+   For a license to use the ser software under conditions
+   other than those described here, or to purchase support for this
+   software, please contact iptel.org by e-mail at the following addresses:
+
+    [email protected]
+
+   (see http://www.gnu.org/copyleft/gpl-faq.html#TOCHeardOtherLicense
+    for an explanation how parallel licenses comply with GPL)
+
+2) ser software allows programmers to plug-in external modules to the
+   core part. Note that GPL mandates all plug-ins developed for the
+   ser software released under GPL license to be GPL-ed as well.
+
+   (see http://www.gnu.org/copyleft/gpl-faq.html#GPLAndPlugins
+    for a detailed explanation)
+
+3) Note that the GPL bellow is copyrighted by the Free Software Foundation,
+   but the ser software is copyrighted by iptel.org.
+
+
+-------------------------------------------------------------------------
+
 		    GNU GENERAL PUBLIC LICENSE
 		       Version 2, June 1991
 

+ 1 - 1
Makefile

@@ -10,7 +10,7 @@ auto_gen=lex.yy.c cfg.tab.c   #lexx, yacc etc
 #include  source related defs
 include Makefile.sources
 
-exclude_modules=CVS mysql pike
+exclude_modules=CVS mysql pike 
 static_modules=
 static_modules_path=$(addprefix modules/, $(static_modules))
 extra_sources=$(wildcard $(addsuffix /*.c, $(static_modules_path)))

+ 12 - 23
Makefile.defs

@@ -76,9 +76,6 @@ INSTALL-MAN = $(INSTALL) -m 644
 #		issues additional debugging information if lock/unlock is called
 # -DFAST_LOCK
 #		uses fast arhitecture specific locking (see the arh. specific section)
-# -DNOISY_REPLIES
-#		turns on appending User-agent and Content-length:0 to ser-generated
-#		replies; 
 # -DBUSY_WAIT
 #		uses busy waiting on the lock
 # -DADAPTIVE_WAIT
@@ -90,37 +87,29 @@ INSTALL-MAN = $(INSTALL) -m 644
 # -DNOSMP
 #		don't use smp compliant locking (faster but won't work on SMP machines)
 #		(not yet enabled)
-# -DWAIT
-#		protection against race condiditions; turn off only for debugging;
-#       to become non-optional if stable
-#
-# -SILENT_FR
-#		if defined, when FR timer hits (in tm) cancel is sent only if forking
-#		if used; otherwise, just delete the transaction without doing anything
+# -DNO_PINGTEL_TAG_HACK
+#		if enabled, To-header-field will be less liberal and will not accept
+#		'tag=' (tag parameter with equal sign and without value); it is called
+#		this way because such message was sighted from a Pingtel phone
 
 DEFS+= -DNAME='"$(NAME)"' -DVERSION='"$(RELEASE)"' -DARCH='"$(ARCH)"' \
 	 -DOS='"$(OS)"' -DCOMPILER='"$(CC_VER)"'\
 	 -DPKG_MALLOC \
 	 -DSHM_MEM  -DSHM_MMAP \
 	 -DADAPTIVE_WAIT -DADAPTIVE_WAIT_LOOPS=1024 \
-	 -DWAIT \
-	 -DSILENT_FR \
-	 -DNOISY_REPLIES -DVERY_NOISY_REPLIES\
-	 -DPINGTEL_TAG_HACK\
-	 -DF_MALLOC \
- 	 -DUSE_SYNONIM\
 	 -DUSE_IPV6 \
+	 -DEXTRA_DEBUG \
+	 -DVQ_MALLOC  -DDBG_QM_MALLOC \
+	 #-DCONTACT_BUG
+	 #-DF_MALLOC \
+	 #-DDBG_LOCK
 	 #-DNO_DEBUG \
 	 #-DADAPTIVE_WAIT -DADAPTIVE_WAIT_LOOPS=0 \
 	 #-DNOSMP \
 	 #-DEXTRA_DEBUG 
-	 #-DVQ_MALLOC  -DDBG_LOCK  #-DSTATS
-	 #-DDBG_QM_MALLOC 
-# -DUSE_SHM_MEM
-#-DSTATS 
-#-DNO_LOG
-
-
+	 #-DUSE_SHM_MEM
+	 #-DSTATS 
+	 #-DNO_LOG
 
 #PROFILE=  -pg #set this if you want profiling
 #mode = debug

+ 37 - 6
README-MODULES

@@ -3,33 +3,59 @@
 List of currently available ser modules
 ----------------------------------------------------------
 Name	Use		 	 Maturity	Purpose/Depends on
+(owner)
 ----------------------------------------------------------
 acc		regular		 stable		transaction accounting
-		/example				(the module servers also
+(jku)	/example				(the module servers also
 							    as example of how to bind
 								to transaction management)
 								-tm
+
 auth	regular		 stable		digest authentication
-								-sl
+(jja)							-sl
 								-mysql
+
 cpl		experimental alpha		Call Processing Language
+(bia)
+
 ext		experimental alpha		Execution of external URI
-								processing logic
+(bia)							processing logic
+
 im		temporary	 alpha		Stateless instant messaging	
-								client
+(bia)							client
+
 jabber	experimental beta		SIP2Jabber gateway
+(dcm)
+
 maxfwd	regular		 stable		Max-Forwards check
+(bia)
+
 mysql	regular		 stable		supporting MySql interface
+(jja)
+
 pike	experimental alpha		excessive load detection
+(bia)
+
 print	example		 stable		Printing Message to stdout
+(ape)
+
 rr		regular		 stable		Record-Routing
+(jja)
+
 sl		regular		 stable		Stateless Replies
+(bia)
+
 sms		regular		 stable		SMS gateway
-								-tm
+(bia)								-tm
+
 textops regular		 stable		Message Textual Operations
+(ape)
+
 tm		regular		 beta		Transaction Management
+(ape)
+
 usrloc	regular		 stable		User Location
-								-sl
+(jja)								-sl
 								-mysql (optionally)
 
 
@@ -44,3 +70,8 @@ ser programmers.
 Maturity is label as stable if a module has been deployed
 for longer time, alpha if it is still being developed and
 beta if it is under test.
+
+Modules underway include presence server functionality,
+firewall control, message store and more. If you are
+interested in any of these or other modules, write us
+to [email protected].

+ 9 - 9
TODO

@@ -16,16 +16,16 @@ x (different way) add request header bitmap field for the modules
 
 
 High priority:
-- fix/replace T_REF/T_UNREF
-- review all the tm locking
+x fix/replace T_REF/T_UNREF
+x review all the tm locking
 x if () {} else {}
 x plugin interface
 x ipv6 support
-- reply ("response line")
-- drop ACKs for our replies
+x reply ("response line")
+x drop ACKs for our replies
 - icmp error handling
-- add To-tag (for the replies)
-- add User-Agent (for the replies)
+x add To-tag (for the replies)
+x add User-Agent (for the replies)
 
 Low priority:
 x fix via address someday
@@ -38,7 +38,7 @@ x forward to received= if present
 - exec improvments (add format strings to it)
 - command line switch for checking the config file syntax
 - config file version (a la sendmail)
-- loop detection
+0 loop detection
 - cfg. file reload
 - flags for using names or ip adresses in Via ?
 
@@ -55,6 +55,6 @@ x the same for rpm
 - the same for FreeBSD and Slackware
 
 
-- jku: branch hash computation over canonical values
-- jku: loop checking
+x jku: branch hash computation over canonical values
+0 jku: loop checking
 - jku: try CRC as opposed to MD5

+ 21 - 0
action.c

@@ -17,6 +17,7 @@
 #include "sr_module.h"
 #include "mem/mem.h"
 #include "globals.h"
+#include "dset.h"
 
 #include <sys/types.h>
 #include <sys/socket.h>
@@ -179,6 +180,18 @@ int do_action(struct action* a, struct sip_msg* msg)
 			ret=1;
 			break;
 
+		/* jku -- introduce a new branch */
+		case APPEND_BRANCH_T:
+			if ((a->p1_type!=STRING_ST)) {
+				LOG(L_CRIT, "BUG: do_action: bad append_branch_t %d\n",
+					a->p1_type );
+				ret=E_BUG;
+				break;
+			}
+			ret=append_branch( msg, a->p1.string, 
+				a->p1.string ? strlen(a->p1.string):0 );
+			break;
+
 		/* jku begin: is_length_greater_than */
 		case LEN_GT_T:
 			if (a->p1_type!=NUMBER_ST) {
@@ -281,6 +294,14 @@ int do_action(struct action* a, struct sip_msg* msg)
 			}
 			ret=1;
 			break;
+		case REVERT_URI_T:
+			if (msg->new_uri.s) {
+				pkg_free(msg->new_uri.s);
+				msg->new_uri.len=0;
+				msg->new_uri.s=0;
+			};
+			ret=1;
+			break;
 		case SET_HOST_T:
 		case SET_HOSTPORT_T:
 		case SET_USER_T:

+ 18 - 2
cfg.lex

@@ -46,6 +46,7 @@ SEND	send
 LOG		log
 ERROR	error
 ROUTE	route
+REPLY_ROUTE reply_route
 EXEC	exec
 SETFLAG		setflag
 RESETFLAG	resetflag
@@ -57,8 +58,10 @@ SET_USER		"rewriteuser"|"setuser"|"setu"
 SET_USERPASS	"rewriteuserpass"|"setuserpass"|"setup"
 SET_PORT		"rewriteport"|"setport"|"setp"
 SET_URI			"rewriteuri"|"seturi"
+REVERT_URI		"revert_uri"
 PREFIX			"prefix"
 STRIP			"strip"
+APPEND_BRANCH	"append_branch"
 IF				"if"
 ELSE			"else"
 
@@ -94,7 +97,12 @@ STAT	statistics
 MAXBUFFER maxbuffer
 CHILDREN children
 CHECK_VIA	check_via
-LOOP_CHECKS	loop_checks
+SYN_BRANCH syn_branch
+SIP_WARNING sip_warning
+FIFO fifo
+FIFO_MODE fifo_mode
+SERVER_SIGNATURE server_signature
+REPLY_TO_VIA reply_to_via
 
 LOADMODULE	loadmodule
 MODPARAM        modparam
@@ -148,6 +156,7 @@ EAT_ABLE	[\ \t\b\r]
 <INITIAL>{ISFLAGSET}	{ count(); yylval.strval=yytext; return ISFLAGSET; }
 <INITIAL>{LEN_GT}	{ count(); yylval.strval=yytext; return LEN_GT; }
 <INITIAL>{ROUTE}	{ count(); yylval.strval=yytext; return ROUTE; }
+<INITIAL>{REPLY_ROUTE}	{ count(); yylval.strval=yytext; return REPLY_ROUTE; }
 <INITIAL>{EXEC}	{ count(); yylval.strval=yytext; return EXEC; }
 <INITIAL>{SET_HOST}	{ count(); yylval.strval=yytext; return SET_HOST; }
 <INITIAL>{SET_HOSTPORT}	{ count(); yylval.strval=yytext; return SET_HOSTPORT; }
@@ -155,8 +164,10 @@ EAT_ABLE	[\ \t\b\r]
 <INITIAL>{SET_USERPASS}	{ count(); yylval.strval=yytext; return SET_USERPASS; }
 <INITIAL>{SET_PORT}	{ count(); yylval.strval=yytext; return SET_PORT; }
 <INITIAL>{SET_URI}	{ count(); yylval.strval=yytext; return SET_URI; }
+<INITIAL>{REVERT_URI}	{ count(); yylval.strval=yytext; return REVERT_URI; }
 <INITIAL>{PREFIX}	{ count(); yylval.strval=yytext; return PREFIX; }
 <INITIAL>{STRIP}	{ count(); yylval.strval=yytext; return STRIP; }
+<INITIAL>{APPEND_BRANCH}	{ count(); yylval.strval=yytext; return APPEND_BRANCH; }
 <INITIAL>{IF}	{ count(); yylval.strval=yytext; return IF; }
 <INITIAL>{ELSE}	{ count(); yylval.strval=yytext; return ELSE; }
 
@@ -181,7 +192,12 @@ EAT_ABLE	[\ \t\b\r]
 <INITIAL>{MAXBUFFER}	{ count(); yylval.strval=yytext; return MAXBUFFER; }
 <INITIAL>{CHILDREN}	{ count(); yylval.strval=yytext; return CHILDREN; }
 <INITIAL>{CHECK_VIA}	{ count(); yylval.strval=yytext; return CHECK_VIA; }
-<INITIAL>{LOOP_CHECKS}	{ count(); yylval.strval=yytext; return LOOP_CHECKS; }
+<INITIAL>{SYN_BRANCH}	{ count(); yylval.strval=yytext; return SYN_BRANCH; }
+<INITIAL>{SIP_WARNING}	{ count(); yylval.strval=yytext; return SIP_WARNING; }
+<INITIAL>{FIFO}	{ count(); yylval.strval=yytext; return FIFO; }
+<INITIAL>{FIFO_MODE}	{ count(); yylval.strval=yytext; return FIFO_MODE; }
+<INITIAL>{SERVER_SIGNATURE}	{ count(); yylval.strval=yytext; return SERVER_SIGNATURE; }
+<INITIAL>{REPLY_TO_VIA}	{ count(); yylval.strval=yytext; return REPLY_TO_VIA; }
 <INITIAL>{LOADMODULE}	{ count(); yylval.strval=yytext; return LOADMODULE; }
 <INITIAL>{MODPARAM}     { count(); yylval.strval=yytext; return MODPARAM; }
 

+ 41 - 3
cfg.y

@@ -56,15 +56,18 @@ void* f_tmp;
 %token LOG_TOK
 %token ERROR
 %token ROUTE
+%token REPLY_ROUTE
 %token EXEC
 %token SET_HOST
 %token SET_HOSTPORT
 %token PREFIX
 %token STRIP
+%token APPEND_BRANCH
 %token SET_USER
 %token SET_USERPASS
 %token SET_PORT
 %token SET_URI
+%token REVERT_URI
 %token IF
 %token ELSE
 %token URIHOST
@@ -90,7 +93,12 @@ void* f_tmp;
 %token STAT
 %token CHILDREN
 %token CHECK_VIA
-%token LOOP_CHECKS
+%token SYN_BRANCH
+%token SIP_WARNING
+%token FIFO
+%token FIFO_MODE
+%token SERVER_SIGNATURE
+%token REPLY_TO_VIA
 %token LOADMODULE
 %token MODPARAM
 %token MAXBUFFER
@@ -151,6 +159,7 @@ statements:	statements statement {}
 statement:	assign_stm 
 		| module_stm
 		| route_stm 
+		| reply_route_stm
 		| CR	/* null statement*/
 	;
 
@@ -180,8 +189,18 @@ assign_stm:	DEBUG EQUAL NUMBER { debug=$3; }
 		| CHILDREN EQUAL error { yyerror("number expected"); } 
 		| CHECK_VIA EQUAL NUMBER { check_via=$3; }
 		| CHECK_VIA EQUAL error { yyerror("boolean value expected"); }
-		| LOOP_CHECKS EQUAL NUMBER { loop_checks=$3; }
-		| LOOP_CHECKS EQUAL error { yyerror("boolean value expected"); }
+		| SYN_BRANCH EQUAL NUMBER { syn_branch=$3; }
+		| SYN_BRANCH EQUAL error { yyerror("boolean value expected"); }
+		| SIP_WARNING EQUAL NUMBER { sip_warning=$3; }
+		| SIP_WARNING EQUAL error { yyerror("boolean value expected"); }
+		| FIFO EQUAL STRING { fifo=$3; }
+		| FIFO EQUAL error { yyerror("string value expected"); }
+		| FIFO_MODE EQUAL NUMBER { fifo_mode=$3; }
+		| FIFO_MODE EQUAL NUMBER { yyerror("int value expected"); }
+		| SERVER_SIGNATURE EQUAL NUMBER { server_signature=$3; }
+		| SERVER_SIGNATURE EQUAL error { yyerror("boolean value expected"); }
+		| REPLY_TO_VIA EQUAL NUMBER { reply_to_via=$3; }
+		| REPLY_TO_VIA EQUAL error { yyerror("boolean value expected"); }
 		| LISTEN EQUAL ip  {
 								if (sock_no< MAX_LISTEN){
 									tmp=ip_addr2a($3);
@@ -347,6 +366,17 @@ route_stm:	ROUTE LBRACE actions RBRACE { push($3, &rlist[DEFAULT_RT]); }
 										}
 		| ROUTE error { yyerror("invalid  route  statement"); }
 	;
+
+reply_route_stm: REPLY_ROUTE LBRACK NUMBER RBRACK LBRACE actions RBRACE {
+										if (($3<REPLY_RT_NO)&&($3>=1)){
+											push($6, &reply_rlist[$3]);
+										} else {
+											yyerror("invalid reply routing"
+												"table number");
+											YYABORT; }
+										}
+		| REPLY_ROUTE error { yyerror("invalid reply_route statement"); }
+	;
 /*
 rules:	rules rule { push($2, &$1); $$=$1; }
 	| rule {$$=$1; }
@@ -685,6 +715,12 @@ cmd:		FORWARD LPAREN host RPAREN	{ $$=mk_action(	FORWARD_T,
 		| STRIP LPAREN error RPAREN { $$=0; yyerror("bad argument, "
 														"number expected"); }
 
+		| APPEND_BRANCH LPAREN STRING RPAREN { $$=mk_action( APPEND_BRANCH_T,
+													STRING_ST, 0, $3, 0) ; }
+		| APPEND_BRANCH LPAREN RPAREN { $$=mk_action( APPEND_BRANCH_T,
+													STRING_ST, 0, 0, 0 ) ; }
+		| APPEND_BRANCH {  $$=mk_action( APPEND_BRANCH_T, STRING_ST, 0, 0, 0 ) ; }
+
 		| SET_HOSTPORT LPAREN STRING RPAREN { $$=mk_action( SET_HOSTPORT_T, 
 														STRING_ST, 0, $3, 0); }
 		| SET_HOSTPORT error { $$=0; yyerror("missing '(' or ')' ?"); }
@@ -710,6 +746,8 @@ cmd:		FORWARD LPAREN host RPAREN	{ $$=mk_action(	FORWARD_T,
 		| SET_URI error { $$=0; yyerror("missing '(' or ')' ?"); }
 		| SET_URI LPAREN error RPAREN { $$=0; yyerror("bad argument, "
 										"string expected"); }
+		| REVERT_URI LPAREN RPAREN { $$=mk_action( REVERT_URI_T, 0,0,0,0); }
+		| REVERT_URI { $$=mk_action( REVERT_URI_T, 0,0,0,0); }
 		| ID LPAREN RPAREN			{ f_tmp=(void*)find_export($1, 0);
 									   if (f_tmp==0){
 										yyerror("unknown command, missing"

+ 14 - 5
config.h

@@ -22,6 +22,7 @@
 #define CHILD_NO    8
 
 #define RT_NO 10 /* routing tables number */
+#define REPLY_RT_NO 10 /* reply routing tables number */
 #define DEFAULT_RT 0 /* default routing table */
 
 #define MAX_REC_LEV 100 /* maximum number of recursive calls */
@@ -46,8 +47,8 @@
 
 #define MAX_WARNING_LEN  256
 		
-#define MY_BRANCH ";branch=0"
-#define MY_BRANCH_LEN 9
+#define MY_BRANCH ";branch="
+#define MY_BRANCH_LEN 8
 
 
 #define MAX_PORT_LEN 7 /* ':' + max 5 letters + \0 */
@@ -88,8 +89,16 @@
 #define MAX_VIA_LINE_SIZE	240
 #define MAX_RECEIVED_SIZE	57
 
-/* maximum number of processes is constrained by capacity of
-   process bitmaps */
-#define MAX_PROCESSES (sizeof( process_bm_t) * 8 )
+/* maximum number of branches per transaction */
+#define MAX_BRANCHES    4
 
+/* maximum length of a FIFO server command */
+#define MAX_FIFO_COMMAND 512
+
+/* buffer dimensions for FIFO server */
+#define MAX_CONSUME_BUFFER 1024
+/* where reply pipes may be opened */
+#define FIFO_DIR "/tmp/"
+/* max length of the text of fifo 'print' command */
+#define MAX_PRINT_TEXT 256
 #endif

+ 87 - 0
dset.c

@@ -0,0 +1,87 @@
+/*
+ * $Id$
+ *
+ * destination set
+ */
+
+#include <string.h>
+
+#include "dprint.h"
+#include "config.h"
+#include "parser/parser_f.h"
+#include "parser/msg_parser.h"
+#include "ut.h"
+#include "hash_func.h"
+#include "dset.h"
+
+
+
+/* where we store URIs of additional transaction branches
+  (-1 because of the default branch, #0)
+*/
+static struct branch branches[ MAX_BRANCHES - 1 ];
+/* how many of them we have */
+static unsigned int nr_branches=0;
+/* branch iterator */
+static int branch_iterator=0;
+
+void init_branch_iterator(void)
+{
+	branch_iterator=0;
+}
+
+char *next_branch( int *len )
+{
+	unsigned int i;
+
+	i=branch_iterator;
+	if (i<nr_branches) {
+		branch_iterator++;
+		*len=branches[i].len;
+		return branches[i].uri;
+	} else {
+		*len=0;
+		return 0;
+	}
+}
+
+void clear_branches()
+{
+	nr_branches=0;
+}
+
+/* add a new branch to current transaction */
+int append_branch( struct sip_msg *msg, char *uri, int uri_len )
+{
+	/* if we have already set up the maximum number
+	   of branches, don't try new ones */
+	if (nr_branches==MAX_BRANCHES-1) {
+		LOG(L_ERR, "ERROR: append_branch: max nr of branches exceeded\n");
+		return -1;
+	}
+
+	if (uri_len>MAX_URI_SIZE-1) {
+		LOG(L_ERR, "ERROR: append_branch: too long uri: %.*s\n",
+			uri_len, uri );
+		return -1;
+	}
+
+	/* if not parameterized, take current uri */
+	if (uri==0) {
+		if (msg->new_uri.s) { 
+			uri=msg->new_uri.s;
+			uri_len=msg->new_uri.len;
+		} else {
+			uri=msg->first_line.u.request.uri.s;
+			uri_len=msg->first_line.u.request.uri.len;
+		}
+	}
+	
+	memcpy( branches[nr_branches].uri, uri, uri_len );
+	/* be safe -- add zero termination */
+	branches[nr_branches].uri[uri_len]=0;
+	branches[nr_branches].len=uri_len;
+	
+	nr_branches++;
+	return 1;
+}

+ 29 - 0
dset.h

@@ -0,0 +1,29 @@
+/*
+ * $Id$
+ */
+
+#ifndef _T_FORKS_H
+#define _T_FORKS_H
+
+#include "config.h"
+
+struct branch
+{
+	char uri[MAX_URI_SIZE];
+	unsigned int len;
+};
+
+struct sip_msg;
+
+/*
+typedef int (*tfork_f)( struct sip_msg *msg, char *uri, int uri_len );
+*/
+
+/* add a new branch to current transaction */
+int append_branch( struct sip_msg *msg, char *uri, int uri_len );
+/* iterate through list of new transaction branches */
+void init_branch_iterator();
+char *next_branch( int *len );
+void clear_branches();
+
+#endif

+ 108 - 0
error.c

@@ -5,6 +5,9 @@
 
 #include <stdio.h>
 #include "error.h"
+#include "str.h"
+#include "parser/msg_parser.h"
+#include "mem/mem.h"
 
 /* current function's error; */
 int ser_error=-1;
@@ -43,6 +46,10 @@ int err2reason_phrase(
 			error_txt="Regretfuly, we were not able to process the URI";
 			*sip_error=-ser_error;
 			break;
+		case E_BAD_TUPEL:
+			error_txt="Transaction tupel incomplete";
+			*sip_error=-E_BAD_REQ;
+			break;
 		default:
 			error_txt="I'm terribly sorry, server error occured";
 			*sip_error=500;
@@ -51,3 +58,104 @@ int err2reason_phrase(
 	return snprintf( phrase, etl, "%s (%d/%s)", error_txt, 
 		-ser_error, signature );
 }
+
+char *error_text( int code )
+{
+	switch(code) {
+
+		case 100: return "Trying";
+		case 180: return "Ringing";
+		case 181: return "Call is Being Forwarded";
+		case 182: return "Queued";
+		case 183: return "Session Progress";
+
+		case 200: return "OK";
+
+		case 300: return "Multiple Choices";
+		case 301: return "Moved Permanently";
+		case 302: return "Moved Temporarily";
+		case 305: return "Use Proxy";
+		case 380: return "Alternative Service";
+
+		case 400: return "Bad Request";
+		case 401: return "Unauthorized";
+		case 402: return "Payement Required";
+		case 403: return "Forbidden";
+		case 404: return "Not Found";
+		case 405: return "Method not Allowed";
+		case 406: return "Not Acceptable";
+		case 407: return "Proxy authentication Required";
+		case 408: return "Request Timeout";
+		case 410: return "Gone";
+		case 413: return "Request Entity Too Large";
+		case 414: return "Request-URI Too Long";
+		case 415: return "Unsupported Media Type";
+		case 416: return "Unsupported URI Scheme";
+		case 417: return "Bad Extension";
+		case 421: return "Extension Required";
+		case 423: return "Interval Too Brief";
+		case 480: return "Temporarily Unavailable";
+		case 481: return "Call/Transaction Does not Exist";
+		case 482: return "Loop Detected";
+		case 483: return "Too Many Hops";
+		case 484: return "Address Incomplete";
+		case 485: return "Ambigous";
+		case 486: return "Busy Here";
+		case 487: return "Request Terminated";
+		case 488: return "Not Acceptable Here";
+		case 491: return "Request Pending";
+	
+		case 500: return "Server Internal Error";
+		case 501: return "Not Implemented";
+		case 502: return "Bad Gateway";
+		case 503: return "Service Unavailable";
+		case 504: return "Server Time-out";
+		case 505: return "Version not Supported";
+		case 513: return "Message Too Large";
+
+		case 600: return "Busy Everywhere";
+		case 603: return "Decline";
+		case 604: return "Does not Exist Anywhere";
+		case 606: return "Not Acceptable";
+
+	}
+
+	if (code>=600) return "Global Failure";
+	else if (code>=500) return "Server Failure";
+	else if (code>=400) return "Request Failure";
+	else if (code>=300) return "Redirection";
+	else if (code>=200) return "Successful";
+	else if (code>=100) return "Provisional";
+	else return "Unspecified";
+}
+
+void get_reply_status( str *status, struct sip_msg *reply, int code )
+{
+	str phrase;
+
+	status->s=0;
+
+	if (reply==0) {
+		LOG(L_CRIT, "BUG: get_reply_status called with 0 msg\n");
+		return;
+	}
+
+	if (reply==FAKED_REPLY) {
+		phrase.s=error_text(code);
+		phrase.len=strlen(phrase.s);
+	} else {
+		phrase=reply->first_line.u.reply.reason;
+	}
+	status->len=phrase.len+3/*code*/+1/*space*/+1/*ZT*/;
+	status->s=pkg_malloc(status->len);
+	if (!status->s) {
+		LOG(L_ERR, "ERROR: get_reply_status: no mem\n");
+		return;
+	}
+	status->s[3]=' ';
+	status->s[2]='0'+code % 10; code=code/10;
+	status->s[1]='0'+code% 10; code=code/10;
+	status->s[0]='0'+code % 10;
+	memcpy(&status->s[4], phrase.s, phrase.len);
+	status->s[status->len-1]=0;
+}

+ 16 - 0
error.h

@@ -12,6 +12,12 @@
 #define E_BUG         -5
 #define E_CFG         -6
 #define E_NO_SOCKET		-7
+/* unresolveable topmost Via */
+#define E_BAD_VIA		-8
+/* incomplete transaction tupel */
+#define E_BAD_TUPEL		-9
+/* script programming error */
+#define E_SCRIPT		-10
 
 #define E_SEND		  -477
 /* unresolveable next-hop address */
@@ -23,12 +29,22 @@
 
 #define MAX_REASON_LEN	128
 
+#include "str.h"
+
 /* processing status of the last command */
 extern int ser_error;
 extern int prev_ser_error;
 
+struct sip_msg;
+
+/* ser error -> SIP error */
 int err2reason_phrase( int ser_error, int *sip_error, 
                 char *phrase, int etl, char *signature );
 
+/* SIP error core -> SIP text */
+char *error_text( int code );
+
+/* return pkg_malloc-ed reply status in status->s */
+void get_reply_status( str *status, struct sip_msg *reply, int code );
 
 #endif

+ 1 - 0
etc/iptel.cfg

@@ -614,5 +614,6 @@ route[3] {
 		sl_reply_error(); 
 		break; 
 	};
+
 }
 

+ 344 - 0
fifo_server.c

@@ -0,0 +1,344 @@
+/*
+ * $Id$
+ *
+ * simple UAC for things such as SUBSCRIBE or SMS gateway;
+ * no authentication and other UAC features -- just send
+ * a message, retransmit and await a reply; forking is not
+ * supported during client generation, in all other places
+ * it is -- adding it should be simple
+ */
+
+#include <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <string.h>
+#include "dprint.h"
+#include "ut.h"
+#include "error.h"
+#include "config.h"
+#include "globals.h"
+#include "fifo_server.h"
+#include "mem/mem.h"
+
+/* FIFO server vars */
+char *fifo=0; /* FIFO name */
+int fifo_mode=S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP;
+pid_t fifo_pid;
+/* file descriptors */
+static int fifo_read=0;
+static int fifo_write=0;
+static FILE *fifo_stream;
+
+/* list of fifo command */
+static struct fifo_command *cmd_list=0;
+
+static struct fifo_command *lookup_fifo_cmd( char *name )
+{
+	struct fifo_command *c;
+	for(c=cmd_list; c; c=c->next) {
+		if (strcasecmp(c->name, name)==0) return c;
+	}
+	return 0;
+}
+
+int register_fifo_cmd(fifo_cmd f, char *cmd_name, void *param)
+{
+	struct fifo_command *new_cmd;
+
+	if (lookup_fifo_cmd(cmd_name)) {
+		LOG(L_ERR, "ERROR: register_fifo_cmd: attempt to register synonyms\n");
+		return E_BUG;
+	}
+	new_cmd=malloc(sizeof(struct fifo_command));
+	if (new_cmd==0) {
+		LOG(L_ERR, "ERROR: register_fifo_cmd: out of mem\n");
+		return E_OUT_OF_MEM;
+	}
+	new_cmd->f=f;
+	new_cmd->name=cmd_name;
+	new_cmd->param=param;
+
+	new_cmd->next=cmd_list;
+	cmd_list=new_cmd;
+
+	return 1;
+}
+
+
+int read_line( char *b, int max, FILE *stream, int *read )
+{
+	int len;
+	if (fgets(b, max, stream)==NULL) {
+		LOG(L_ERR, "ERROR: fifo_server fgets failed: %s\n",
+			strerror(errno));
+		kill(0, SIGTERM);
+	}
+	/* if we did not read whole line, our buffer is too small
+	   and we cannot process the request; consume the remainder of 
+	   request
+	*/
+	len=strlen(b);
+	if (len && !(b[len-1]=='\n' || b[len-1]=='\r')) {
+		LOG(L_ERR, "ERROR: read_line: request  line too long\n");
+		return 0;
+	}
+	/* trim from right */
+	while(len) {
+		if(b[len-1]=='\n' || b[len-1]=='\r'
+				|| b[len-1]==' ' || b[len-1]=='\t' ) {
+			len--;
+			b[len]=0;
+		} else break;
+	}
+	*read=len;
+	return 1;
+}
+
+static void consume_request( FILE *stream )
+{
+	int len;
+	char buffer[MAX_CONSUME_BUFFER];
+
+	while(!read_line(buffer, MAX_CONSUME_BUFFER, stream, &len));
+
+#ifdef _OBSOLETED
+	int eol_count;
+
+	eol_count=0;
+
+	/* each request must be terminated by two EoLs */
+	while(eol_count!=2) {
+		/* read until EoL is encountered */
+		while(!read_line(buffer, MAX_CONSUME_BUFFER, stream, &len));
+		eol_count=len==0?eol_count+1:1;
+	}
+#endif
+}
+
+int read_eol( FILE *stream )
+{
+	int len;
+	char buffer[MAX_CONSUME_BUFFER];
+	if (!read_line(buffer, MAX_CONSUME_BUFFER, stream, &len) || len!=0) {
+		LOG(L_ERR, "ERROR: read_eol: EOL expected: %.10s...\n",
+			buffer );
+		return 0;
+	}
+	return 1;
+}
+	
+int read_line_set(char *buf, int max_len, FILE *fifo, int *len)
+{
+	int set_len;
+	char *c;
+	int line_len;
+
+	c=buf;set_len=0;
+	while(1) {
+		if (!read_line(c,max_len,fifo,&line_len)) {
+			LOG(L_ERR, "ERROR: fifo_server: line expected\n");
+			return 0;
+		}
+		/* end encountered ... return */
+		if (line_len==0) {
+			*len=set_len;
+			return 1;
+		}
+		max_len-=line_len; c+=line_len; set_len+=line_len;
+		if (max_len<CRLF_LEN) {
+			LOG(L_ERR, "ERROR: fifo_server: no place for CRLF\n");
+			return 0;
+		}
+		memcpy(c, CRLF, CRLF_LEN);
+		max_len-=CRLF_LEN; c+=CRLF_LEN; set_len+=CRLF_LEN;
+	}
+}
+
+static char *trim_filename( char * file )
+{
+	int prefix_len, fn_len;
+	char *new_fn;
+
+	/* we only allow files in "/tmp" -- any directory
+	   changes are not welcome
+	*/
+	if (strchr(file,'.') || strchr(file,'/')
+				|| strchr(file, '\\')) {
+		LOG(L_ERR, "ERROR: trim_filename: forbidden filename: %s\n"
+			, file);
+		return 0;
+	}
+	prefix_len=strlen(FIFO_DIR); fn_len=strlen(file);
+	new_fn=pkg_malloc(prefix_len+fn_len+1);
+	if (new_fn==0) {
+		LOG(L_ERR, "ERROR: trim_filename: no mem\n");
+		return 0;
+	}
+
+	memcpy(new_fn, FIFO_DIR, prefix_len);
+	memcpy(new_fn+prefix_len, file, fn_len );
+	new_fn[prefix_len+fn_len]=0;
+
+	return new_fn;
+}
+
+static void fifo_server(FILE *fifo_stream)
+{
+	char buf[MAX_FIFO_COMMAND];
+	int line_len;
+	char *file_sep, *command, *file;
+	struct fifo_command *f;
+
+	file_sep=command=file=0;
+
+	while(1) {
+
+		/* commands must look this way ':<command>:[filename]' */
+		if (!read_line(buf, MAX_FIFO_COMMAND, fifo_stream, &line_len)) {
+			/* line breaking must have failed -- consume the rest
+			   and proceed to a new request
+			*/
+			LOG(L_ERR, "ERROR: fifo_server: command expected\n");
+			goto consume;
+		}
+		if (line_len==0) {
+			LOG(L_ERR, "ERROR: fifo_server: command empty\n");
+			continue;
+		}
+		if (line_len<3) {
+			LOG(L_ERR, "ERROR: fifo_server: command must have at least 3 chars\n");
+			goto consume;
+		}
+		if (*buf!=CMD_SEPARATOR) {
+			LOG(L_ERR, "ERROR: fifo_server: command must start with %c\n", 
+				CMD_SEPARATOR);
+			goto consume;
+		}
+		command=buf+1;
+		file_sep=strchr(command, CMD_SEPARATOR );
+		if (file_sep==NULL) {
+			LOG(L_ERR, "ERROR: fifo_server: file separator missing\n");
+			goto consume;
+		}
+		if (file_sep==command) {
+			LOG(L_ERR, "ERROR: fifo_server: empty command\n");
+			goto consume;
+		}
+		if (*(file_sep+1)==0) file=NULL; 
+		else {
+			file=file_sep+1;
+			file=trim_filename(file);
+			if (file==0) {
+				LOG(L_ERR, "ERROR: fifo_server: trimming filename\n");
+				goto consume;
+			}
+		}
+		/* make command zero-terminated */
+		*file_sep=0;
+
+		f=lookup_fifo_cmd( command );
+		if (f==0) {
+			LOG(L_ERR, "ERROR: fifo_server: command %s is not available\n",
+				command);
+			goto consume;
+		}
+		if (f->f(fifo_stream, file)<0) {
+			LOG(L_ERR, "ERROR: fifo_server: command (%s) "
+				"processing failed\n", command );
+			goto consume;
+		}
+
+consume:
+		if (file) { pkg_free(file); file=0;}
+		consume_request(fifo_stream);
+	}
+}
+
+int open_fifo_server()
+{
+	if (fifo==NULL) {
+		DBG("TM: open_uac_fifo: no fifo will be opened\n");
+		/* everything is ok, we just do not want to start */
+		return 1;
+	}
+	DBG("TM: open_uac_fifo: opening fifo...\n");
+	if ((mkfifo(fifo, fifo_mode)<0) && (errno!=EEXIST)) {
+		LOG(L_ERR, "ERROR: open_fifo_server; can't create FIFO: %s\n",
+			strerror(errno));
+		return -1;
+	}
+	process_no++;
+	fifo_pid=fork();
+	if (fifo_pid<0) {
+		LOG(L_ERR, "ERROR: open_fifo_server: failure to fork: %s\n",
+			strerror(errno));
+		return -1;
+	}
+	if (fifo_pid==0) { /* child == FIFO server */
+		LOG(L_INFO, "INFO: fifo process starting: %d\n", getpid());
+		fifo_read=open(fifo, O_RDONLY, 0);
+		if (fifo_read<0) {
+			LOG(L_ERR, "SER: open_uac_fifo: fifo_read did not open: %s\n",
+				strerror(errno));
+			return -1;
+		}
+		fifo_stream=fdopen(fifo_read, "r"	);
+		if (fifo_stream==NULL) {
+			LOG(L_ERR, "SER: open_uac_fifo: fdopen failed: %s\n",
+				strerror(errno));
+			return -1;
+		}
+		LOG(L_INFO, "SER: open_uac_fifo: fifo server up...\n");
+		fifo_server( fifo_stream ); /* never retruns */
+	}
+	/* dad process */
+	pids[process_no]=fifo_pid;
+	/* make sure the read fifo will not close */
+	fifo_write=open(fifo, O_WRONLY, 0);
+	if (fifo_write<0) {
+		LOG(L_ERR, "SER: open_uac_fifo: fifo_write did not open: %s\n",
+			strerror(errno));
+		return -1;
+	}
+	return 1;
+}
+
+/* diagnostic and hello-world FIFO command */
+int print_fifo_cmd( FILE *stream, char *response_file )
+{
+	char text[MAX_PRINT_TEXT];
+	int text_len;
+	int file;
+	
+	/* expect one line which will be printed out */
+	if (!read_line(text, MAX_PRINT_TEXT, stream, &text_len)) {
+		LOG(L_ERR, "ERROR: print_fifo_cmd: too big text\n");
+		return -1;
+	}
+	/* now the work begins */
+	if (response_file) {
+		file=open( response_file , O_WRONLY);
+		if (file<0) {
+			LOG(L_ERR, "ERROR: print_fifo_cmd: open error (%s): %s\n",
+				response_file, strerror(errno));
+			return -1;
+		}
+		if (write(file, text,text_len)<0) {
+			LOG(L_ERR, "ERROR: print_fifo_cmd: write error: %s\n",
+				 strerror(errno));
+			close(file);
+			return -1;
+		}
+		close(file);
+	} else {
+		LOG(L_INFO, "INFO: print_fifo_cmd: %.*s\n", 
+			text_len, text );
+	}
+	return 1;
+}

+ 34 - 0
fifo_server.h

@@ -0,0 +1,34 @@
+/*
+ * $Id$
+ *
+ */
+
+#ifndef _FIFO_SERVER_H
+#define _FIFO_SERVER_H
+
+#include <stdio.h>
+
+#define CMD_SEPARATOR ':'
+
+typedef int (fifo_cmd)( FILE *fifo_stream, char *response_file );
+
+struct fifo_command{
+	fifo_cmd *f;
+	struct fifo_command *next;
+	void *param;
+	char *name;
+};
+
+int register_fifo_cmd(fifo_cmd f, char *cmd_name, void *param);
+
+/* read a single EoL-terminated line from fifo */
+int read_line( char *b, int max, FILE *stream, int *read );
+/* consume EoL from fifo */
+int read_eol( FILE *stream );
+/* consume a set of EoL-terminated lines terminated by an additional EoL */
+int read_line_set(char *buf, int max_len, FILE *fifo, int *len);
+
+int open_fifo_server();
+
+int print_fifo_cmd( FILE *stream, char *response_file );
+#endif

+ 2 - 2
flags.c

@@ -16,12 +16,12 @@ int setflag( struct sip_msg* msg, flag_t flag ) {
 }
 
 int resetflag( struct sip_msg* msg, flag_t flag ) {
-	msg->flags &= ~ flag;
+	msg->flags &= ~ (1 << flag);
 	return 1;
 }
 
 int isflagset( struct sip_msg* msg, flag_t flag ) {
-	return msg->flags & (1<<flag) ? 1 : -1;
+	return (msg->flags & (1<<flag)) ? 1 : -1;
 }
 
 int flag_in_range( flag_t flag ) {

+ 50 - 8
forward.c

@@ -13,6 +13,7 @@
 #include <arpa/inet.h>
 
 #include "forward.h"
+#include "hash_func.h"
 #include "config.h"
 #include "parser/msg_parser.h"
 #include "route.h"
@@ -67,6 +68,7 @@ int forward_request( struct sip_msg* msg, struct proxy_l * p)
 	char* buf;
 	union sockaddr_union* to;
 	struct socket_info* send_sock;
+	char md5[MD5_LEN];
 	
 	to=0;
 	buf=0;
@@ -98,13 +100,39 @@ int forward_request( struct sip_msg* msg, struct proxy_l * p)
 		LOG(L_ERR, "forward_req: ERROR: cannot forward to af %d "
 				"no coresponding listening socket\n", to->s.sa_family);
 		ser_error=E_NO_SOCKET;
-		goto error;
+		goto error1;
 	}
-	
+
+	/* calculate branch for outbound request;  if syn_branch is turned off,
+	   calculate is from transaction key, i.e., as an md5 of From/To/CallID/
+	   CSeq exactly the same way as TM does; good for reboot -- than messages
+	   belonging to transaction lost due to reboot will still be forwarded
+	   with the same branch parameter and will be match-able downstream
+
+       if it is turned on, we don't care about reboot; we simply put a simple
+	   value in there; better for performance
+	*/
+
+	if (syn_branch ) {
+		*msg->add_to_branch_s='0';
+		msg->add_to_branch_len=1;
+	} else {
+		if (!char_msg_val( msg, md5 )) 	{ /* parses transaction key */
+			LOG(L_ERR, "ERROR: forward_request: char_msg_val failed\n");
+			goto error1;
+		}
+		msg->hash_index=hash( msg->callid->body, get_cseq(msg)->number);
+		if (!branch_builder( msg->hash_index, 0, md5, 0 /* 0-th branch */,
+					msg->add_to_branch_s, &msg->add_to_branch_len )) {
+			LOG(L_ERR, "ERROR: forward_request: branch_builder failed\n");
+			goto error1;
+		}
+	}
+
 	buf = build_req_buf_from_sip_req( msg, &len, send_sock);
 	if (!buf){
-		LOG(L_ERR, "ERROR: forward_reply: building failed\n");
-		goto error;
+		LOG(L_ERR, "ERROR: forward_request: building failed\n");
+		goto error1;
 	}
 	 /* send it! */
 	DBG("Sending:\n%s.\n", buf);
@@ -116,7 +144,7 @@ int forward_request( struct sip_msg* msg, struct proxy_l * p)
 			p->errors++;
 			p->ok=0;
 			STATS_TX_DROPS;
-			goto error;
+			goto error1;
 	}
 	/* sent requests stats */
 	else STATS_TX_REQUEST(  msg->first_line.u.request.method_value );
@@ -124,13 +152,26 @@ int forward_request( struct sip_msg* msg, struct proxy_l * p)
 	free(to);
 	/* received_buf & line_buf will be freed in receiv_msg by free_lump_list*/
 	return 0;
+
+error1:
+	free(to);
 error:
 	if (buf) pkg_free(buf);
-	if (to) free(to);
 	return -1;
 }
 
 
+int update_sock_struct_from_ip( union sockaddr_union* to,
+	struct sip_msg *msg )
+{
+	to->sin.sin_port=(msg->via1->port)
+		?htons(msg->via1->port): htons(SIP_PORT);
+	to->sin.sin_family=msg->src_ip.af;
+	memcpy(&to->sin.sin_addr, &msg->src_ip.u, msg->src_ip.len);
+
+	return 1;
+}
+
 int update_sock_struct_from_via( union sockaddr_union* to,
 								 struct via_body* via )
 {
@@ -252,9 +293,10 @@ int forward_reply(struct sip_msg* msg)
 			if (mod->exports->response_f(msg)==0) goto skip;
 		}
 	}
-	
+
 	/* we have to forward the reply stateless, so we need second via -bogdan*/
-	if ((msg->via2==0) || (msg->via2->error!=PARSE_OK))
+	if (parse_headers( msg, HDR_VIA2, 0 )==-1 
+		|| (msg->via2==0) || (msg->via2->error!=PARSE_OK))
 	{
 		/* no second via => error */
 		LOG(L_ERR, "ERROR: forward_msg: no 2nd via found in reply\n");

+ 2 - 0
forward.h

@@ -16,6 +16,8 @@ struct socket_info* get_send_socket(union sockaddr_union* su);
 int forward_request( struct sip_msg* msg,  struct proxy_l* p);
 int update_sock_struct_from_via( union sockaddr_union* to,
 								struct via_body* via );
+int update_sock_struct_from_ip( union sockaddr_union* to,
+    struct sip_msg *msg );
 int forward_reply( struct sip_msg* msg);
 
 #endif

+ 12 - 1
globals.h

@@ -44,8 +44,10 @@ extern int children_no;
 extern int dont_fork;
 extern int check_via;
 extern int received_dns;
-extern int loop_checks;
+extern int syn_branch;
 extern int process_no;
+extern int sip_warning;
+extern int server_signature;
 /*
  * debug & log_stderr moved to dprint.h*/
 
@@ -57,4 +59,13 @@ extern unsigned int msg_no;
 
 extern unsigned int shm_mem_size;
 
+/* FIFO server config */
+char extern *fifo; /* FIFO name */
+extern int fifo_mode;
+
+extern int *pids;
+extern int process_no;
+
+extern int reply_to_via;
+
 #endif

+ 48 - 27
main.c

@@ -36,6 +36,7 @@
 #include "resolve.h"
 #include "parser/parse_hname2.h"
 #include "parser/digest/digest_parser.h"
+#include "fifo_server.h"
 
 
 #include "stats.h"
@@ -93,21 +94,6 @@ static char flags[]=
 #ifdef DEBUG_DMALLOC
 ", DEBUG_DMALLOC"
 #endif
-#ifdef SILENT_FR
-", SILENT_FR"
-#endif
-#ifdef USE_SYNONIM
-", USE_SYNONIM"
-#endif
-#ifdef NOISY_REPLIES
-", NOISY_REPLIES"
-#endif
-#ifdef VERY_NOISY_REPLIES
-", VERY_NOISY_REPLIES"
-#endif
-#ifdef NEW_HNAME
-", NEW_HNAME"
-#endif
 #ifdef FAST_LOCK
 ", FAST_LOCK"
 #ifdef BUSY_WAIT
@@ -125,7 +111,6 @@ static char flags[]=
 static char help_msg[]= "\
 Usage: " NAME " -l address [-p port] [-l address [-p port]...] [options]\n\
 Options:\n\
-    -c           Perform loop checks and compute branches\n\
     -f file      Configuration file (default " CFG_FILE ")\n\
     -p port      Listen on the specified port (default: 5060)\n\
                  applies to the last address in -l and to all \n\
@@ -173,8 +158,8 @@ void print_ct_constants()
 #endif
 */
 	printf("MAX_RECV_BUFFER_SIZE %d, MAX_LISTEN %d,"
-			" MAX_URI_SIZE %d, MAX_PROCESSES %d, BUF_SIZE %d\n",
-		MAX_RECV_BUFFER_SIZE, MAX_LISTEN, MAX_URI_SIZE, MAX_PROCESSES,
+			" MAX_URI_SIZE %d, BUF_SIZE %d\n",
+		MAX_RECV_BUFFER_SIZE, MAX_LISTEN, MAX_URI_SIZE, 
 		BUF_SIZE );
 }
 
@@ -209,14 +194,29 @@ int sig_flag = 0;              /* last signal received */
 int debug = 0;
 int dont_fork = 0;
 int log_stderr = 0;
-int check_via =  0;        /* check if reply first via host==us */
-int loop_checks = 0;	/* calculate branches and check for loops/spirals */
-int received_dns = 0;      /* use dns and/or rdns or to see if we need to 
-                              add a ;received=x.x.x.x to via: */
+/* check if reply first via host==us */
+int check_via =  0;        
+/* shall use stateful synonym branches? faster but not reboot-safe */
+int syn_branch = 0;
+/* should replies include extensive warnings? by default yes,
+   good for trouble-shooting
+*/
+int sip_warning = 1;
+/* should localy-generated messages include server's signature?
+   be default yes, good for trouble-shooting
+*/
+int server_signature=1;
+/* use dns and/or rdns or to see if we need to add 
+   a ;received=x.x.x.x to via: */
+int received_dns = 0;      
 char* working_dir = 0;
 char* chroot_dir = 0;
 int uid = 0;
 int gid = 0;
+/* a hint to reply modules whether they should send reply
+   to IP advertised in Via or IP from which a request came
+*/
+int reply_to_via=0;
 
 #if 0
 char* names[MAX_LISTEN];              /* our names */
@@ -497,6 +497,12 @@ int main_loop()
 			LOG(L_ERR, "init_child failed\n");
 			goto error;
 		}
+
+		/* if configured to do so, start a server for accepting FIFO commands */
+		if (open_fifo_server()<0) {
+			LOG(L_ERR, "opening fifo server failed\n");
+			goto error;
+		}
 		is_main=1; /* hack 42: call init_child with is_main=0 in case
 					 some modules wants to fork a child */
 		
@@ -543,6 +549,11 @@ int main_loop()
 			/*close(udp_sock)*/; /*if it's closed=>sendto invalid fd errors?*/
 		}
 	}
+	/* if configured to do so, start a server for accepting FIFO commands */
+	if (open_fifo_server()<0) {
+		LOG(L_ERR, "opening fifo server failed\n");
+		goto error;
+	}
 	/*this is the main process*/
 	pids[process_no]=getpid();
 	process_bit = 0;
@@ -590,6 +601,7 @@ int main_loop()
 static void sig_usr(int signo)
 {
 
+
 	if (is_main){
 		if (sig_flag==0) sig_flag=signo;
 		else /*  previous sig. not processed yet, ignoring? */
@@ -604,6 +616,11 @@ static void sig_usr(int signo)
 			case SIGINT:
 			case SIGPIPE:
 			case SIGTERM:
+					/* print memory stats for non-main too */
+					#ifdef PKG_MALLOC
+					LOG(L_INFO, "Memory status (pkg):\n");
+					pkg_status();
+					#endif
 					exit(0);
 					break;
 			case SIGUSR1:
@@ -675,7 +692,7 @@ int main(int argc, char** argv)
 #ifdef STATS
 	"s:"
 #endif
-	"f:p:m:b:l:n:rRvcdDEVhw:t:u:g:P:";
+	"f:p:m:b:l:n:rRvdDEVhw:t:u:g:P:";
 	
 	while((c=getopt(argc,argv,options))!=-1){
 		switch(c){
@@ -748,9 +765,6 @@ int main(int argc, char** argv)
 			case 'v':
 					check_via=1;
 					break;
-			case 'c':
-					loop_checks=1;
-					break;
 			case 'r':
 					received_dns|=DO_DNS;
 					break;
@@ -847,6 +861,11 @@ int main(int argc, char** argv)
 		goto error;
 	}
 
+	/* register a diagnostic FIFO command */
+	if (register_fifo_cmd(print_fifo_cmd, "print", 0)<0) {
+		LOG(L_CRIT, "unable to register 'print' FIFO cmd\n");
+		goto error;
+	}
 
 	/*register builtin  modules*/
 	register_builtin_modules();
@@ -869,17 +888,19 @@ int main(int argc, char** argv)
 
 	
 	if (children_no<=0) children_no=CHILD_NO;
+#ifdef _OBSOLETED
 	else if (children_no >= MAX_PROCESSES ) {
 		fprintf(stderr, "ERROR: too many children processes configured;"
 				" maximum is %d\n",
 			MAX_PROCESSES-1 );
 		goto error;
 	}
+#endif
 	
 	if (working_dir==0) working_dir="/";
 	/*alloc pids*/
 #ifdef SHM_MEM
-	pids=shm_malloc(sizeof(int)*(children_no+1));
+	pids=shm_malloc(sizeof(int)*(children_no+1/*timer */+1/*fifo*/));
 #else
 	pids=malloc(sizeof(int)*(children_no+1));
 #endif

+ 2 - 3
md5utils.c

@@ -80,8 +80,7 @@ void MDStringArray (char *dst, str src[], int size)
   }
   MDFinal (digest, &context);
 
-  for (i=0; i<16; i++)
-    sprintf(dst+i*2, "%02x", digest[i] );
+  string2hex(digest, 16, dst );
+  DBG("DEBUG: MD5 calculated: %.*s\n", MD5_LEN, dst );
 
-  DBG("DEBUG: MD5 calculated: %32s\n", dst );
 }

+ 6 - 0
mem/mem.c

@@ -2,8 +2,10 @@
  * $Id *
  */
 
+#include <stdio.h>
 #include "../config.h"
 #include "../dprint.h"
+#include "../globals.h"
 #include "mem.h"
 
 #ifdef PKG_MALLOC
@@ -42,6 +44,8 @@ int init_mallocs()
 	#endif
 	if (mem_block==0){
 		LOG(L_CRIT, "could not initialize memory pool\n");
+		fprintf(stderr, "Too much pkg memory demanded: %d\n",
+			PKG_MEM_POOL_SIZE );
 		return -1;
 	}
 #endif
@@ -49,6 +53,8 @@ int init_mallocs()
 #ifdef SHM_MEM
 	if (shm_mem_init()<0) {
 		LOG(L_CRIT, "could not initialize shared memory pool, exiting...\n");
+		 fprintf(stderr, "Too much shared memory demanded: %d\n",
+			shm_mem_size );
 		return -1;
 	}
 #endif

+ 6 - 1
mem/memtest.c

@@ -1,5 +1,10 @@
 #ifdef DBG_QM_MALLOC
 
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+
 #include "../globals.h"
 #include "../config.h"
 
@@ -33,7 +38,7 @@ void memtest()
                                                                 __LINE__);
 	char tst_mem[TEST_SIZE];
 	struct MY_BLOCK* mem_block;
-	char *p0,*p1,*p2,*p3,*p4,*p5,*p6,*p7,*p8,*p9;
+	char *p0,*p1,*p2,*p3,*p4,*p5,*p6/*,*p7,*p8,*p9*/;
 	int i, j, f;
 	char *p[TEST_RUN];
 	int t;

+ 37 - 2
mem/shm_mem.c

@@ -5,6 +5,8 @@
 
 #ifdef SHM_MEM
 
+#include <stdlib.h>
+
 #include "shm_mem.h"
 #include "../config.h"
 #include "../globals.h"
@@ -81,6 +83,37 @@ inline static void* sh_realloc(void* p, unsigned int size)
     guarantee for buffer content; if allocation fails, we return
     NULL
 */
+
+#ifdef DBG_QM_MALLOC
+void* _shm_resize( void* p, unsigned int s, char* file, char* func, unsigned int line)
+#else
+void* _shm_resize( void* p , unsigned int s)
+#endif
+{
+#ifdef VQ_MALLOC
+	struct vqm_frag *f;
+#endif
+	if (p==0) {
+		DBG("WARNING:vqm_resize: resize(0) called\n");
+		return shm_malloc( s );
+	}
+#	ifdef DBG_QM_MALLOC
+#	ifdef VQ_MALLOC
+	f=(struct  vqm_frag*) ((char*)p-sizeof(struct vqm_frag));
+	DBG("_shm_resize(%p, %d), called from %s: %s(%d)\n",  
+		p, s, file, func, line);
+	VQM_DEBUG_FRAG(shm_block, f);
+	if (p>(void *)shm_block->core_end || p<(void*)shm_block->init_core){
+		LOG(L_CRIT, "BUG: vqm_free: bad pointer %p (out of memory block!) - "
+				"aborting\n", p);
+		abort();
+	}
+#endif
+#	endif
+	return sh_realloc( p, s ); 
+}
+
+#ifdef _OBSOLETED
 #ifdef DBG_QM_MALLOC
 void* _shm_resize( void* p, unsigned int s, char* file, char* func, unsigned int line)
 #else
@@ -108,10 +141,11 @@ void* _shm_resize( void* p , unsigned int s)
 #	ifdef VQ_MALLOC
 	f=(struct  vqm_frag*) ((char*)p-sizeof(struct vqm_frag));
 #	ifdef DBG_QM_MALLOC
-	DBG("_shm_resize(%x, %d), called from %s: %s(%d)\n",  p, s, file, func, line);
+	DBG("_shm_resize(%p, %d), called from %s: %s(%d)\n",  
+		p, s, file, func, line);
 	VQM_DEBUG_FRAG(shm_block, f);
 	if (p>(void *)shm_block->core_end || p<(void*)shm_block->init_core){
-		LOG(L_CRIT, "BUG: vqm_free: bad pointer %x (out of memory block!) - "
+		LOG(L_CRIT, "BUG: vqm_free: bad pointer %p (out of memory block!) - "
 				"aborting\n", p);
 		abort();
 	}
@@ -126,6 +160,7 @@ void* _shm_resize( void* p , unsigned int s)
 	/* we can't make the request happy with current size */
 	return sh_realloc( p, s ); 
 }
+#endif
 
 
 

+ 21 - 16
mem/vq_malloc.c

@@ -49,6 +49,8 @@
 
 #ifdef VQ_MALLOC
 
+#include <stdlib.h>
+
 #include "../config.h"
 #include "../globals.h"
 #include "vq_malloc.h"
@@ -79,7 +81,7 @@ void my_assert( int assertation, int line, char *file, char *function )
 {
 	if (assertation) return;
 
-	LOG(L_CRIT,"CRIT: assertation failed in $s (%s:%d)\n",
+	LOG(L_CRIT,"CRIT: assertation failed in %s (%s:%d)\n",
 		function, file, line);
 	abort();
 }
@@ -88,16 +90,15 @@ void my_assert( int assertation, int line, char *file, char *function )
 void vqm_debug_frag(struct vqm_block* qm, struct vqm_frag* f)
 {
 
-	int r;
 
 	if (f->check!=ST_CHECK_PATTERN){
-		LOG(L_CRIT, "BUG: vqm_*: fragm. %x beginning overwritten(%x)!\n",
+		LOG(L_CRIT, "BUG: vqm_*: fragm. %p beginning overwritten(%x)!\n",
 				f, f->check);
 		vqm_status(qm);
 		abort();
 	};
 	if (memcmp(f->end_check, END_CHECK_PATTERN, END_CHECK_PATTERN_LEN)!=0) {
-		LOG(L_CRIT, "BUG: vqm_*: fragm. %x end overwritten(%*s)!\n",
+		LOG(L_CRIT, "BUG: vqm_*: fragm. %p end overwritten(%*s)!\n",
 				f, END_CHECK_PATTERN_LEN, f->end_check );
 		vqm_status(qm);
 		abort();
@@ -235,7 +236,6 @@ static inline void vqm_detach_free( struct vqm_block* qm, struct vqm_frag* frag)
 {
 
 	struct vqm_frag *prev, *next;
-	struct vqm_frag_end *end;
 
 	prev=FRAG_END(frag)->prv_free; 
 	next=frag->u.nxt_free;
@@ -260,8 +260,8 @@ void* vqm_malloc(struct vqm_block* qm, unsigned int size)
 	
 #ifdef DBG_QM_MALLOC
 	unsigned int demanded_size;
-	DBG("vqm_malloc(%x, %d) called from %s: %s(%d)\n", qm, size, file, func,
-			line);
+	DBG("vqm_malloc(%p, %d) called from %s: %s(%d)\n", qm, size, file,
+	 func, line);
 	demanded_size = size;
 #endif
 	new_chunk=0;
@@ -285,7 +285,11 @@ void* vqm_malloc(struct vqm_block* qm, unsigned int size)
 
 	if (!new_chunk) { /* no chunk can be reused; slice one from the core */
 		new_chunk=MORE_CORE( qm, bucket, size );
-		if (!new_chunk) return 0;
+		if (!new_chunk) {
+			LOG(L_ERR, "vqm_malloc(%p, %d) called from %s: %s(%d)\n", 
+				qm, size, file, func, line);
+			return 0;
+		}
 	}
 	new_chunk->u.inuse.magic = FR_USED;
 	new_chunk->u.inuse.bucket=bucket;
@@ -295,7 +299,7 @@ void* vqm_malloc(struct vqm_block* qm, unsigned int size)
 	new_chunk->line=line;
 	new_chunk->demanded_size=demanded_size;
 	qm->usage[ bucket ]++;
-	DBG("vqm_malloc( %x, %d ) returns address %x in bucket %d, real-size %d \n",
+	DBG("vqm_malloc( %p, %d ) returns address %p in bucket %d, real-size %d \n",
 		qm, demanded_size, (char*)new_chunk+sizeof(struct vqm_frag), 
 		bucket, size );
 
@@ -317,9 +321,10 @@ void vqm_free(struct vqm_block* qm, void* p)
 	unsigned char b;
 
 #ifdef DBG_QM_MALLOC
-	DBG("vqm_free(%x, %x), called from %s: %s(%d)\n", qm, p, file, func, line);
+	DBG("vqm_free(%p, %p), called from %s: %s(%d)\n", 
+		qm, p, file, func, line);
 	if (p>(void *)qm->core_end || p<(void*)qm->init_core){
-		LOG(L_CRIT, "BUG: vqm_free: bad pointer %x (out of memory block!) - "
+		LOG(L_CRIT, "BUG: vqm_free: bad pointer %p (out of memory block!) - "
 				"aborting\n", p);
 		abort();
 	}
@@ -389,13 +394,13 @@ void vqm_free(struct vqm_block* qm, void* p)
 
 void dump_frag( struct vqm_frag* f, int i )
 {
-	LOG(L_INFO, "    %3d. address=%x  real size=%d bucket=%d\n", i, 
+	LOG(L_INFO, "    %3d. address=%p  real size=%d bucket=%d\n", i, 
 		(char*)f+sizeof(struct vqm_frag), f->size, f->u.inuse.bucket);
 #ifdef DBG_QM_MALLOC
 	LOG(L_INFO, "            demanded size=%d\n", f->demanded_size );
 	LOG(L_INFO, "            alloc'd from %s: %s(%d)\n",
 		f->file, f->func, f->line);
-	LOG(L_INFO, "        start check=%x, end check= %*s\n",
+	LOG(L_INFO, "        start check=%x, end check= %.*s\n",
 			f->check, END_CHECK_PATTERN_LEN, f->end_check );
 #endif
 }
@@ -403,9 +408,9 @@ void dump_frag( struct vqm_frag* f, int i )
 void vqm_status(struct vqm_block* qm)
 {
 	struct vqm_frag* f;
-	unsigned int i,j,on_list;
+	unsigned int i,on_list;
 
-	LOG(L_INFO, "vqm_status (%x):\n", qm);
+	LOG(L_INFO, "vqm_status (%p):\n", qm);
 	if (!qm) return;
 	LOG(L_INFO, " heap size= %d, available: %d\n", 
 		qm->core_end-qm->init_core, qm->free_core );
@@ -422,7 +427,7 @@ void vqm_status(struct vqm_block* qm)
 	DBG("dumping bucket statistics:\n");
 	for (i=0; i<=BIG_BUCKET(qm); i++) {
 		for(on_list=0, f=qm->next_free[i]; f; f=f->u.nxt_free ) on_list++;
-		LOG(L_DBG, "    %3d. bucket: in use: %d, on free list: %d\n", 
+		LOG(L_DBG, "    %3d. bucket: in use: %ld, on free list: %d\n", 
 			i, qm->usage[i], on_list );
 	}
 #endif

+ 1 - 0
mem/vq_malloc.h

@@ -113,6 +113,7 @@ struct vqm_block{
 struct vqm_block* vqm_malloc_init(char* address, unsigned int size);
 
 #ifdef DBG_QM_MALLOC
+void vqm_debug_frag(struct vqm_block* qm, struct vqm_frag* f);
 void* vqm_malloc(struct vqm_block*, unsigned int size, char* file, char* func, 
 					unsigned int line);
 void  vqm_free(struct vqm_block*, void* p, char* file, char* func, 

+ 303 - 5
modules/tm/README

@@ -1,5 +1,303 @@
-Issues
-- currently, each process keeps a T with properly
-  increased ref_count -> it can never be deleted
-  by the delete timer
-  Action: none, we don't mind
+#
+# $Id$
+#
+# TM Module README
+#
+# Module depends on: none
+#
+
+TM Module enables stateful processing of SIP transactions.
+The main use of stateful logic, which is costly in terms of
+memory and CPU, is some services inherently need state.
+For example, transaction-based accounting (module acc) needs
+to process transaction state as opposed to individual messages,
+and any kinds of forking must be implemented statefuly.
+Other use of stateful processing is it trading CPU caused by 
+retransmission processing for memory. That makes however only
+sense if CPU consumption per request is huge. For example,
+if you want to avoid costly DNS resolution for every retransmission
+of a request to an unresolveable destination, use stateful mode.
+Then, only the initial message burdens server by DNS queries,
+subsequent retranmissions will be dropped and will not result in
+more processes blocked by DNS resolution. The price is more 
+memory consumption and higher processing latency.
+
+From user's perspective, there are two major functions :
+t_relay and t_relay_to. Both setup transaction state,
+absorb retransmissions from upstream, generate downstream
+retransmissions and correlate replies to requests.
+t_relay forwards to current uri (be it original request's
+uri or a uri changed by some of uri-modifying functions,
+such as sethost). t_relay_to forwards to a specific address.
+
+In general, if TM is used, it copies clones of received SIP
+messages in shared memory. That costs the memory and also CPU
+time (memcpys, lookups, shmem locks, etc.) Note that non-TM
+functions operate over the received message in private memory,
+that means that any core operations will have no effect on
+statefuly processed messages after creating the transactional
+state. For example, calling addRecordRoute *after* t_relay
+is pretty useless, as the RR is added to privately held message
+whereas its TM clone is being forwarded.
+
+TM is quite big and uneasy to programm -- lot of mutexes, shared
+memory access, malloc & free, timers -- you really need to be careful
+when you do anything. To simplify TM programming, there is the
+instrument of callbacks. The callback mechanisms allow programmers
+to register their functions to specific event. See t_hooks.h
+for a list of possible events.
+
+Other things programmers may want to know is UAC -- it is 
+a very simplictic code which allows you to generate your own
+transactions. Particularly useful for things like NOTIFYs or
+IM gateways. The UAC takes care of all the transaction
+machinery: retransmissions , FR timeouts, forking, etc.
+See t_uac prototype in uac.h for more details. Who wants to
+see the transaction result may register for a callback.
+
+Exported parameters:
+--------------------
+
+Name:		fr_timer
+Type:		int (seconds)
+Default:	FR_TIME_OUT=30
+Desc:		timer which hits if no final reply for a request or
+			ACK for a negative INVITE reply arrives
+
+Name:		fr_inv_timer
+Type:		int(seconds)
+Default:	INV_FR_TIME_OUT=120
+Desc:		timer which hits if no final reply for an INVITE
+			arrives after a provisional message was received
+
+Name:		wt_timer
+Type:		int (seconds)
+Default:	WT_TIME_OUT=5
+Desc:		time for which a transaction stays in memory to absorb
+			delayed messages after it completed; also, when this
+			timer hits, retransmission of local cancels is stopped
+			(a puristic but complex behviour would be not to enter
+			wait state until local branches are finished by a final
+			reply or FR timer -- we simplified)
+
+Name:		delete_timer
+Type:		int (seconds)
+Default:	DEL_TIME_OUT=2
+Desc:		time after which a to-be-deleted transaction currently
+			ref-ed by a process will be tried to be deleted again
+
+Name:		retr_timer1p1, 2, 3
+Type:		int (seconds)
+Default:	RETR_T1=1, 2*RETR_T1, 4*RETR_T1
+Desc:		retransmission period
+
+Name:		retr_timer2
+Type:		int (seconds)
+Default:	RETR_T2=4
+Desc:		maximum retransmission period
+
+Name:		noisy_ctimer
+Type:		int (boolean)
+Default:	0 (FALSE)
+Desc:		if set, on FR timer INVITE transactions will be 
+			explicitly cancelled if possible, silently dropped
+			otherwise; preferably, it is turned off to allow
+			very long ringing; this behaviour is overridden if
+			a request is forked, or some functionality explicitly
+			turned it off for a transaction (like acc does to avoid
+			unaccounted transactions due to expired timer)
+
+Exported Functions:
+-------------------
+
+For use in scripts, t_relay_to and t_relay are design. All other
+functions are advanced and should be used with care.
+
+Name: 	t_relay_to
+Params:	ip address, port number
+Desc:	relay a message statefuly to a fixed destination; this along with
+		t_relay is the function most users want to use -- all other are
+		mostly for programming; programmers interested in writing TM
+		logic should review how t_relay is implemented in tm.c and how
+		TM callbacks work
+
+Name:	t_relay
+Params:	0
+Desc:	relay a message statefuly to destination indicated in current URI;
+		(if the original URI was rewritten by UsrLoc, RR, strip/prefix,
+		etc., the new URI will be taken); returns a negative value on
+		failure -- you may still want to send a negative reply upstream
+		statelessly not to leave upstream UAC in lurch
+Example: if (!t_relay()) { sl_reply_error(); break; };
+
+Name:	t_on_negative
+Params:	reply_route
+Desc:	sets reply routing block, to which control is passed after
+		a transaction completed with a negative result but before
+		sending a final reply; In the refered block, you can
+		either start a new branch (good for services such as
+		forward_on_no_reply) or send a final reply on your own
+		(good for example for message silo, which received 
+		a negative reply from upstream and wants to tell
+		upstream "202 I will take care of it"); Note that the
+		set of command which are useable within reply_routes is
+		strictly limited to rewriting URI, initiating new branches,
+		logging, and sending 'unsafe' replies (t_reply_unsafe). Any 
+		other commands may result in unpredictable behaviour and 
+		possible server failure.
+		Note that whenever reply_route is entered, uri is reset to
+		value which it had on relaying. If it temporarily changed
+		during a reply_route processing, subsequent reply_route
+		will ignore the changed value and use again the original
+		one.
+Example: route { t_on_negative("1"); t_relay(); } reply_route[1] {
+			revert_uri(); setuser("voicemail"); append_branch(); }
+
+		see test/onr.cfg for a more complex example of combination
+		of serial with parallel forking
+
+
+Name:	append_branch (actually part of core now)
+Params:	uri
+Desc:	adds a new destination to destination set; if used,
+		a subsequent call to t_relay (or t_forward_nonack, 
+		on which t_relay is based) than introduces
+	    a new branch and forks a transaction; append_branch may
+		also be called from reply processing -- this may 
+	    be particularly useful for services such as
+		"fork_on_no_reply"
+
+Name:	append_branch
+Params:	0
+Desc:	similarly to t_fork_to, it extends destination set
+		by a new entry; the difference is that current uri
+		is taken as new entry;
+Example: set_user("john"); t_fork(); set_user("alice");
+		 t_fork(); t_relay();
+
+-----   ----  ---- medium-advanced commands here --- on -----
+
+Name:	t_newtran
+Params: 0
+Desc:	creates a new transaction, returns a negative value on 
+	    error; this is the only way a script can add a new transaction 
+	    in an atomic way; typically, it is used to deploy a UAS
+Example: see test/uas.cfg: 
+	    if (t_newtran()) { log("UAS logic"); t_reply("999","hello"); }
+		else sl_reply_error();
+
+Name:	t_reply
+Params:	code, reason phrase
+Desc: 	sends a stateful reply after a transaction has been
+		established; see t_newtran for usage; note: never use
+	    t_reply from within reply_route ... always use t_reply_unsafe
+
+----- only --- advanced --- commands --- from --- here --- on -----
+
+Name:	t_lookup_request
+Params:	0
+Desc:	checks if a transaction exists; returns a positive value
+		if so, negative otherwise; most likely you will not want
+	    to use it, as a typicall application of a looku-up is to
+	    introduce a new transaction if none was found; however
+	    this is safely (atomically) done using t_newtran
+
+
+Name:	t_retransmit_reply
+Params:	0
+Desc:	retransmits a reply sent previously by UAS transaction
+
+Name:	t_release
+Params:	0
+Desc:	remove transaction from memory (it will be first put on
+		a wait timer to absorb delayed messages)
+
+Name:	t_forward_nonack
+Params:	ip, port
+Desc:	mainly for internal -- forward a non-ACK request statefuly
+
+Name:	register_tmcb
+Params:	callback type, callback function
+Desc:	for programmatic use only -- register a function to be called
+		back on an event; see t_hooks.h for more details
+
+Name:	load_tm
+Params:	*import_structure
+Desc:	for programmatic use only -- import exported TM functions;
+		see the acc module for an example of use
+
+Name:	t_reply_unsafe
+Params:	code, reason phrase
+Desc: 	sends a stateful reply after a transaction has been
+		established; it can only be used from reply processing;
+	    using it from regular processing will introduce erroneous
+	    conditions; using t_reply from reply_processing will
+	    introduce a deadlock
+
+External Usage of TM
+---------------------
+There are applications which would like to generate SIP
+transactions without too big onvolvement in SIP stack, transaction
+management, etc. An example of such an application
+is sending instant messages from a website. To address needs
+of such apps, SER accepts requests for new transactions via
+fifo pipes too. If you want to enable this feature, statrt
+FIFO server by configuration option
+	fifo="/tmp/filename"
+Then, an application can easily launch a new transaction by
+writing a transaction request to this named pipe. The request
+must follow very simple format, whic is
+ :t_uac:[<file_name>]\n<method>\n<dst uri>\n<CR_separated_headers>\n<body>\n\n\n
+(Filename is to where a report will be dumped. ser assumes /tmp
+as file's directory.)
+
+A convenience library fifo_uac.c implements this simple functionality.
+Note the the request write must be atomic, otherwise the request
+might get intermixes with writes from other writers.
+You can easily use it via Unix command-line tools, see the following
+example:
+---
+[jiri@bat jiri]$ cat > /tmp/fifo
+:t_uac:xxx
+MESSAGE
+sip:[email protected]
+header:value
+foo:bar
+bznk:hjhjk
+p_header: p_value
+
+body body body
+yet body
+end of body
+
+
+
+---
+or use an example file and call cat test/transaction.fifo > /tmp/fifo
+
+
+Known Issues
+-----------
+- need to revisit profiling again
+- review whether there is not potential for to-tag
+  rewriting and ACK matching
+- we don't have authentication merging on forking
+- branch tid is not used yet
+- local ACK/CANCELs copy'n'pastes Route and ignores deleted
+  Routes
+- 6xx should be delayed 
+- possibly, performance could be improved by not parsing non-INVITEs,
+  as they do not be replied with 100, and do not result in ACK/CANCELs,
+  and other things which take parsing. However, we need to rethink
+  whether we don't need parsed headers later for something else.
+  Remember, when we now conserver a request in sh_mem, we can't apply
+  any pkg_mem operations to it any more. (that might be redesigned too)
+- t_replicate should be done more cleanly -- Vias, Routes, etc. should
+  be removed from a message prior to replicating it
+- SNMP support
+- lookup fails to recognize subsequent requests which have additional 
+  leading spaces in header field values
+- make UAC session-aware (as opposed to just transaction aware) -- needed
+  for keeing SUB-NOT dialog state, etc. Currently, there are only
+  place-holders for in in TM.
+- places labeled with "HACK" strongly deserve beautification

+ 0 - 15
modules/tm/TODO

@@ -1,15 +0,0 @@
-Things we have omitted for now:
-
-- we don't make a deep copy of msg->lump_list; that means
-  that all changes made prior to going stateful go lost
-- we don't generate CANCELs for forked requests
-- no ACK matching (waiting for To-tag)
-- no SIP-wise HF comparison within T-matching
-  (just memcmp)
-- 6xx should be delayed indeed
-- relaying CANCEL should be delayed until a reply to
-  INVITE received if not yet
-- ACK of 2xx INV caching (avoid e2e retransmission
-  of an ACK hit the proxy too)
-
-To-do: semaphore clean-up on exit (even better: w/sibling check)

+ 13 - 10
modules/tm/config.h

@@ -10,9 +10,14 @@
 #define T_TABLE_POWER    12
 #define TABLE_ENTRIES    (1 << (T_TABLE_POWER))
 
-/* maximum number of forks per transaction */
-enum fork_list { MAX_FORK=4 , NO_RPL_BRANCH ,NR_OF_CLIENTS};
-enum fork_type { DEFAULT, NO_RESPONSE };
+/* this is where table size is defined now -- sort of
+   ugly, core should not be bothered by TM table size,
+   but on the other, core's stateless forwarding should 
+   have consistent branch generation with stateful mode
+   and needs to calculate branch/hash, for which table size
+   is needed 
+*/
+#include "../../hash_func.h"
 
 /* maximumum length of localy generated acknowledgement */
 #define MAX_ACK_LEN   1024
@@ -41,11 +46,9 @@ enum fork_type { DEFAULT, NO_RESPONSE };
 #define REPLY_OVERBUFFER_LEN 160
 #define TAG_OVERBUFFER_LEN 32
 
-/* character which separates individual parts of MPLS-ized branch */
-#ifdef BRUT_HACK
-#	define BRANCH_SEPARATOR 'X'
-#else
-#	define BRANCH_SEPARATOR '.'
-#endif
-
+/* dimensions of FIFO server */
+#define MAX_METHOD	64
+#define MAX_HEADER	1024
+#define MAX_BODY	1024
+#define MAX_DST	512
 #endif

+ 53 - 0
modules/tm/fix_lumps.h

@@ -0,0 +1,53 @@
+/*
+ * $Id$
+ *
+ * here, we delete message lumps which are generated in
+ * core functions using pkg_malloc and applied to shmem
+ * requests; not doing so would result ugly memory problems
+ *
+ * I admit it is not a nice hack; -jiri 
+ */
+
+#ifndef _FIX_LUMPS_H
+#define _FIX_LUMPS_H
+
+/* used to delete attached via lumps from msg; msg can
+   be either an original pkg msg, whose Via lump I want
+   to delete before generating next branch, or a shmem-stored
+   message processed during on_reply -- then I want to
+   delete the Via lump for the same reason
+
+   the other case when I want to delete them is when a message
+   is stored in shmem for branch picking, forwarded lated and
+   Via removal is applied to the shmem-ed message
+*/
+inline static void free_via_lump( struct lump **list )
+{
+	struct lump *prev_lump, *lump, *a, *foo;
+
+	prev_lump=0;
+	for(lump=*list;lump;lump=lump->next) {
+		if (lump->type==HDR_VIA) {
+			a=lump->before;
+			while(a) {
+				foo=a; a=a->before;
+				free_lump(foo);
+				pkg_free(foo);
+			}
+			a=lump->after;
+			while(a) {
+				foo=a; a=a->after;
+				free_lump(foo);
+				pkg_free(foo);
+			}
+			if (prev_lump) prev_lump->next = lump->next;
+			else *list = lump->next;
+			free_lump(lump);pkg_free(lump);
+		} else {
+			/* store previous position */
+			prev_lump=lump;
+		}
+	}
+}
+
+#endif

+ 96 - 63
modules/tm/h_table.c

@@ -2,15 +2,28 @@
  * $Id$
  */
 
-#include "hash_func.h"
+#include "../../mem/shm_mem.h"
+#include "../../hash_func.h"
 #include "h_table.h"
 #include "../../dprint.h"
-#include "sh_malloc.h"
 #include "../../md5utils.h"
 /* bogdan test */
 #include "../../ut.h"
 #include "../../globals.h"
 #include "../../error.h"
+#include "t_reply.h"
+#include "t_cancel.h"
+
+unsigned int transaction_count( void )
+{
+	unsigned int i;
+	unsigned int count;
+
+	count=0;	
+	for (i=0; i<TABLE_ENTRIES; i++) 
+		count+=hash_table->entrys[i].entries;
+	return count;
+}
 
 
 
@@ -18,6 +31,7 @@ void free_cell( struct cell* dead_cell )
 {
 	char *b;
 	int i;
+	struct sip_msg *rpl;
 
 	release_cell_lock( dead_cell );
 	shm_lock();
@@ -27,31 +41,35 @@ void free_cell( struct cell* dead_cell )
 		sip_msg_free_unsafe( dead_cell->uas.request );
 	if ( dead_cell->uas.response.buffer )
 		shm_free_unsafe( dead_cell->uas.response.buffer );
+	if (dead_cell->uas.to_tag.s)
+		shm_free_unsafe(dead_cell->uas.to_tag.s);
+
+	/* completion callback */
+	if (dead_cell->cbp) shm_free_unsafe(dead_cell->cbp);
 
 	/* UA Clients */
 	for ( i =0 ; i<dead_cell->nr_of_outgoings;  i++ )
 	{
 		/* retransmission buffer */
 		if ( (b=dead_cell->uac[i].request.buffer) )
-		{
 			shm_free_unsafe( b );
-			b = 0;
-		}
+#ifdef OLD_CANCEL
 		if ( (b=dead_cell->uac[i].request.ack) )
-		{
 			shm_free_unsafe( b );
-			b = 0;
-		}
 		if ( (b=dead_cell->uac[i].request.cancel) )
-		{
 			shm_free_unsafe( b );
-			b = 0;
+#endif
+		b=dead_cell->uac[i].local_cancel.buffer;
+		if (b!=0 && b!=BUSY_BUFFER)
+			shm_free_unsafe( b );
+		rpl=dead_cell->uac[i].reply;
+		if (rpl && rpl!=FAKED_REPLY) {
+			sip_msg_free_unsafe( rpl );
 		}
+#ifdef _OBSOLETED
 		if ( (b=dead_cell->uac[i].rpl_buffer.s) )
-		{
 			shm_free_unsafe( b );
-			b = 0;
-		}
+#endif
 	}
 
 	/* the cell's body */
@@ -67,16 +85,16 @@ struct cell*  build_cell( struct sip_msg* p_msg )
 {
 	struct cell* new_cell;
 	unsigned int i;
-#ifndef USE_SYNONIM
-	str          src[8];
-#endif
+	unsigned int rand;
+	int size;
+	char *c;
+	struct ua_client *uac;
 
-	/* do we have the source for the build process? */
-	if (!p_msg)
-		return NULL;
+	/* avoid 'unitialized var use' warning */
+	rand=0;
 
 	/* allocs a new cell */
-	new_cell = (struct cell*)sh_malloc( sizeof( struct cell ) );
+	new_cell = (struct cell*)shm_malloc( sizeof( struct cell ) );
 	if  ( !new_cell ) {
 		ser_error=E_OUT_OF_MEM;
 		return NULL;
@@ -90,66 +108,75 @@ struct cell*  build_cell( struct sip_msg* p_msg )
 	new_cell->uas.response.fr_timer.tg=TG_FR;
 	new_cell->uas.response.fr_timer.payload =
 		new_cell->uas.response.retr_timer.payload = &(new_cell->uas.response);
+	new_cell->uas.response.my_T=new_cell;
 
 	/* bogdan - debug */
 	/*fprintf(stderr,"before clone VIA |%.*s|\n",via_len(p_msg->via1),
 		via_s(p_msg->via1,p_msg));*/
 
-	new_cell->uas.request = sip_msg_cloner(p_msg);
-
-    /* bogdan - debug */
-    /*fprintf(stderr,"after clone VIA |%.*s|\n",
-		via_len(new_cell->uas.request->via1),
-		via_s(new_cell->uas.request->via1,new_cell->uas.request) );*/
+	if (p_msg) {
+		new_cell->uas.request = sip_msg_cloner(p_msg);
+		if (!new_cell->uas.request)
+			goto error;
+	}
 
-	if (!new_cell->uas.request)
-		goto error;
-	new_cell->uas.tag = &( get_to(new_cell->uas.request)->tag_value );
+	/* new_cell->uas.to_tag = &( get_to(new_cell->uas.request)->tag_value ); */
 	new_cell->uas.response.my_T = new_cell;
 
 	/* UAC */
-	for(i=0;i<MAX_FORK;i++)
+	for(i=0;i<MAX_BRANCHES;i++)
 	{
-		new_cell->uac[i].request.my_T = new_cell;
-		new_cell->uac[i].request.branch = i;
-		new_cell->uac[i].request.fr_timer.tg = TG_FR;
-		new_cell->uac[i].request.retr_timer.tg = TG_RT;
-		new_cell->uac[i].request.retr_timer.payload = 
-			new_cell->uac[i].request.fr_timer.payload =
-			&(new_cell->uac[i].request);
+		uac=&new_cell->uac[i];
+		uac->request.my_T = new_cell;
+		uac->request.branch = i;
+		uac->request.fr_timer.tg = TG_FR;
+		uac->request.retr_timer.tg = TG_RT;
+		uac->request.retr_timer.payload = 
+			uac->request.fr_timer.payload =
+			&uac->request;
+		uac->local_cancel=uac->request;
 	}
 
 	/* global data for transaction */
-	new_cell->hash_index = p_msg->hash_index;
+	if (p_msg) {
+		new_cell->hash_index = p_msg->hash_index;
+	} else {
+		rand = random();
+		new_cell->hash_index = rand % TABLE_ENTRIES ;
+	}
 	new_cell->wait_tl.payload = new_cell;
 	new_cell->dele_tl.payload = new_cell;
 	new_cell->relaied_reply_branch   = -1;
-	new_cell->T_canceled = T_UNDEFINED;
+	/* new_cell->T_canceled = T_UNDEFINED; */
 	new_cell->wait_tl.tg=TG_WT;
 	new_cell->dele_tl.tg=TG_DEL;
-#ifndef USE_SYNONIM
-	src[0]= p_msg->from->body;
-	src[1]= p_msg->to->body;
-	src[2]= p_msg->callid->body;
-	src[3]= p_msg->first_line.u.request.uri;
-	src[4]= get_cseq( p_msg )->number;
-
-	/* topmost Via is part of transaction key as well ! */
-	src[5]= p_msg->via1->host;
-	src[6]= p_msg->via1->port_str;
-	if (p_msg->via1->branch) {
-		src[7]= p_msg->via1->branch->value;
-		MDStringArray ( new_cell->md5, src, 8 );
-	} else {
-		MDStringArray ( new_cell->md5, src, 7 );
+
+	if (!syn_branch) {
+		if (p_msg) {
+			/* char value of a proxied transaction is
+			   calculated out of header-fileds forming
+			   transaction key
+			*/
+			char_msg_val( p_msg, new_cell->md5 );
+		} else {
+			/* char value for a UAC transaction is created
+			   randomly -- UAC is an originating stateful element 
+			   which cannot be refreshed, so the value can be
+			   anything
+			*/
+			/* HACK : not long enough */
+			c=new_cell->md5;
+			size=MD5_LEN;
+			memset(c, '0', size );
+			int2reverse_hex( &c, &size, rand );
+		}
 	}
- #endif
 
 	init_cell_lock(  new_cell );
 	return new_cell;
 
 error:
-	sh_free(new_cell);
+	shm_free(new_cell);
 	return NULL;
 }
 
@@ -183,7 +210,7 @@ void free_hash_table( struct s_table *hash_table )
 		for ( i=0 ; i<NR_OF_TIMER_LISTS ; i++ )
 			release_timerlist_lock( &(hash_table->timers[i]) );
 
-		sh_free( hash_table );
+		shm_free( hash_table );
 	}
 }
 
@@ -198,7 +225,7 @@ struct s_table* init_hash_table()
 	int              i;
 
 	/*allocs the table*/
-	hash_table = (struct s_table*)sh_malloc( sizeof( struct s_table ) );
+	hash_table = (struct s_table*)shm_malloc( sizeof( struct s_table ) );
 	if ( !hash_table )
 		goto error;
 
@@ -248,6 +275,9 @@ void insert_into_hash_table_unsafe( struct s_table *hash_table,
 	} else p_entry->first_cell = p_cell;
 
 	p_entry->last_cell = p_cell;
+
+	/* update stats */
+	p_entry->entries++;
 }
 
 
@@ -255,21 +285,22 @@ void insert_into_hash_table_unsafe( struct s_table *hash_table,
 
 void insert_into_hash_table(struct s_table *hash_table,  struct cell * p_cell)
 {
-	lock( &(hash_table->entrys[ p_cell->hash_index ].mutex) );
+	LOCK_HASH(p_cell->hash_index);
 	insert_into_hash_table_unsafe( hash_table,  p_cell );
-	unlock( &(hash_table->entrys[ p_cell->hash_index ].mutex) );
+	UNLOCK_HASH(p_cell->hash_index);
 }
 
 
 
 
 /*  Un-link a  cell from hash_table, but the cell itself is not released */
-void remove_from_hash_table(struct s_table *hash_table,  struct cell * p_cell)
+void remove_from_hash_table_unsafe(struct s_table *hash_table,  
+ struct cell * p_cell)
 {
 	struct entry*  p_entry  = &(hash_table->entrys[p_cell->hash_index]);
 
 	/* unlink the cell from entry list */
-	lock( &(p_entry->mutex) );
+	/* lock( &(p_entry->mutex) ); */
 
 	if ( p_cell->prev_cell )
 		p_cell->prev_cell->next_cell = p_cell->next_cell;
@@ -280,8 +311,10 @@ void remove_from_hash_table(struct s_table *hash_table,  struct cell * p_cell)
 		p_cell->next_cell->prev_cell = p_cell->prev_cell;
 	else
 		p_entry->last_cell = p_cell->prev_cell;
+	/* update stats */
+	p_entry->entries--;
 
-	unlock( &(p_entry->mutex) );
+	/* unlock( &(p_entry->mutex) ); */
 }
 
 

+ 82 - 43
modules/tm/h_table.h

@@ -15,22 +15,19 @@
 #include "../../types.h"
 #include "../../md5utils.h"
 #include "config.h"
-/*#include "t_flags.h"*/
 
 struct s_table;
 struct entry;
 struct cell;
 struct timer;
+struct retr_buf;
 
-#include "sh_malloc.h"
-
-#include "timer.h"
+#include "../../mem/shm_mem.h"
+#include "timer.h" 
 #include "lock.h"
 #include "sip_msg.h"
-
-
-#define T_UNDEFINED  ( (struct cell*) -1 )
-#define T_NULL       ( (struct cell*) 0 )
+#include "t_reply.h"
+#include "t_hooks.h"
 
 
 #define NO_CANCEL       ( (char*) 0 )
@@ -39,7 +36,14 @@ struct timer;
 #define TYPE_LOCAL_CANCEL -1
 #define TYPE_REQUEST       0
 
-
+/* to be able to assess whether a script writer forgot to
+   release a transaction and leave it for ever in memory,
+   we mark it with operations done over it; if none of these
+   flags is set and script is being left, it is a sign of
+   script error and we need to release on writer's
+   behalf
+*/
+enum kill_reason { REQ_FWDED=1, REQ_RPLD=2, REQ_RLSD=4, REQ_EXIST=8 };
 
 typedef struct retr_buf
 {
@@ -49,18 +53,10 @@ typedef struct retr_buf
 
 	char *buffer;
 	int   buffer_len;
-	char *ack;
-	int   ack_len;
-	char *cancel;
-	int   cancel_len;
 
-	/* v6 changes; -jiri
-	struct sockaddr_in to; */
 	union sockaddr_union to;
 	struct socket_info* send_sock;
 
-	size_t tolen;
-
 	/* a message can be linked just to retransmission and FR list */
 	struct timer_link retr_timer;
 	struct timer_link fr_timer;
@@ -81,7 +77,7 @@ typedef struct ua_server
 	struct sip_msg   *request;
 	struct retr_buf  response;
 	unsigned int     status;
-	str              *tag;
+	str              to_tag;
 	unsigned int     isACKed;
 }ua_server_type;
 
@@ -92,11 +88,21 @@ typedef struct ua_server
 typedef struct ua_client
 {
 	struct retr_buf  request;
-	unsigned int     status;
-	str              tag;
+	/* we maintain a separate copy of cancel rather than
+	   reuse the strructure for original request; the 
+	   original request is no longer needed but its delayed
+	   timer may fire and interfere with whoever tries to
+	   rewrite it
+	*/
+	struct retr_buf local_cancel;
+	/* pointer to retransmission buffer where uri is printed;
+	   good for generating ACK/CANCEL */
 	str              uri;
-	str              rpl_buffer;
-	unsigned int     rpl_received;
+	/* if we store a reply (branch picking), this is where it is */
+	struct sip_msg 	*reply;
+	/* if we don't store, we at least want to know the status */
+	int	last_received;
+
 }ua_client_type;
 
 
@@ -109,8 +115,40 @@ typedef struct cell
 	struct cell*     next_cell;
 	struct cell*     prev_cell;
 
-	/* indicates which process is currently processing this transaction */
-	process_bm_t  ref_bitmap;
+	/* needed for generating local ACK/CANCEL for local
+	   transactions; all but cseq_n include the entire
+	   header field value, cseq_n only Cseq number; with
+	   local transactions, pointers point to outbound buffer,
+	   with proxied transactions to inbound request */
+	str from, callid, cseq_n, to;
+	/* a short-cut for remember whether this transaction needs
+	   INVITE-special handling (e.g., CANCEL, ACK, FR...)
+	*/
+	short is_invite;
+	/* method shortcut -- for local transactions, pointer to
+	   outbound buffer, for proxies transactions pointer to
+	   original message; needed for reply matching
+	*/
+	str method;
+
+	/* callback and parameter on completion of local transactions */
+	transaction_cb *completion_cb;
+	/* the parameter stores a pointer to shmem -- it will be released
+	   during freeing transaction too
+	*/
+	void *cbp;
+
+	/* how many processes are currently processing this transaction ;
+	   note that only processes working on a request/reply belonging
+	   to a transaction increase ref_count -- timers don't, since we
+	   rely on transaction state machine to clean-up all but wait timer
+	   when entering WAIT state and the wait timer is the only place
+	   from which a transaction can be deleted (if ref_count==0); good
+	   for protecting from conditions in which wait_timer hits and
+	   tries to delete a transaction whereas at the same time 
+	   a delayed message belonging to the transaction is received
+	*/
+	volatile unsigned int ref_count;
 	/* tells in which hash table entry the cell lives */
 	unsigned int  hash_index;
 	/* sequence number within hash collision slot */
@@ -120,47 +158,43 @@ typedef struct cell
 	struct timer_link wait_tl;
 	struct timer_link dele_tl;
 
-	/* useful data */
 	/* number of forks */
 	int nr_of_outgoings;
 	/* nr of replied branch */
 	int relaied_reply_branch;
-	/* transaction that is canceled (usefull only for CANCEL req) */
-	struct cell *T_canceled;
 	/* UA Server */
 	struct ua_server  uas;
 	/* UA Clients */
-	struct ua_client  uac[ NR_OF_CLIENTS ];
+	struct ua_client  uac[ MAX_BRANCHES ];
 
 	/* protection against concurrent reply processing */
 	ser_lock_t   reply_mutex;
-	/* protection against concurrent ACK processing */
-	ser_lock_t	ack_mutex;
 
-/*	tflags_t	flags; */
+	/* the route to take if no final positive reply arrived */
+	unsigned int on_negative;
+	/* set to one if you want to disallow silent transaction
+	   dropping when C timer hits
+	*/
+	int noisy_ctimer;
+	/* is it a local transaction ? */
+	int local;
 
-#ifdef WAIT
+#ifdef _XWAIT
 	/* protection against reentering WAIT state */
 	ser_lock_t	wait_mutex;
 	/* has the transaction been put on wait status ? */
 	int on_wait;
 #endif
 
-	/* this is where destination is stored for picked branch;
-	good if a need to forward ACK later on */
-	/* v6 changes; -jiri
-	struct sockaddr_in ack_to; */
-	union sockaddr_union ack_to;
-#ifndef	USE_SYNONIM
-	/* MD5checksum */
+	/* MD5checksum  (meaningful only if syn_branch=0 */
 	char md5[MD5_LEN];
-#endif
 
 #ifdef	EXTRA_DEBUG
 	/* scheduled for deletion ? */
 	short damocles;
 #endif
-
+	/* has the transaction been scheduled to die? */
+	enum kill_reason kr;
 }cell_type;
 
 
@@ -174,6 +208,7 @@ typedef struct entry
 	unsigned int    next_label;
 	/* sync mutex */
 	ser_lock_t      mutex;
+	unsigned int	entries;
 }entry_type;
 
 
@@ -193,11 +228,15 @@ struct s_table* init_hash_table();
 void   free_hash_table( struct s_table* hash_table );
 void   free_cell( struct cell* dead_cell );
 struct cell*  build_cell( struct sip_msg* p_msg );
-void   remove_from_hash_table(struct s_table *hash_table,struct cell * p_cell);
-void   insert_into_hash_table(struct s_table *hash_table,struct cell * p_cell);
+void   remove_from_hash_table_unsafe(struct s_table *hash_table,
+	struct cell * p_cell);
+void   insert_into_hash_table(struct s_table *hash_table,
+	struct cell * p_cell);
 void   insert_into_hash_table_unsafe( struct s_table *hash_table,
 		struct cell * p_cell );
 
+unsigned int transaction_count( void );
+
 #endif
 
 

+ 0 - 148
modules/tm/hash_func.c

@@ -1,148 +0,0 @@
-/*
- * $Id$
- */
-
-
-#ifndef _CRC_H_
-#define _CRC_H_
-
-extern unsigned long int crc_32_tab[];
-extern unsigned short int ccitt_tab[];
-extern unsigned short int crc_16_tab[];
-
-#endif
-
-
-#include "hash_func.h"
-#include "../../dprint.h"
-#include "../../crc.h"
-#include "../../ut.h"
-
-int old_hash( str  call_id, str cseq_nr )
-{
-   int  hash_code = 0;
-   int  i;
-	
-#if 0 /*def i386*/
-   int ci_len, cs_len;
-   char *ci, *cs;
-   
-	trim_len( ci_len, ci, call_id );
-	trim_len( cs_len, cs, cseq_nr );
-
-		int dummy1;
-		if (call_id.len>=4){
-			asm(
-				"1: \n\r"
-				"addl (%1), %0 \n\r"
-				"add $4, %1 \n\r"
-				"cmp %2, %1 \n\r"
-				"jl 1b  \n\r"
-				: "=r"(hash_code), "=r"(dummy1)
-				:  "0" (hash_code), "1"(ci),
-				"r"( (ci_len & (~3)) +ci)
-			);
-		}
-#else
-    if ( call_id.len>0 )
-      for( i=0 ; i<call_id.len ; hash_code+=call_id.s[i++]  );
-#endif
-
-#if 0 /*def i386*/
-
-		int dummy2;
-		if (cseq_nr.len>=4){
-			asm(
-				"1: \n\r"
-				"addl (%1), %0 \n\r"
-				"add $4, %1 \n\r"
-				"cmp %2, %1 \n\r"
-				"jl 1b  \n\r"
-				: "=r"(hash_code), "=r"(dummy2)
-				:  "0" (hash_code), "1"(cs),
-				"r"((cs_len & (~3) )+ cs)
-			);
-		}
-#else
-    if ( cseq_nr.len>0 )
-      for( i=0 ; i<cseq_nr.len ; hash_code+=cseq_nr.s[i++] );
-#endif
-   return hash_code &= (TABLE_ENTRIES-1); /* TABLE_ENTRIES = 2^k */
-}
-
-int new_hash( str call_id, str cseq_nr )
-{
-	int hash_code = 0;
-	int i,j, k, third;
-	int ci_len, cs_len;
-	char *ci, *cs;
-
-	/* trim EoLs */
-/*
-	ci_len = call_id.len;
-	while (ci_len && ((c=call_id.s[ci_len-1])==0 || c=='\r' || c=='\n'))
-		ci_len--;
-	cs_len = cseq_nr.len;
-	while (cs_len && ((c=cseq_nr.s[cs_len-1])==0 || c=='\r' || c=='\n'))
-		cs_len--;
-*/
-	trim_len( ci_len, ci, call_id );
-	trim_len( cs_len, cs, cseq_nr );
-
-	/* run the cycle from the end ... we are interested in the
-	   most-right digits ... and just take the %10 value of it
-	*/
-	third=(ci_len-1)/3;
-	for ( i=ci_len-1, j=2*third, k=third;
-		k>0 ; i--, j--, k-- ) {
-		hash_code+=crc_16_tab[(unsigned char)(*(ci+i)) /*+7*/ ]+
-			ccitt_tab[*(ci+k)+63]+
-			ccitt_tab[*(ci+j)+13];
-	}
-	for( i=0 ; i<cs_len ; i++ )
-		//hash_code+=crc_32_tab[(cseq_nr.s[i]+hash_code)%243];
-		hash_code+=ccitt_tab[*(cs+i)+123];
-
-	hash_code &= (TABLE_ENTRIES-1); /* TABLE_ENTRIES = 2^k */
-   	return hash_code;
-}
-
-void hashtest_cycle( int hits[TABLE_ENTRIES], char *ip )
-{
-	long int i,j,k, l;
-	int  hashv;
-	static char buf1[1024];
-	static char buf2[1024];
-	str call_id; 
-	str cseq;
-
-	call_id.s=buf1;
-	cseq.s=buf2;
-
-	for (i=987654328;i<987654328+10;i++)
-		for (j=85296341;j<85296341+10;j++)
-			for (k=987654;k<=987654+10;k++)
-				for (l=101;l<201;l++) {
-					call_id.len=sprintf( buf1, "%d-%d-%d@%s",(int)i,(int)j,
-						(int)k, ip );
-					cseq.len=sprintf( buf2, "%d", (int)l );
-					printf("%s\t%s\n", buf1, buf2 );
-					hashv=hash( call_id, cseq );
-					hits[ hashv ]++;
-				}
-}
-
-void hashtest()
-{
-	int hits[TABLE_ENTRIES];
-	int i;
-	
-	memset( hits, 0, sizeof hits );
-	hashtest_cycle( hits, "192.168.99.100" );
-	hashtest_cycle( hits, "172.168.99.100" );
-	hashtest_cycle( hits, "142.168.99.100" );
-	for (i=0; i<TABLE_ENTRIES; i++)
-		printf("[%d. %d]\n", i, hits[i] );
-	exit(0);
-}
-

+ 0 - 17
modules/tm/hash_func.h

@@ -1,17 +0,0 @@
-/*
- * $Id$
- */
-
-
-#ifndef _HASH_H
-#define _HASH_H
-
-#include "../../str.h"
-#include "h_table.h"
-
-int new_hash( str  call_id, str cseq_nr );
-int old_hash( str  call_id, str cseq_nr );
-
-#define hash( cid, cseq) new_hash( cid, cseq )
-
-#endif

+ 25 - 12
modules/tm/lock.c

@@ -49,9 +49,11 @@
 static int
 	entry_semaphore=0, 
 	timer_semaphore=0, 
-	reply_semaphore=0,
+	reply_semaphore=0;
+#ifdef _OBSOLETED
 	ack_semaphore=0;
-#ifdef WAIT
+#endif
+#ifdef _XWAIT
 static int  wait_semaphore=0;
 #endif
 /* and the maximum number of semaphores in the entry_semaphore set */
@@ -171,9 +173,11 @@ again:
 			semctl( entry_semaphore, 0 , IPC_RMID , 0 );
 		if (reply_semaphore>0)
 			semctl(reply_semaphore, 0 , IPC_RMID , 0 );
+#ifdef _OBSOLETED
 		if (ack_semaphore>0)
 			semctl(reply_semaphore, 0 , IPC_RMID , 0 );
-#ifdef WAIT
+#endif
+#ifdef _XWAIT
 		if (wait_semaphore>0)
 			semctl(wait_semaphore, 0 , IPC_RMID , 0 );
 #endif
@@ -233,7 +237,7 @@ again:
 			goto error;
 		}
 	}
-	
+#ifdef _OBSOLETED	
 	if ((ack_semaphore=init_semaphore_set(sem_nr))<0){
 		if (errno==EINVAL || errno==ENOSPC ) {
 			DBG( "DEBUG:lock_initialize: ack semaphore initialization"
@@ -247,8 +251,9 @@ again:
 			goto error;
 		}
 	}
+#endif
 
-#ifdef WAIT
+#ifdef _XWAIT
 	if ((wait_semaphore=init_semaphore_set(sem_nr))<0){
 		if (errno==EINVAL || errno==ENOSPC ) {
 			DBG( "DEBUG:lock_initialize: wait semaphore initialization"
@@ -281,7 +286,6 @@ error:
 void lock_cleanup()
 {
 	/* must check if someone uses them, for now just leave them allocated*/
-	LOG(L_INFO, "INFO: lock_cleanup:  clean-up still not implemented properly \n");
 }
 
 #else
@@ -294,7 +298,6 @@ void lock_cleanup()
 	   no other process lives 
 	*/
 
-	LOG(L_INFO, "INFO: lock_cleanup:  clean-up still not implemented properly (no sibling check)\n");
 	/* sibling double-check missing here; install a signal handler */
 
 	if (entry_semaphore > 0 && 
@@ -306,18 +309,24 @@ void lock_cleanup()
 	if (reply_semaphore > 0 &&
 	    semctl( reply_semaphore, 0 , IPC_RMID , 0 )==-1)
 		LOG(L_ERR, "ERROR: lock_cleanup, reply_semaphore cleanup failed\n");
+#ifdef _OBSOLETED
 	if (ack_semaphore > 0 &&
 	    semctl( ack_semaphore, 0 , IPC_RMID , 0 )==-1)
 		LOG(L_ERR, "ERROR: lock_cleanup, ack_semaphore cleanup failed\n");
-#ifdef WAIT
+#endif
+#ifdef _XWAIT
 	if (wait_semaphore > 0 &&
 		semctl( wait_semaphore, 0 , IPC_RMID , 0 )==-1)
 		LOG(L_ERR, "ERROR: lock_cleanup, wait_semaphore cleanup failed\n");
 #endif
 
 
-	entry_semaphore = timer_semaphore = reply_semaphore = ack_semaphore = 0;
-#ifdef WAIT
+	entry_semaphore = timer_semaphore = reply_semaphore 
+#ifdef _OBSOLETED
+		= ack_semaphore 
+#endif
+		= 0;
+#ifdef _XWAIT
 	wait_semaphore = 0;
 #endif
 
@@ -333,17 +342,21 @@ int init_cell_lock( struct cell *cell )
 {
 #ifdef FAST_LOCK
 	init_lock(cell->reply_mutex);
+#ifdef _OBSOLETED
 	init_lock(cell->ack_mutex);
-#ifdef WAIT
+#endif
+#ifdef _XWAIT
 	init_lock(cell->wait_mutex);
 #endif
 	return 0;
 #else
 	cell->reply_mutex.semaphore_set=reply_semaphore;
 	cell->reply_mutex.semaphore_index = cell->hash_index % sem_nr;
+#ifdef _OBSOLETED
 	cell->ack_mutex.semaphore_set=ack_semaphore;
 	cell->ack_mutex.semaphore_index = cell->hash_index % sem_nr;
-#ifdef WAIT
+#endif
+#ifdef _XWAIT
 	cell->wait_mutex.semaphore_set=wait_semaphore;
 	cell->wait_mutex.semaphore_index = cell->hash_index % sem_nr;
 #endif /* WAIT */

+ 1 - 1
modules/tm/lock.h

@@ -42,7 +42,7 @@ enum timer_groups {
 
 
 #include "h_table.h"
-#include "timer.h"
+#include "timer.h" 
 
 /* Uni*x permissions for IPC */
 #define IPC_PERMISSIONS 0666

+ 0 - 30
modules/tm/sh_malloc.h

@@ -1,30 +0,0 @@
-/*
- * $Id$
- */
-
-
-#ifndef _SH_MALLOC_H
-#define _SH_MALLOC_H
-
-#include "../../mem/shm_mem.h"
-
-#if defined SHM_MEM
-
-#include "../../mem/shm_mem.h"
-
-#define sh_malloc(size)		shm_malloc((size))
-#define sh_free(ptr)		shm_free((ptr))
-#define sh_status()			shm_status()
-
-#else
-
-#include <stdlib.h>
-
-#warn "you should define SHM_MEM"
-#define sh_malloc(size)		malloc((size))
-#define sh_free(ptr)		free((ptr))
-#define sh_status()
-
-#endif
-
-#endif

+ 59 - 1
modules/tm/sip_msg.c

@@ -1,5 +1,16 @@
 /*
  * $Id$
+ * 
+ * cloning a message into shared memory (TM keeps a snapshot
+ * of messages in memory); note that many operations, which
+ * allocate pkg memory (such as parsing) cannot be used with
+ * a cloned message -- it would result in linking pkg structures
+ * to shmem msg and eventually in a memory error 
+ *
+ * the cloned message is stored in a single memory fragment to
+ * save too many shm_mallocs -- these are expensive as they
+ * not only take lookup in fragment table but also a shmem lock
+ * operation (the same for shm_free)
  */
 
 #include <stdio.h>
@@ -154,6 +165,12 @@ struct sip_msg*  sip_msg_cloner( struct sip_msg *org_msg )
 						len+=ROUND4(sizeof(struct via_param ));
 				}
 				break;
+			default:
+				if (hdr->parsed) {
+					LOG(L_WARN, "WARNING: sip_msg_cloner: "
+						"header body ignored: %d\n", hdr->type );
+				}
+				break;
 		}/*switch*/
 	}/*for all headers*/
 
@@ -184,7 +201,7 @@ struct sip_msg*  sip_msg_cloner( struct sip_msg *org_msg )
 	for(rpl_lump=org_msg->reply_lump;rpl_lump;rpl_lump=rpl_lump->next)
 		len+=rpl_lump->text.len;
 
-	p=(char *)sh_malloc(len);foo=p;
+	p=(char *)shm_malloc(len);foo=p;
 	if (!p)
 	{
 		LOG(L_ERR , "ERROR: sip_msg_cloner: cannot allocate memory\n" );
@@ -252,6 +269,11 @@ struct sip_msg*  sip_msg_cloner( struct sip_msg *org_msg )
 			hdr->name.s);
 		new_hdr->body.s = translate_pointer(new_msg->buf, org_msg->buf,
 			hdr->body.s);
+		/* by default, we assume we don't understand this header in TM
+		   and better set it to zero; if we do, we will set a specific
+		   valu in the following switch statement
+		*/
+		new_hdr->parsed=0;
 
 		switch (hdr->type)
 		{
@@ -360,6 +382,42 @@ struct sip_msg*  sip_msg_cloner( struct sip_msg *org_msg )
 			case HDR_ROUTE :
 				new_msg->route = new_hdr;
 				break;
+			case HDR_RECORDROUTE :
+				new_msg->record_route = new_hdr;
+				break;
+			case HDR_CONTENTTYPE :
+				new_msg->content_type = new_hdr;
+				break;
+			case HDR_CONTENTLENGTH :
+				new_msg->content_length = new_hdr;
+				break;
+			case HDR_AUTHORIZATION :
+				new_msg->authorization = new_hdr;
+				break;
+			case HDR_EXPIRES :
+				new_msg->expires = new_hdr;
+				break;
+			case HDR_PROXYAUTH :
+				new_msg->proxy_auth = new_hdr;
+				break;
+			case HDR_WWWAUTH :
+				new_msg->www_auth = new_hdr;
+				break;
+			case HDR_SUPPORTED :
+				new_msg->supported = new_hdr;
+				break;
+			case HDR_REQUIRE :
+				new_msg->require = new_hdr;
+				break;
+			case HDR_PROXYREQUIRE :
+				new_msg->proxy_require = new_hdr;
+				break;
+			case HDR_UNSUPPORTED :
+				new_msg->unsupported = new_hdr;
+				break;
+			case HDR_ALLOW :
+				new_msg->unsupported = new_hdr;	
+				break;
 		}/*switch*/
 
 		if ( last_hdr )

+ 1 - 2
modules/tm/sip_msg.h

@@ -7,8 +7,7 @@
 #define _SIP_MSG_H
 
 #include "../../parser/msg_parser.h"
-
-#include "sh_malloc.h"
+#include "../../mem/shm_mem.h"
 
 #define  sip_msg_free(_p_msg) shm_free( (_p_msg ))
 #define  sip_msg_free_unsafe(_p_msg) shm_free_unsafe( (_p_msg) )

+ 97 - 0
modules/tm/t_cancel.c

@@ -0,0 +1,97 @@
+/*
+ * $Id$
+ *
+ */
+
+
+#include "t_funcs.h"
+#include "../../dprint.h"
+#include "../../ut.h"
+#include "t_reply.h"
+#include "t_cancel.h"
+#include "t_msgbuilder.h"
+
+
+/* determine which branches should be cancelled; do it
+   only from within REPLY_LOCK, otherwise collisions
+   could occur (e.g., two 200 for two branches processed
+   by two processes might concurrently try to generate
+   a CANCEL for the third branch, resulting in race conditions
+   during writing to cancel buffer
+*/
+
+
+void which_cancel( struct cell *t, branch_bm_t *cancel_bm )
+{
+	int i;
+
+	for( i=0 ; i<t->nr_of_outgoings ; i++ ) {
+		if (should_cancel_branch(t, i)) 
+			*cancel_bm |= 1<<i ;
+
+	}
+}
+
+
+/* cancel branches scheduled for deletion */
+void cancel_uacs( struct cell *t, branch_bm_t cancel_bm )
+{
+	int i;
+
+	/* cancel pending client transactions, if any */
+	for( i=0 ; i<t->nr_of_outgoings ; i++ ) 
+		if (cancel_bm & (1<<i))
+           	cancel_branch(t, i);
+}
+
+void cancel_branch( struct cell *t, int branch )
+{
+	char *cancel;
+	int len;
+	struct retr_buf *crb, *irb;
+
+	crb=&t->uac[branch].local_cancel;
+	irb=&t->uac[branch].request;
+
+#	ifdef EXTRA_DEBUG
+	if (crb->buffer!=0 && crb->buffer!=BUSY_BUFFER) {
+		LOG(L_CRIT, "ERROR: attempt to rewrite cancel buffer\n");
+		abort();
+	}
+#	endif
+
+	cancel=build_cancel(t, branch, &len);
+	if (!cancel) {
+		LOG(L_ERR, "ERROR: attempt to build a CANCEL failed\n");
+		return;
+	}
+	/* install cancel now */
+	crb->buffer=cancel;
+	crb->buffer_len=len;
+	crb->to=irb->to;
+	crb->send_sock=irb->send_sock;
+	crb->branch=branch;
+#ifdef _OBSOLETED
+	crb->fr_timer.tg=TG_FR;
+	crb->retr_timer.tg=TG_RT;
+	crb->my_T=t;
+#endif
+	crb->retr_timer.payload=crb->fr_timer.payload=crb;
+	/* label it as cancel so that FR timer can better now how to
+	   deal with it */
+	crb->activ_type=TYPE_LOCAL_CANCEL;
+
+    DBG("DEBUG: cancel_branch: sending cancel...\n");
+	SEND_BUFFER( crb );
+
+    /*sets and starts the FINAL RESPONSE timer */
+	start_retr( crb );
+}
+
+char *build_cancel(struct cell *Trans,unsigned int branch,
+	unsigned int *len )
+{
+	return build_local( Trans, branch, len,
+		CANCEL, CANCEL_LEN, &Trans->to );
+}
+

+ 45 - 0
modules/tm/t_cancel.h

@@ -0,0 +1,45 @@
+/*
+ * $Id$
+ *
+ */
+
+#ifndef _CANCEL_H
+#define _CANCEL_H
+
+/* a buffer is empty but cannot be used by anyone else;
+   particularly, we use this value in the buffer pointer
+   in local_buffer to tell "a process is already scheduled
+   to generate a CANCEL, other processes are not supposed to"
+   (which might happen if for example in a three-branch forking,
+   two 200 would enter separate processes and compete for
+   cancelling the third branch); note that to really avoid
+   race conditions, the value must be set in REPLY_LOCK
+*/
+
+#define BUSY_BUFFER ((char *)-1)
+
+void which_cancel( struct cell *t, branch_bm_t *cancel_bm );
+void cancel_uacs( struct cell *t, branch_bm_t cancel_bm );
+void cancel_branch( struct cell *t, int branch );
+
+char *build_cancel(struct cell *Trans,unsigned int branch,
+	unsigned int *len );
+
+inline short static should_cancel_branch( struct cell *t, int b )
+{
+	int last_received;
+	short should;
+
+	last_received=t->uac[b].last_received;
+	/* cancel only if provisional received and noone else
+	   attempted to cancel yet */
+	should=last_received>=100 && last_received<200
+		&& t->uac[b].local_cancel.buffer==0;
+	/* we'll cancel -- label it so that noone else
+		(e.g. another 200 branch) will try to do the same */
+	if (should) t->uac[b].local_cancel.buffer=BUSY_BUFFER;
+	return should;
+}
+
+
+#endif

+ 20 - 0
modules/tm/t_dlg.c

@@ -0,0 +1,20 @@
+/*
+ * $Id$
+ *
+ */
+
+#include "t_dlg.h"
+
+static struct dialog *dlg=0;
+
+int t_newdlg( struct sip_msg *msg )
+{
+	/* place-holder */
+	dlg=0;
+	return 0;
+}
+
+struct dialog *t_getdlg() {
+	return dlg;
+}
+

+ 18 - 0
modules/tm/t_dlg.h

@@ -0,0 +1,18 @@
+/*
+ * $Id$
+ *
+ */
+
+#ifndef _T_DLG_H
+#define _T_DLG_H
+
+#include "../../parser/msg_parser.h"
+
+struct dialog {
+	int place_holder;
+};
+
+int t_newdlg( struct sip_msg *msg );
+struct dialog *t_getdlg() ;
+
+#endif

+ 0 - 95
modules/tm/t_fork.c

@@ -1,95 +0,0 @@
-/*
- * $Id$
- *
- * forking requests
- */
-
-#include "../../dprint.h"
-#include "../../config.h"
-#include "../../parser/parser_f.h"
-#include "../../ut.h"
-#include "hash_func.h"
-#include "t_funcs.h"
-#include "t_fork.h"
-
-
-
-unsigned int     nr_forks;
-struct fork      t_forks[ NR_OF_CLIENTS ];
-
-
-int t_add_fork( union sockaddr_union to, char* uri_s,
-			unsigned int uri_len, enum fork_type type, 
-			unsigned char free_flag)
-{
-	unsigned int pos=0;
-	char         *foo=0;
-
-	switch (type)
-	{
-		case DEFAULT:
-			if (nr_forks+1>=MAX_FORK)
-			{
-				LOG(L_ERR,"ERROR:t_add_fork: trying to add new fork ->"
-					" MAX_FORK exceded\n");
-				return -1;
-			}
-			pos = ++nr_forks;
-			break;
-		case NO_RESPONSE:
-			/* v6; -Jiri
-			if (t_forks[NO_RPL_BRANCH].ip)
-			*/
-			if (!t_forks[NO_RPL_BRANCH].inactive)
-				LOG(L_WARN,"WARNING:t_add_fork: trying to add NO_RPL fork ->"
-					" it was set before -> overriding\n");
-			if (uri_s && uri_len)
-			{
-				foo = (char*)shm_malloc(uri_len);
-				if (!foo)
-				{
-					LOG(L_ERR,"ERROR:t_add_fork: cannot get free memory\n");
-					return -1;
-				}
-				memcpy(foo,uri_s,uri_len);
-			}
-			if (free_flag && uri_s)
-				pkg_free(uri_s);
-			uri_s = foo;
-			free_flag = 0;
-			pos = NO_RPL_BRANCH;
-	}
-	/* -v6
-	t_forks[pos].ip = ip;
-	t_forks[pos].port = port;
-	*/
-	t_forks[pos].to=to;
-
-	if (uri_s && uri_len)
-	{
-		t_forks[pos].free_flag = free_flag;
-		t_forks[pos].uri.len = uri_len;
-		t_forks[pos].uri.s = uri_s;
-	}
-
-	return 1;
-}
-
-
-
-
-int t_clear_forks( )
-{
-	int i;
-
-	DBG("DEBUG: t_clear_forks: clearing tabel...\n");
-	for(i=1;i<nr_forks;i++)
-		if (t_forks[i].free_flag && t_forks[i].uri.s)
-			pkg_free(t_forks[i].uri.s);
-	memset( t_forks, 0, sizeof(t_forks));
-	nr_forks = 0;
-	return 1;
-}
-
-
-

+ 0 - 30
modules/tm/t_fork.h

@@ -1,30 +0,0 @@
-/*
- * $Id$
- */
-
-#ifndef _T_FORKS_H
-#define _T_FORKS_H
-
-#include "../../ip_addr.h"
-#include "../../str.h"
-
-
-struct fork
-{
-    union sockaddr_union to;
-    char inactive;
-    unsigned char free_flag;
-    str           uri;
-
-};
-
-extern struct fork      t_forks[ NR_OF_CLIENTS ];
-extern unsigned int     nr_forks;
-
-int t_add_fork( union sockaddr_union to, char* uri_s,
-				unsigned int uri_len, enum fork_type type,
-				unsigned char free_flag);
-int t_clear_forks();
-
-
-#endif

+ 166 - 171
modules/tm/t_funcs.c

@@ -4,19 +4,43 @@
  * transaction maintenance functions
  */
 
+#include <limits.h>
 #include "../../dprint.h"
 #include "../../config.h"
 #include "../../parser/parser_f.h"
 #include "../../ut.h"
-#include "hash_func.h"
+#include "../../hash_func.h"
+#include "../../dset.h"
 #include "t_funcs.h"
-#include "t_fork.h"
+#include "t_fwd.h"
+#include "t_lookup.h"
 
-
-struct cell      *T;
-unsigned int     global_msg_id;
+/* pointer to the big table where all the transaction data
+   lives
+*/
 struct s_table*  hash_table;
 
+/* ----------------------------------------------------- */
+
+int send_pr_buffer( struct retr_buf *rb,
+	void *buf, int len, char *function, int line )
+{
+	if (buf && len && rb )
+		return udp_send( rb->send_sock, buf,
+			len, &rb->to,  sizeof(union sockaddr_union) ) ;
+	else {
+		LOG(L_CRIT, "ERROR: sending an empty buffer from %s (%d)\n",
+			function, line );
+		return -1;
+	}
+}
+
+void start_retr( struct retr_buf *rb )
+{
+	rb->retr_list=RT_T1_TO_1;
+	set_timer( hash_table, &rb->retr_timer, RT_T1_TO_1 );
+	set_timer( hash_table, &rb->fr_timer, FR_TIMER_LIST );
+}
 
 int tm_startup()
 {
@@ -35,15 +59,12 @@ int tm_startup()
 	hash_table->timers[WT_TIMER_LIST].id     = WT_TIMER_LIST;
 	hash_table->timers[DELETE_LIST].id       = DELETE_LIST;
 
-	/* register the timer function */
-	register_timer( timer_routine , hash_table , 1 );
 
 	/* fork table */
-	nr_forks = 0;
+	/* nr_forks = 0; */	
 
-	/*first msg id*/
-	global_msg_id = 0;
-	T = T_UNDEFINED;
+	/* init static hidden values */
+	init_t();
 
 	return 0;
 }
@@ -82,208 +103,182 @@ void tm_shutdown()
 }
 
 
-
-
-/* function returns:
- *       1 - a new transaction was created
- *      -1 - error, including retransmission
- */
-int t_add_transaction( struct sip_msg* p_msg )
-{
-	struct cell*    new_cell;
-
-	DBG("DEBUG: t_add_transaction: adding......\n");
-	/* sanity check: ACKs can never establish a transaction */
-	if ( p_msg->REQ_METHOD==METHOD_ACK )
-	{
-		LOG(L_ERR, "ERROR: add_transaction: ACK can't be used to add"
-			" transaction\n");
-		return -1;
-	}
-
-	/* creates a new transaction */
-	new_cell = build_cell( p_msg ) ;
-	DBG("DEBUG: t_add_transaction: new transaction created %p\n", new_cell);
-	if  ( !new_cell ){
-		LOG(L_ERR, "ERROR: add_transaction: out of mem:\n");
-		sh_status();
-		return -1;
-	}
-	/*insert the transaction into hash table*/
-	insert_into_hash_table( hash_table , new_cell );
-	DBG("DEBUG: t_add_transaction: new transaction inserted, hash: %d\n",
-		new_cell->hash_index );
-
-	T = new_cell;
-	T_REF(T);
-	return 1;
-}
-
-
-
-
 /*   returns 1 if everything was OK or -1 for error
 */
-int t_release_transaction( struct sip_msg* p_msg)
+int t_release_transaction( struct cell *trans )
 {
-      return t_put_on_wait( T );
-}
-
+	trans->kr|=REQ_RLSD;
 
+	reset_timer( hash_table, & trans->uas.response.fr_timer );
+	reset_timer( hash_table, & trans->uas.response.retr_timer );
 
-int t_unref( /* struct sip_msg* p_msg */ )
-{
-	if (T==T_UNDEFINED || T==T_NULL)
-		return -1;
-	T_UNREF( T );
-	T=T_UNDEFINED;
+	cleanup_uac_timers( trans );
+	
+	put_on_wait( trans );
 	return 1;
 }
 
 
-
-
-
 /* ----------------------------HELPER FUNCTIONS-------------------------------- */
 
 
-int t_update_timers_after_sending_reply( struct retr_buf *rb )
-{
-	struct cell *Trans = rb->my_T;
-
-	/* make sure that if we send something final upstream, everything else
-	   will be cancelled */
-	if (Trans->uas.status>=300&&Trans->uas.request->REQ_METHOD==METHOD_INVITE)
-	{
-		rb->retr_list = RT_T1_TO_1;
-		set_timer( hash_table, &(rb->retr_timer), RT_T1_TO_1 );
-		set_timer( hash_table, &(rb->fr_timer), FR_TIMER_LIST );
-	} else if ( Trans->uas.request->REQ_METHOD==METHOD_CANCEL ) {
-		if ( Trans->T_canceled==T_UNDEFINED )
-			Trans->T_canceled = t_lookupOriginalT( hash_table ,
-				Trans->uas.request );
-		if ( Trans->T_canceled==T_NULL )
-			return 1;
-		/* put CANCEL transaction on wait only if canceled transaction already
-		    is in final status and there is nothing to cancel; */
-		if ( Trans->T_canceled->uas.status>=200)
-			t_put_on_wait( Trans );
-	} else if (Trans->uas.status>=200)
-		t_put_on_wait( Trans );
-   return 1;
-}
-
-
-
-
 /*
   */
-int t_put_on_wait(  struct cell  *Trans  )
+void put_on_wait(  struct cell  *Trans  )
 {
-	unsigned int i;
-	//struct retrans_buff* rb;
-
-#ifndef WAIT
-	if (is_in_timer_list2( &(Trans->wait_tl)))
-  	{
-		DBG("DEBUG: t_put_on_wait: already on wait\n");
-		return 1;
-	}
-#else
-	/* have some race conditons occured and we already
-	  entered/passed the wait status previously?
-	  if so, exit now
-	*/
 
+#ifdef _XWAIT
 	LOCK_WAIT(Trans);
 	if (Trans->on_wait)
 	{
 		DBG("DEBUG: t_put_on_wait: already on wait\n");
 		UNLOCK_WAIT(Trans);
-		return 1;
 	} else {
 		Trans->on_wait=1;
 		UNLOCK_WAIT(Trans);
 	}
 #endif
-
-	/* remove from  retranssmision  and  final response   list */
-	DBG("DEBUG: t_put_on_wait: stopping timers (FR and RETR)\n");
-	reset_retr_timers(hash_table,Trans) ;
-
-#ifdef SILENT_FR
-	if (Trans->nr_of_outgoings>1)
+#ifdef EXTRA_DEBUG
+	DBG("DEBUG: --- out on WAIT --- \n");
 #endif
-	{
-	/* cancel pending client transactions, if any */
-	for( i=0 ; i<Trans->nr_of_outgoings ; i++ )
-		if ( Trans->uac[i].rpl_received && Trans->uac[i].status<200 )
-			t_build_and_send_CANCEL(Trans , i);
-	}
 
-	/* adds to Wait list*/
-	set_timer( hash_table, &(Trans->wait_tl), WT_TIMER_LIST );
-	return 1;
+
+	/* we put the transaction on wait timer; we do it only once
+	   in transaction's timelife because putting it multiple-times
+	   might result in a second instance of a wait timer to be
+	   set after the first one fired; on expiration of the second
+	   instance, the transaction would be re-deleted
+
+			PROCESS1		PROCESS2		TIMER PROCESS
+		0. 200/INVITE rx;
+		   put_on_wait
+		1.					200/INVITE rx;
+		2.									WAIT fires; transaction
+											about to be deleted
+		3.					avoid putting
+							on WAIT again
+		4.									WAIT timer executed,
+											transaction deleted
+	*/
+	set_1timer( hash_table, &(Trans->wait_tl), WT_TIMER_LIST );
 }
 
 
 
+static int kill_transaction( struct cell *trans )
+{
+	char err_buffer[128];
+	int sip_err;
+	int reply_ret;
+	int ret;
+
+	/*  we reply statefuly and enter WAIT state since error might
+		have occured in middle of forking and we do not
+		want to put the forking burden on upstream client;
+		howver, it may fail too due to lack of memory */
+
+	ret=err2reason_phrase( ser_error, &sip_err,
+		err_buffer, sizeof(err_buffer), "TM" );
+	if (ret>0) {
+		reply_ret=t_reply( trans, trans->uas.request, 
+			sip_err, err_buffer);
+		/* t_release_transaction( T ); */
+		return reply_ret;
+	} else {
+		LOG(L_ERR, "ERROR: kill_transaction: err2reason failed\n");
+		return -1;
+	}
+}
+
 
 
-void delete_cell( struct cell *p_cell )
+int t_relay_to( struct sip_msg  *p_msg , struct proxy_l *proxy,
+	int replicate)
 {
-#ifdef EXTRA_DEBUG
-	int i;
+	int ret;
+	int new_tran;
+	str *uri;
+	int reply_ret;
+	/* struct hdr_field *hdr; */
+	struct cell *t;
 
-	if (is_in_timer_list2(& p_cell->wait_tl )) {
-		LOG( L_ERR, "ERROR: transaction %p scheduled for deletion and"
-			" still on WAIT\n", p_cell);
-		abort();
-	}
-	/*
-	if (is_in_timer_list2(& p_cell->outbound_response.retr_timer )) {
-		LOG( L_ERR, "ERROR: transaction %p scheduled for deletion and"
-			" still on RETR (rep)\n",
-			p_cell);
-		abort();
+	ret=0;
+
+	new_tran = t_newtran( p_msg );
+	
+
+	/* parsing error, memory alloc, whatever ... if via is bad
+	   and we are forced to reply there, return with 0 (->break),
+	   pass error status otherwise
+	*/
+	if (new_tran<0) {
+		ret = (ser_error==E_BAD_VIA && reply_to_via) ? 0 : new_tran;
+		goto done;
 	}
-	if (is_in_timer_list2(& p_cell->outbound_response.fr_timer )) {
-		LOG( L_ERR, "ERROR: transaction %p scheduled for deletion and"
-			" still on FR (rep)\n", p_cell);
-		abort();
+	/* if that was a retransmission, return we are happily done */
+	if (new_tran==0) {
+		ret = 1;
+		goto done;
 	}
-	for (i=0; i<p_cell->nr_of_outgoings; i++) {
-		if (is_in_timer_list2(& p_cell->outbound_request[i]->retr_timer)) {
-			LOG( L_ERR, "ERROR: transaction %p scheduled for deletion and"
-				" still on RETR (req %d)\n", p_cell, i);
-			abort();
-		}
-		if (is_in_timer_list2(& p_cell->outbound_request[i]->fr_timer)) {
-			LOG( L_ERR, "ERROR: transaction %p scheduled for deletion and"
-				" still on FR (req %d)\n", p_cell, i);
-			abort();
+
+	/* new transaction */
+
+	/* ACKs do not establish a transaction and are fwd-ed statelessly */
+	if ( p_msg->REQ_METHOD==METHOD_ACK) {
+		DBG( "SER: forwarding ACK  statelessly \n");
+		if (proxy==0) {
+			uri=(p_msg->new_uri.s==0 || p_msg->new_uri.len==0) ?
+				&p_msg->first_line.u.request.uri :
+				&p_msg->new_uri;
+			proxy=uri2proxy( uri );
+			if (proxy==0) {
+					ret=E_BAD_ADDRESS;
+					goto done;
+			}
+			ret=forward_request( p_msg , proxy ) ;
+			free_proxy( proxy );	
+			free( proxy );
+		} else {
+			ret=forward_request( p_msg , proxy ) ;
 		}
+		goto done;
 	}
+
+	/* if replication flag is set, mark the transaction as local
+	   so that replies will not be relaied
 	*/
-	reset_retr_timers( hash_table, p_cell );
-#endif
-	/* still in use ... don't delete */
-	if ( T_IS_REFED(p_cell) ) {
-#ifdef	EXTRA_DEBUG
-		if (T_REFCOUNTER(p_cell)>1) {
-			DBG("DEBUG: while debugging with a single process, ref_count>1\n");
-			DBG("DEBUG: transaction =%p\n", p_cell );
-			abort();
+	t=get_t();
+	t->local=replicate;
+
+	/* INVITE processing might take long, partcularly because of DNS
+	   look-ups -- let upstream know we're working on it */
+	if (p_msg->REQ_METHOD==METHOD_INVITE )
+	{
+		DBG( "SER: new INVITE\n");
+		if (!t_reply( t, p_msg , 100 ,
+			"trying -- your call is important to us"))
+				DBG("SER: ERROR: t_reply (100)\n");
+	} 
+
+	/* now go ahead and forward ... */
+	ret=t_forward_nonack(t, p_msg, proxy);
+	if (ret<=0) {
+		DBG( "SER:ERROR: t_forward \n");
+		reply_ret=kill_transaction( t );
+		if (reply_ret>0) {
+			/* we have taken care of all -- do nothing in
+		  	script */
+			DBG("ERROR: generation of a stateful reply "
+				"on error succeeded\n");
+			ret=0;
+		}  else {
+			DBG("ERROR: generation of a stateful reply "
+				"on error failed\n");
 		}
-#endif
-		DBG("DEBUG: delete_cell: t=%p post for delete (refbitmap %x,"
-			" refcount %d)\n",p_cell,p_cell->ref_bitmap,T_REFCOUNTER(p_cell));
-		/* it's added to del list for future del */
-		set_timer( hash_table, &(p_cell->dele_tl), DELETE_LIST );
 	} else {
-		DBG("DEBUG: delete transaction %p\n", p_cell );
-		free_cell( p_cell );
+		DBG( "SER: new transaction fwd'ed\n");
 	}
-}
-
 
+done:
+	return ret;
+}

+ 40 - 292
modules/tm/t_funcs.h

@@ -10,6 +10,7 @@
 #include <netinet/in.h>
 #include <netdb.h>
 
+#include "../../mem/shm_mem.h"
 #include "../../parser/msg_parser.h"
 #include "../../globals.h"
 #include "../../udp_server.h"
@@ -23,8 +24,9 @@
 #include "config.h"
 #include "lock.h"
 #include "timer.h"
-#include "sh_malloc.h"
 #include "sip_msg.h"
+#include "h_table.h"
+#include "ut.h"
 
 
 struct s_table;
@@ -32,115 +34,56 @@ struct timer;
 struct entry;
 struct cell;
 
-extern struct cell      *T;
-extern unsigned int     global_msg_id;
 extern struct s_table*  hash_table;
+extern int noisy_ctimer;
 
 
+#define LOCK_HASH(_h) lock(&(hash_table->entrys[(_h)].mutex))
+#define UNLOCK_HASH(_h) unlock(&(hash_table->entrys[(_h)].mutex))
 
-#define LOCK_REPLIES(_t) lock(&(_t)->reply_mutex )
-#define UNLOCK_REPLIES(_t) unlock(&(_t)->reply_mutex )
+#ifdef _OBSOLETED
 #define LOCK_ACK(_t) lock(&(_t)->ack_mutex )
 #define UNLOCK_ACK(_t) unlock(&(_t)->ack_mutex )
-#define LOCK_WAIT(_t) lock(&(_t)->wait_mutex )
-#define UNLOCK_WAIT(_t) unlock(&(_t)->wait_mutex )
+#endif
 
+#ifdef _XWAIT
+	#define LOCK_WAIT(_t) lock(&(_t)->wait_mutex )
+	#define UNLOCK_WAIT(_t) unlock(&(_t)->wait_mutex )
+#else
+	#define LOCK_WAIT(_t)
+	#define UNLOCK_WAIT(_t)
+#endif
 
 /* send a private buffer: utilize a retransmission structure
    but take a separate buffer not refered by it; healthy
    for reducing time spend in REPLIES locks
 */
 
-inline static int send_pr_buffer( struct retr_buf *rb,
-	void *buf, int len, char *function, int line )
-{
-	if (buf && len && rb )
-		return udp_send( rb->send_sock, buf, 
-			len, &rb->to,  sizeof(union sockaddr_union) ) ;
-	else {
-		LOG(L_CRIT, "ERROR: sending an empty buffer from %s (%d)\n",
-			function, line );
-		return -1;
-	}
-}
+int send_pr_buffer( struct retr_buf *rb,
+	void *buf, int len, char *function, int line );
 
+/* send a buffer -- 'PR' means private, i.e., it is assumed noone
+   else can affect the buffer during sending time
+*/
 #define SEND_PR_BUFFER(_rb,_bf,_le ) \
 	send_pr_buffer( (_rb), (_bf), (_le),  __FUNCTION__, __LINE__ )
 
-/*
-#define SEND_PR_BUFFER(_rb,_bf,_le ) \
-	( ((_bf) && (_le) && (_bf)) ? \
-	udp_send( (_bf), (_le), &((_rb)->to), sizeof(union sockaddr_union) ) : \
-	log_send_error( __FUNCTION__, __LINE__ ) )
-*/
-
-/* just for understanding of authors of the following macros, who did not
-   include 'PR' in macro names though they use 'PR' macro: PR stands for
-   PRIVATE and indicates usage of memory buffers in PRIVATE memory space,
-   where -- as opposed to SHARED memory space -- no concurrent memory
-   access can occur and thus no locking is needed ! -jiri
-*/
-#define SEND_ACK_BUFFER( _rb ) \
-	SEND_PR_BUFFER( (_rb) , (_rb)->ack , (_rb)->ack_len )
-
-#define SEND_CANCEL_BUFFER( _rb ) \
-	SEND_PR_BUFFER( (_rb) , (_rb)->cancel , (_rb)->cancel_len )
-
 #define SEND_BUFFER( _rb ) \
 	SEND_PR_BUFFER( (_rb) , (_rb)->buffer , (_rb)->buffer_len )
 
 
-/*
-  macros for reference bitmap (lock-less process non-exclusive ownership) 
-*/
-#define T_IS_REFED(_T_cell) ((_T_cell)->ref_bitmap)
-#define T_REFCOUNTER(_T_cell) \
-	( { int _i=0; \
-		process_bm_t _b=(_T_cell)->ref_bitmap; \
-		while (_b) { \
-			if ( (_b) & 1 ) _i++; \
-			(_b) >>= 1; \
-		} ;\
-		(_i); \
-	 } )
-		
-
-#ifdef EXTRA_DEBUG
-#define T_IS_REFED_BYSELF(_T_cell) ((_T_cell)->ref_bitmap & process_bit)
-#	define DBG_REF(_action, _t) DBG("DEBUG: XXXXX %s (%s:%d): T=%p , ref (bm=%x, cnt=%d)\n",\
-			(_action), __FUNCTION__, __LINE__, (_t),(_t)->ref_bitmap, T_REFCOUNTER(_t));
-#	define T_UNREF(_T_cell) \
-	( { \
-		DBG_REF("unref", (_T_cell)); \
-		if (!T_IS_REFED_BYSELF(_T_cell)) { \
-			DBG("ERROR: unrefering unrefered transaction %p from %s , %s : %d\n", \
-				(_T_cell), __FUNCTION__, __FILE__, __LINE__ ); \
-			abort(); \
-		} \
-		(_T_cell)->ref_bitmap &= ~process_bit; \
-	} )
-
-#	define T_REF(_T_cell) \
-	( { \
-		DBG_REF("ref", (_T_cell));	 \
-		if (T_IS_REFED_BYSELF(_T_cell)) { \
-			DBG("ERROR: refering already refered transaction %p from %s,%s :"\
-				" %d\n",(_T_cell), __FUNCTION__, __FILE__, __LINE__ ); \
-			abort(); \
-		} \
-		(_T_cell)->ref_bitmap |= process_bit; \
-	} )
-#else
-#	define T_UNREF(_T_cell) ({ (_T_cell)->ref_bitmap &= ~process_bit; })
-#	define T_REF(_T_cell) ({ (_T_cell)->ref_bitmap |= process_bit; })
-#endif
-
-
-
-/*
-enum addifnew_status { AIN_ERROR, AIN_RETR, AIN_NEW, AIN_NEWACK,
-	AIN_OLDACK, AIN_RTRACK } ;
-*/
+#define UNREF_UNSAFE(_T_cell) ({  (_T_cell)->ref_count--; })
+#define UNREF(_T_cell) ({ \
+	LOCK_HASH( (_T_cell)->hash_index ); \
+	UNREF_UNSAFE(_T_cell); \
+	UNLOCK_HASH( (_T_cell)->hash_index ); })
+#define REF_UNSAFE(_T_cell) ({  (_T_cell)->ref_count++; })
+#define REF(_T_cell) ({ \
+	LOCK_HASH( (_T_cell)->hash_index ); \
+	REF_UNSAFE(_T_cell); \
+	UNLOCK_HASH( (_T_cell)->hash_index ); })
+#define INIT_REF_UNSAFE(_T_cell) (_T_cell)->ref_count=1
+#define IS_REFFED_UNSAFE(_T_cell) ((_T_cell)->ref_count!=0)
 
 
 int   tm_startup();
@@ -154,225 +97,30 @@ void tm_shutdown();
 int  t_add_transaction( struct sip_msg* p_msg  );
 
 
-
-
-/* function returns:
- *      -1 - transaction wasn't found
- *       1 - transaction found
- */
-int t_check( struct sip_msg* , int *branch , int* is_cancel);
-
-
-
-
-/* Forwards the inbound request to a given IP and port.  Returns:
- *       1 - forward successfull
- *      -1 - error during forward
- */
-/* v6; -jiri
-int t_forward( struct sip_msg* p_msg , unsigned int dst_ip ,
-										unsigned int dst_port);
-*/
-
-
-
-
-/* Forwards the inbound request to dest. from via.  Returns:
- *       1 - forward successfull
- *      -1 - error during forward
- */
-/* v6; -jiri
-int t_forward_uri( struct sip_msg* p_msg  );
-*/
-
-
-
-
-/* This function is called whenever a reply for our module is received;
- * we need to register this function on module initialization;
- * Returns :   0 - core router stops
- *             1 - core router relay statelessly
- */
-int t_on_reply( struct sip_msg  *p_msg ) ;
-
-
-
-
-/* This function is called whenever a request for our module is received;
- * we need to register this function on module initialization;
- * Returns :   0 - core router stops
- *             1 - core router relay statelessly
- */
-int t_on_request_received( struct sip_msg  *p_msg , unsigned int ip, unsigned int port) ;
-
-
-
-
-/* This function is called whenever a request for our module is received;
- * we need to register this function on module initialization;
- * Returns :   0 - core router stops
- *             1 - core router relay statelessly
- */
-int t_on_request_received_uri( struct sip_msg  *p_msg ) ;
-
-
-
-
 /* returns 1 if everything was OK or -1 for error
  */
-int t_release_transaction( struct sip_msg* );
-
-
-
-
-/* Retransmits the last sent inbound reply.
- * Returns  -1 - error
- *           1 - OK
- */
-int t_retransmit_reply( /* struct sip_msg * */  );
-
+int t_release_transaction( struct cell *trans );
 
 
-
-/* Force a new response into inbound response buffer.
- * returns 1 if everything was OK or -1 for erro
- */
-int t_send_reply( struct sip_msg * , unsigned int , char *  , unsigned int);
-
-
-
-
-/* releases T-context */
-int t_unref( /* struct sip_msg* p_msg */ );
-
-
-
-/* v6; -jiri
-int t_forward_nonack( struct sip_msg* p_msg , unsigned int dest_ip_param ,
-	unsigned int dest_port_param );
-int t_forward_ack( struct sip_msg* p_msg , unsigned int dest_ip_param ,
-	unsigned int dest_port_param );
-*/
-int t_forward_nonack( struct sip_msg* p_msg, struct proxy_l * p );
-int t_forward_ack( struct sip_msg* p_msg );
-
-
-int forward_serial_branch(struct cell* Trans,int branch);
-struct cell* t_lookupOriginalT(  struct s_table* hash_table,
-	struct sip_msg* p_msg );
-int t_reply_matching( struct sip_msg* , int* ,  int* );
-int t_store_incoming_reply( struct cell* , unsigned int , struct sip_msg* );
-int t_lookup_request( struct sip_msg* p_msg , int leave_new_locked );
-int t_all_final( struct cell * );
-int t_build_and_send_ACK( struct cell *Trans , unsigned int brach ,
-	struct sip_msg* rpl);
-int t_should_relay_response( struct cell *Trans, int new_code, int branch,
-	int *should_store );
-int t_update_timers_after_sending_reply( struct retr_buf *rb );
+/* int forward_serial_branch(struct cell* Trans,int branch); */
 int t_put_on_wait(  struct cell  *Trans  );
-int relay_lowest_reply_upstream( struct cell *Trans , struct sip_msg *p_msg );
-int add_branch_label( struct cell *Trans, struct sip_msg *p_msg , int branch );
 int get_ip_and_port_from_uri( str* uri , unsigned int *param_ip,
 	unsigned int *param_port);
-int t_build_and_send_CANCEL(struct cell *Trans, unsigned int branch);
-char *build_ack( struct sip_msg* rpl, struct cell *trans, int branch ,
-	int *ret_len);
 
-int t_addifnew( struct sip_msg* p_msg );
 
 void timer_routine(unsigned int, void*);
 
+int t_newtran( struct sip_msg* p_msg );
 
+void put_on_wait(  struct cell  *Trans  );
 
+void start_retr( struct retr_buf *rb );
 
-inline int static attach_ack(  struct cell *t, int branch,
-									char *ack, int ack_len )
-{
-	LOCK_ACK( t );
-	if (t->uac[branch].request.ack) {
-		UNLOCK_ACK(t);
-		shm_free( ack );
-		LOG(L_WARN, "attach_ack: Warning: ACK already sent out\n");
-		return 0;
-	}
-	t->uac[branch].request.ack = ack;
-	t->uac[branch].request.ack_len = ack_len;
-	UNLOCK_ACK( t );
-	return 1;
-}
-
-
-
-
-/* remove from timer list */
-static inline void reset_timer( struct s_table *hash_table,
-													struct timer_link* tl )
-{
-	/* lock(timer_group_lock[ tl->tg ]); */
-	/* hack to work arround this timer group thing*/
-	lock(hash_table->timers[timer_group[tl->tg]].mutex);
-	remove_timer_unsafe( tl );
-	unlock(hash_table->timers[timer_group[tl->tg]].mutex);
-	/*unlock(timer_group_lock[ tl->tg ]);*/
-}
-
-
-
-
-/* determine timer length and put on a correct timer list */
-static inline void set_timer( struct s_table *hash_table,
-							struct timer_link *new_tl, enum lists list_id )
-{
-	unsigned int timeout;
-	struct timer* list;
+void cleanup_localcancel_timers( struct cell *t );
 
+int t_relay_to( struct sip_msg  *p_msg ,
+	struct proxy_l *proxy, int replicate ) ;
 
-	if (list_id<FR_TIMER_LIST || list_id>=NR_OF_TIMER_LISTS) {
-		LOG(L_CRIT, "ERROR: set_timer: unkown list: %d\n", list_id);
-#ifdef EXTRA_DEBUG
-		abort();
-#endif
-		return;
-	}
-	timeout = timer_id2timeout[ list_id ];
-	list= &(hash_table->timers[ list_id ]);
-
-	lock(list->mutex);
-	/* make sure I'm not already on a list */
-	remove_timer_unsafe( new_tl );
-	add_timer_unsafe( list, new_tl, get_ticks()+timeout);
-	unlock(list->mutex);
-}
-
-
-
-
-static inline void reset_retr_timers( struct s_table *h_table,
-													struct cell *p_cell )
-{
-	int ijk;
-
-	/* lock the first timer list of the FR group -- all other
-	   lists share the same lock*/
-	lock(hash_table->timers[RT_T1_TO_1].mutex);
-	remove_timer_unsafe( & p_cell->uas.response.retr_timer );
-	for( ijk=0 ; ijk<(p_cell)->nr_of_outgoings ; ijk++ )  {
-		remove_timer_unsafe( & p_cell->uac[ijk].request.retr_timer );
-	}
-	unlock(hash_table->timers[RT_T1_TO_1].mutex);
-
-	lock(hash_table->timers[FR_TIMER_LIST].mutex);
-	remove_timer_unsafe( & p_cell->uas.response.fr_timer );
-	for( ijk=0 ; ijk<(p_cell)->nr_of_outgoings ; ijk++ )  {
-		remove_timer_unsafe( & p_cell->uac[ijk].request.fr_timer );
-	}
-	unlock(hash_table->timers[FR_TIMER_LIST].mutex);
-	DBG("DEBUG:stop_RETR_and_FR_timers : timers stopped\n");
-}
-
-void delete_cell( struct cell *p_cell );
-
-int t_newtran( struct sip_msg* p_msg );
 
 #endif
 

+ 336 - 329
modules/tm/t_fwd.c

@@ -8,13 +8,20 @@
 #include "../../parser/parser_f.h"
 #include "../../ut.h"
 #include "../../timer.h"
-#include "hash_func.h"
+#include "../../hash_func.h"
+#include "../../globals.h"
+#include "../../dset.h"
 #include "t_funcs.h"
-#include "t_fork.h"
-
 #include "t_hooks.h"
+#include "t_msgbuilder.h"
+#include "ut.h"
+#include "t_cancel.h"
+#include "t_lookup.h"
+#include "t_fwd.h"
+#include "fix_lumps.h"
 
 
+#ifdef _OBSOLETED
 #define shm_free_lump( _lmp) \
 	do{\
 		if ((_lmp)) {\
@@ -23,367 +30,367 @@
 			shm_free((_lmp));\
 		}\
 	}while(0);
+#endif
 
-
-
-/* function returns:
- *       1 - forward successfull
- *      -1 - error during forward
- */
-int t_forward_nonack( struct sip_msg* p_msg , 
-					  struct proxy_l * p )
-/* v6; -jiri									unsigned int dest_ip_param ,
-												unsigned int dest_port_param )
-*/
+char *print_uac_request( struct cell *t, struct sip_msg *i_req,
+	int branch, str *uri, int *len, struct socket_info *send_sock )
 {
-	int          branch;
-	unsigned int len;
-	char         *buf, *shbuf;
-	struct cell  *T_source = T;
-	struct lump  *a,*b,*b1,*c;
-	str          backup_uri;
-	int			 ret;
-	struct socket_info* send_sock;
-	union sockaddr_union to;
-
+	char *buf, *shbuf;
 
-	/* default error value == -1; be more specific if you want to */
-	ret=-1;
-	buf    = 0;
-	shbuf  = 0;
-	backup_uri.s = p_msg->new_uri.s;
-	backup_uri.len = p_msg->new_uri.len;
+	shbuf=0;
 
+	/* ... we calculate branch ... */	
+	if (!t_setbranch( t, i_req, branch )) {
+		LOG(L_ERR, "ERROR: print_uac_request: branch computation failed\n");
+		goto error01;
+	}
 
+	/* ... update uri ... */
+	i_req->new_uri=*uri;
 
-	/* are we forwarding for the first time? */
-	if ( T->uac[0].request.buffer )
-	{	/* rewriting a request should really not happen -- retransmission
-		   does not rewrite, whereas a new request should be written
-		   somewhere else */
-		LOG( L_CRIT, "ERROR: t_forward_nonack: attempt to rewrite"
-			" request structures\n");
-		ser_error=E_BUG;
-		return 0;
-	}
+	/* ... give apps a chance to change things ... */
+	callback_event( TMCB_REQUEST_OUT, t, i_req, -i_req->REQ_METHOD);
 
-	/* v6; -jiri ... copynpasted from forward_request */
-	/* if error try next ip address if possible */
-	if (p->ok==0){
-		if (p->host.h_addr_list[p->addr_idx+1])
-			p->addr_idx++;
-		else p->addr_idx=0;
-		p->ok=1;
+	/* ... and build it now */
+	buf=build_req_buf_from_sip_req( i_req, len, send_sock );
+	if (!buf) {
+		LOG(L_ERR, "ERROR: print_uac_request: no pkg_mem\n"); 
+		ser_error=E_OUT_OF_MEM;
+		goto error01;
 	}
-	hostent2su(&to, &p->host, p->addr_idx,
-    	(p->port)?htons(p->port):htons(SIP_PORT));
-
-	/* sets as first fork the default outgoing */
-	nr_forks++;
-	/* v6; -jiri
-	t_forks[0].ip = dest_ip_param;
-	t_forks[0].port = dest_port_param;
+	/*	clean Via's we created now -- they would accumulate for
+		other branches  and for  shmem i_req they would mix up
+	 	shmem with pkg_mem
 	*/
-	t_forks[0].to=to;
-	t_forks[0].uri.len = p_msg->new_uri.len;
-	t_forks[0].uri.s =  p_msg->new_uri.s;
-	t_forks[0].free_flag = 0;
-
-	DBG("DEBUG: t_forward_nonack: first time forwarding\n");
-	/* special case : CANCEL */
-	if ( p_msg->REQ_METHOD==METHOD_CANCEL  )
-	{
-		DBG("DEBUG: t_forward_nonack: it's CANCEL\n");
-		/* find original cancelled transaction; if found, use its
-		   next-hops; otherwise use those passed by script */
-		if ( T->T_canceled==T_UNDEFINED )
-			T->T_canceled = t_lookupOriginalT( hash_table , p_msg );
-		/* if found */
-		if ( T->T_canceled!=T_NULL )
-		{
-			for(nr_forks=0;nr_forks<T->T_canceled->nr_of_outgoings;nr_forks++)
-			{
-				/* if in 1xx status, send to the same destination */
-				if ( (T->T_canceled->uac[nr_forks].status/100)==1 )
-				{
-					DBG("DEBUG: t_forward_nonack: branch %d not finalize"
-						": sending CANCEL for it\n",nr_forks);
-					/* v6; -jiri
-					t_forks[nr_forks].ip =
-					  T->T_canceled->uac[nr_forks].request.to.sin_addr.s_addr; 
-					t_forks[nr_forks].port =
-					  T->T_canceled->uac[nr_forks].request.to.sin_port;
-					*/
-					t_forks[nr_forks].to = T->T_canceled->uac[nr_forks].request.to;
-
-					t_forks[nr_forks].uri.len =
-					  T->T_canceled->uac[nr_forks].uri.len;
-					t_forks[nr_forks].uri.s =
-					  T->T_canceled->uac[nr_forks].uri.s;
-					t_forks[nr_forks].free_flag = 0;
-				}else{
-					/* transaction exists, but nothing to cancel */
-					DBG("DEBUG: t_forward_nonack: branch %d finalized"
-						": no CANCEL sent here\n",nr_forks);
-					/* -v6; -jiri
-					t_forks[nr_forks].ip = 0;
-					*/
-					t_forks[nr_forks].inactive= 1;
-				}
-			}
-#ifdef USE_SYNONIM
-			T_source = T->T_canceled;
-			T->label  = T->T_canceled->label;
-#endif
-		} else { /* transaction doesnot exists  */
-			DBG("DEBUG: t_forward_nonack: canceled request not found! "
-			"nothing to CANCEL\n");
+#ifdef OBSOLETED
+	if (branch) for(b=i_req->add_rm,b1=0;b;b1=b,b=b->next)
+		if (b->type==HDR_VIA) {
+			for(a=b->before;a;)
+				{c=a->before;free_lump(a);pkg_free(a);a=c;}
+			for(a=b->after;a;)
+				{c=a->after;free_lump(a);pkg_free(a);a=c;}
+			if (b1) b1->next = b->next;
+			else i_req->add_rm = b->next;
+			free_lump(b);pkg_free(b);
 		}
-	}/* end special case CANCEL*/
+#endif
+	free_via_lump(&i_req->add_rm);
+
+	shbuf=(char *)shm_malloc(*len);
+	if (!shbuf) {
+		ser_error=E_OUT_OF_MEM;
+		LOG(L_ERR, "ERROR: print_uac_request: no shmem\n");
+		goto error02;
+	}
+	memcpy( shbuf, buf, *len );
+
+error02:
+	pkg_free( buf );
+error01:
+	return shbuf;
+}
+
+/* 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 itnerfere with the processes of adding multiple branches
+*/
+int add_uac( struct cell *t, struct sip_msg *request, str *uri, 
+	struct proxy_l *proxy )
+{
+
+	int ret;
+	short temp_proxy;
+	union sockaddr_union to;
+	unsigned short branch;
+	struct socket_info* send_sock;
+	char *shbuf;
+	unsigned int len;
 
-#ifndef USE_SYNONIM
-	branch=0;
-	if ( nr_forks && add_branch_label( T_source, T->uas.request , branch )==-1)
+	branch=t->nr_of_outgoings;
+	if (branch==MAX_BRANCHES) {
+		LOG(L_ERR, "ERROR: add_uac: maximum number of branches exceeded\n");
+		ret=E_CFG;
 		goto error;
-#endif
+	}
 
-	DBG("DEBUG: t_forward_nonack: nr_forks=%d\n",nr_forks);
-	for(branch=0;branch<nr_forks;branch++)
-	{
-		/* -v6; -jiri if (!t_forks[branch].ip) */
-		if (t_forks[branch].inactive)
-			goto end_loop;
-		DBG("DEBUG: t_forward_nonack: branch = %d\n",branch);
-		/*generates branch param*/
-		if ( add_branch_label( T_source, p_msg , branch )==-1)
-			goto error;
-		/* remove all the HDR_VIA type lumps */
-		if (branch)
-			for(b=p_msg->add_rm,b1=0;b;b1=b,b=b->next)
-				if (b->type==HDR_VIA)
-				{
-					for(a=b->before;a;)
-						{c=a->before;free_lump(a);pkg_free(a);a=c;}
-					for(a=b->after;a;)
-						{c=a->after;free_lump(a);pkg_free(a);a=c;}
-					if (b1) b1->next = b->next;
-						else p_msg->add_rm = b->next;
-					free_lump(b);pkg_free(b);
-				}
-		/* updates the new uri*/
-		p_msg->new_uri.s = t_forks[branch].uri.s;
-		p_msg->new_uri.len = t_forks[branch].uri.len;
-
-		T->uac[branch].request.to = t_forks[branch].to;
-		send_sock=get_send_socket( & T->uac[branch].request.to );
-		if (send_sock==0) {
-			LOG(L_ERR, "ERROR: t_forward_nonack: can't fwd to af %d "
-				"no corresponding listening socket\n", 
-				T->uac[branch].request.to.s.sa_family);
-			ser_error=E_NO_SOCKET;
-			goto error;
-		}
-		T->uac[branch].request.send_sock=send_sock;
-		
-		callback_event( TMCB_REQUEST_OUT, T, p_msg );	
-		/* _test_insert_to_reply(p_msg, "Foo: Bar\r\n");*/
-		if ( !(buf = build_req_buf_from_sip_req  ( p_msg, &len, send_sock ))) {
-			ser_error=ret=E_OUT_OF_MEM;
-			goto error;
-		}
-		/* allocates a new retrans_buff for the outbound request */
-		DBG("DEBUG: t_forward_nonack: building outbound request"
-			" for branch %d.\n",branch);
-		shbuf = (char *) shm_malloc( len );
-		if (!shbuf)
-		{
-			LOG(L_ERR, "ERROR: t_forward_nonack: out of shmem buffer\n");
-			ser_error=ret=E_OUT_OF_MEM;
-			goto error;
-		}
-		T->uac[branch].request.buffer = shbuf;
-		T->uac[branch].request.buffer_len = len ;
-		memcpy( T->uac[branch].request.buffer , buf , len );
-		/* keeps a hooker to uri inside buffer*/
-		T->uac[branch].uri.s = T->uac[branch].request.buffer +
-			(p_msg->first_line.u.request.uri.s - p_msg->buf);
-		T->uac[branch].uri.len=t_forks[branch].uri.s?(t_forks[branch].uri.len)
-			:(p_msg->first_line.u.request.uri.len);
-		/* send the request */
-		/* v6; -jiri
-		T->uac[branch].request.to.sin_addr.s_addr = t_forks[branch].ip;
-		T->uac[branch].request.to.sin_port = t_forks[branch].port;
-		T->uac[branch].request.to.sin_family = AF_INET;
-		*/
-		T->uac[branch].request.to = t_forks[branch].to;
-		p->tx++;
-		p->tx_bytes+=len;
-		if (SEND_BUFFER( &(T->uac[branch].request) )==-1) {
-			p->errors++;
-			p->ok=0;
-			ser_error=ret=E_SEND;
+	/* check existing buffer -- rewriting should never occur */
+	if (t->uac[branch].request.buffer) {
+		LOG(L_CRIT, "ERROR: add_uac: buffer rewrite attempt\n");
+		ret=ser_error=E_BUG;
+		goto error;
+	}
+
+	/* check DNS resolution */
+	if (proxy) temp_proxy=0; else {
+		proxy=uri2proxy( uri );
+		if (proxy==0)  {
+			ret=E_BAD_ADDRESS;
 			goto error;
 		}
-		/* should have p->errors++; p->ok=0; on error here... */
-
-
-		pkg_free( buf ) ;
-		buf=NULL;
-
-		DBG("DEBUG: t_forward_nonack: starting timers (retrans and FR) %d\n",
-			get_ticks() );
-		/*sets and starts the FINAL RESPONSE timer */
-		set_timer( hash_table, &(T->uac[branch].request.fr_timer),
-		/*p_msg->REQ_METHOD==METHOD_INVITE?FR_INV_TIMER_LIST:FR_TIMER_LIST);*/
-			FR_TIMER_LIST ); 
-		/* sets and starts the RETRANS timer */
-		T->uac[branch].request.retr_list = RT_T1_TO_1;
-		set_timer( hash_table, &(T->uac[branch].request.retr_timer),
-			RT_T1_TO_1 );
-		end_loop:
-		T->nr_of_outgoings++ ;
-		DBG("DEBUG: branch %d done; outgoing uri=|%.*s|\n",branch,
-			T->uac[branch].uri.len,T->uac[branch].uri.s);
+		temp_proxy=1;
 	}
 
-	/* if we have a branch spec. for NO_RESPONSE_RECEIVED, we have to 
-	move it immediatly after the last parallel branch */
-	/* v6; -jiri 
-	if (t_forks[NO_RPL_BRANCH].ip && T->nr_of_outgoings!=NO_RPL_BRANCH ) */
-	if (!t_forks[NO_RPL_BRANCH].inactive && T->nr_of_outgoings!=NO_RPL_BRANCH )
-	{
-		branch = T->nr_of_outgoings;
-		/* v6; -jiri
-		T->uac[branch].request.to.sin_addr.s_addr = t_forks[NO_RPL_BRANCH].ip;
-		T->uac[branch].request.to.sin_port = t_forks[NO_RPL_BRANCH].port;
-		*/
-		T->uac[branch].request.to = t_forks[NO_RPL_BRANCH].to;
-
-		T->uac[branch].uri.s = t_forks[NO_RPL_BRANCH].uri.s;
-		T->uac[branch].uri.len = t_forks[NO_RPL_BRANCH].uri.len;
+	if (proxy->ok==0) {
+		if (proxy->host.h_addr_list[proxy->addr_idx+1])
+			proxy->addr_idx++;
+		else proxy->addr_idx=0;
+		proxy->ok=1;
 	}
-	p_msg->new_uri.s = backup_uri.s;
-	p_msg->new_uri.len = backup_uri.len;
-	t_clear_forks();
-	return 1;
 
+	hostent2su( &to, &proxy->host, proxy->addr_idx, 
+		proxy->port ? htons(proxy->port):htons(SIP_PORT));
+
+	send_sock=get_send_socket( &to );
+	if (send_sock==0) {
+		LOG(L_ERR, "ERROR: add_uac: can't fwd to af %d "
+			" (no corresponding listening socket)\n",
+			to.s.sa_family );
+		ret=ser_error=E_NO_SOCKET;
+		goto error01;
+	}
+
+	/* now message printing starts ... */
+	shbuf=print_uac_request( t, request, branch, uri, 
+		&len, send_sock );
+	if (!shbuf) {
+		ret=ser_error=E_OUT_OF_MEM;
+		goto error01;
+	}
+
+	/* things went well, move ahead and install new buffer! */
+	t->uac[branch].request.to=to;
+	t->uac[branch].request.send_sock=send_sock;
+	t->uac[branch].request.buffer=shbuf;
+	t->uac[branch].request.buffer_len=len;
+	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++;
+
+	/* update stats */
+	proxy->tx++;
+	proxy->tx_bytes+=len;
+
+	/* done! */	
+	ret=branch;
+		
+error01:
+	if (temp_proxy) {
+		free_proxy( proxy );
+		free( proxy );
+	}
 error:
-	if (shbuf) shm_free(shbuf);
-	T->uac[branch].request.buffer=NULL;
-	if (buf) pkg_free( buf );
-	p_msg->new_uri.s = backup_uri.s;
-	p_msg->new_uri.len = backup_uri.len;
-	t_clear_forks();
 	return ret;
 }
 
-
-int forward_serial_branch(struct cell* Trans,int branch)
+int e2e_cancel_branch( struct sip_msg *cancel_msg, struct cell *t_cancel, 
+	struct cell *t_invite, int branch )
 {
-	struct sip_msg*  p_msg = Trans->uas.request;
-	struct lump      *a, *b, *b1, *c;
-	unsigned int     len;
-	char             *buf=0, *shbuf=0;
-	str              backup_uri;
-	union sockaddr_union *to;
-	struct socket_info* send_sock;
+	int ret;
+	char *shbuf;
+	int len;
 
-	backup_uri.s = p_msg->new_uri.s;
-	backup_uri.len = p_msg->new_uri.len;
-
-	/*generates branch param*/
-	if ( add_branch_label( Trans, p_msg , branch )==-1)
+	if (t_cancel->uac[branch].request.buffer) {
+		LOG(L_CRIT, "ERROR: e2e_cancel_branch: buffer rewrite attempt\n");
+		ret=ser_error=E_BUG;
 		goto error;
-	/* remove all the HDR_VIA type lumps - they are in SHM memory!!! */
-	for(b=p_msg->add_rm,b1=0;b;b1=b,b=b->next)
-		if (b->type==HDR_VIA)
-		{
-			for(a=b->before;a;)
-				{c=a->before;shm_free_lump(a);a=c;}
-			for(a=b->after;a;)
-				{c=a->after;shm_free_lump(a);a=c;}
-			if (b1) b1->next = b->next;
-				else p_msg->add_rm = b->next;
-			shm_free_lump(b);
-		}
+	}	
 
-	LOG(L_ERR,"DEBUG: t_forward_serial_branch: building req for branch"
-		"%d; uri=|%.*s|.\n", branch, Trans->uac[branch].uri.len,
-		Trans->uac[branch].uri.s);
-	/* updates the new uri*/
-	p_msg->new_uri.s = Trans->uac[branch].uri.s;
-	p_msg->new_uri.len = Trans->uac[branch].uri.len;
+	/* note -- there is a gap in proxy stats -- we don't update 
+	   proxy stats with CANCEL (proxy->ok, proxy->tx, etc.)
+	*/
 
-	to=&Trans->uac[branch].request.to;
-	send_sock=get_send_socket(to);
-	if (send_sock==0) {
-		LOG(L_ERR, "ERROR: t_forward_nonack: can't fwd to af %d "
-		"no corresponding listening socket\n", to->s.sa_family );
-		ser_error=E_NO_SOCKET;
+	/* print */
+	shbuf=print_uac_request( t_cancel, cancel_msg, branch, 
+		&t_invite->uac[branch].uri, &len, 
+		t_invite->uac[branch].request.send_sock);
+	if (!shbuf) {
+		LOG(L_ERR, "ERROR: e2e_cancel_branch: printing e2e cancel failed\n");
+		ret=ser_error=E_OUT_OF_MEM;
 		goto error;
 	}
-	if ( !(buf = build_req_buf_from_sip_req  ( p_msg, &len, send_sock )))
-		goto error;
-	shm_free(Trans->uac[branch].uri.s);
+	
+	/* install buffer */
+	t_cancel->uac[branch].request.to=t_invite->uac[branch].request.to;
+	t_cancel->uac[branch].request.send_sock=t_invite->uac[branch].request.send_sock;
+	t_cancel->uac[branch].request.buffer=shbuf;
+	t_cancel->uac[branch].request.buffer_len=len;
+	t_cancel->uac[branch].uri.s=t_cancel->uac[branch].request.buffer+
+		cancel_msg->first_line.u.request.method.len+1;
+	t_cancel->uac[branch].uri.len=t_invite->uac[branch].uri.len;
+	
 
-	/* allocates a new retrans_buff for the outbound request */
-	shbuf = (char *) shm_malloc( len );
-	if (!shbuf)
-	{
-		LOG(L_ERR, "ERROR: t_forward_serial_branch: out of shmem buffer\n");
-		goto error;
-	}
-	Trans->uac[branch].request.buffer = shbuf;
-	Trans->uac[branch].request.buffer_len = len ;
-	memcpy( Trans->uac[branch].request.buffer , buf , len );
-	/* keeps a hooker to uri inside buffer*/
-	Trans->uac[branch].uri.s = Trans->uac[branch].request.buffer +
-		(p_msg->first_line.u.request.uri.s - p_msg->buf);
-	Trans->uac[branch].uri.len=p_msg->new_uri.len?(p_msg->new_uri.len)
-		:(p_msg->first_line.u.request.uri.len);
-	Trans->nr_of_outgoings++ ;
-	/* send the request */
-	/* -v6; -jiri Trans->uac[branch].request.to.sin_family = AF_INET; */
-	SEND_BUFFER( &(T->uac[branch].request) );
-
-	pkg_free( buf ) ;
-	buf=NULL;
-
-	DBG("DEBUG: t_forward_serial_branch:starting timers (retrans and FR) %d\n",
-		get_ticks() );
-	/*sets and starts the FINAL RESPONSE timer */
-	set_timer( hash_table, &(T->uac[branch].request.fr_timer), 
-			FR_TIMER_LIST ); 
-			/* p_msg->REQ_METHOD==METHOD_INVITE ? FR_INV_TIMER_LIST : FR_TIMER_LIST ); */
-	/* sets and starts the RETRANS timer */
-	T->uac[branch].request.retr_list = RT_T1_TO_1;
-	set_timer( hash_table, &(T->uac[branch].request.retr_timer), RT_T1_TO_1 );
-
-	p_msg->new_uri.s = backup_uri.s;
-	p_msg->new_uri.len = backup_uri.len;
-
-	for(b=p_msg->add_rm,b1=0;b;b1=b,b=b->next)
-		if (b->type==HDR_VIA)
-		{
-			for(a=b->before;a;)
-				{c=a->before;free_lump(a);pkg_free(a);a=c;}
-			for(a=b->after;a;)
-				{c=a->after;free_lump(a);pkg_free(a);a=c;}
-			if (b1) b1->next = b->next;
-				else p_msg->add_rm = b->next;
-			free_lump(b);pkg_free(b);
-		}
+	/* success */
+	ret=1;
 
-	return 1;
 
 error:
-	if (shbuf) shm_free(shbuf);
-	T->uac[branch].request.buffer=NULL;
-	if (buf) pkg_free( buf );
-	p_msg->new_uri.s = backup_uri.s;
-	p_msg->new_uri.len = backup_uri.len;
-	return -1;
+	return ret;
 }
 
+void e2e_cancel( struct sip_msg *cancel_msg, 
+	struct cell *t_cancel, struct cell *t_invite )
+{
+	branch_bm_t cancel_bm;
+	int i;
+	int lowest_error;
+	str backup_uri;
+	int ret;
+
+	cancel_bm=0;
+	lowest_error=0;
+
+	backup_uri=cancel_msg->new_uri;
+	/* determine which branches to cancel ... */
+	which_cancel( t_invite, &cancel_bm );
+	/* ... and install CANCEL UACs */
+	for (i=0; i<t_invite->nr_of_outgoings; i++)
+		if (cancel_bm & (1<<i)) {
+			ret=e2e_cancel_branch(cancel_msg, t_cancel, t_invite, i);
+			if (ret<0) cancel_bm &= ~(1<<i);
+			if (ret<lowest_error) lowest_error=ret;
+		}
+	t_cancel->nr_of_outgoings=t_invite->nr_of_outgoings;
+	t_cancel->label=t_invite->label;
+	cancel_msg->new_uri=backup_uri;
+
+	/* send them out */
+	for (i=0; i<t_cancel->nr_of_outgoings; i++) {
+		if (cancel_bm & (1<<i)) {
+			if (SEND_BUFFER( &t_cancel->uac[i].request)==-1) {
+				LOG(L_ERR, "ERROR: e2e_cancel: send failed\n");
+			}
+			start_retr( &t_cancel->uac[i].request );
+		}
+	}
+
 
+	/* if error occured, let it know upstream (final reply
+	   will also move the transaction on wait state
+	*/
+	if (lowest_error<0) {
+		LOG(L_ERR, "ERROR: cancel error\n");
+		t_reply( t_cancel, cancel_msg, 500, "cancel error");
+	/* if there are pending branches, let upstream know we
+	   are working on it
+	*/
+	} else if (cancel_bm) {
+		DBG("DEBUG: e2e_cancel: e2e cancel proceeding\n");
+		t_reply( t_cancel, cancel_msg, 100, "trying to cancel" );
+	/* if the transaction exists, but there is no more pending
+	   branch, tell usptream we're done
+	*/
+	} else {
+		DBG("DEBUG: e2e_cancel: e2e cancel -- no more pending branches\n");
+		t_reply( t_cancel, cancel_msg, 200, "ok, no more pending branches" );
+	}
+}
 
+
+/* function returns:
+ *       1 - forward successfull
+ *      -1 - error during forward
+ */
+int t_forward_nonack( struct cell *t, struct sip_msg* p_msg , 
+	struct proxy_l * proxy )
+{
+	str          backup_uri;
+	int branch_ret, lowest_ret;
+	str current_uri;
+	branch_bm_t	added_branches;
+	int first_branch;
+	int i;
+	struct cell *t_invite;
+
+	/* make -Wall happy */
+	current_uri.s=0;
+
+	t->kr|=REQ_FWDED;
+
+	if (p_msg->REQ_METHOD==METHOD_CANCEL) {
+		t_invite=t_lookupOriginalT( hash_table, p_msg );
+		if (t_invite!=T_NULL) {
+			e2e_cancel( p_msg, t, t_invite );
+			UNREF(t_invite);
+			return 1;
+		}
+	}
+
+	/* backup current uri ... add_uac changes it */
+	backup_uri = p_msg->new_uri;
+	/* if no more specific error code is known, use this */
+	lowest_ret=E_BUG;
+	/* branches added */
+	added_branches=0;
+	/* branch to begin with */
+	first_branch=t->nr_of_outgoings;
+
+	/* on first-time forwarding, use current uri, later only what
+	   is in additional branches (which may be continuously refilled
+	*/
+	if (first_branch==0) {
+		branch_ret=add_uac( t, p_msg, 
+			p_msg->new_uri.s ? &p_msg->new_uri :  
+				&p_msg->first_line.u.request.uri,
+			proxy );
+		if (branch_ret>=0) 
+			added_branches |= 1<<branch_ret;
+		else
+			lowest_ret=branch_ret;
+	}
+
+	init_branch_iterator(p_msg);
+	while((current_uri.s=next_branch( &current_uri.len))) {
+		branch_ret=add_uac( t, p_msg, &current_uri, proxy );
+		/* 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
+		   branch result */
+		if (branch_ret>=0) 
+			added_branches |= 1<<branch_ret;
+		else
+			lowest_ret=branch_ret;
+	}
+	/* consume processed branches */
+	clear_branches();
+
+	/* restore original URI */
+	p_msg->new_uri=backup_uri;
+
+	/* don't forget to clear all branches processed so far */
+
+	/* things went wrong ... no new branch has been fwd-ed at all */
+	if (added_branches==0)
+		return lowest_ret;
+
+	/* if someone set on_negative, store in in T-context */
+	t->on_negative=get_on_negative();
+
+	/* send them out now */
+	for (i=first_branch; i<t->nr_of_outgoings; i++) {
+		if (added_branches & (1<<i)) {
+			if (SEND_BUFFER( &t->uac[i].request)==-1) {
+				LOG(L_ERR, "ERROR: add_uac: sending request failed\n");
+				if (proxy) { proxy->errors++; proxy->ok=0; }
+			}
+			start_retr( &t->uac[i].request );
+		}
+	}
+	return 1;
+}	
+
+int t_replicate(struct sip_msg *p_msg,  struct proxy_l *proxy )
+{
+	/* this is a quite horrible hack -- we just take the message
+	   as is, including Route-s, Record-route-s, and Vias ,
+	   forward it downstream and prevent replies received
+	   from relaying by setting the replication/local_trans bit;
+
+		nevertheless, it should be good enough for the primary
+		customer of this function, REGISTER replication
+
+		if we want later to make it thoroughly, we need to
+		introduce delete lumps for all the header fields above
+	*/
+	return t_relay_to(p_msg, proxy, 1 /* replicate */);
+}

+ 25 - 0
modules/tm/t_fwd.h

@@ -0,0 +1,25 @@
+/*
+ * $Id$
+ *
+ */
+
+#ifndef _T_FWD_H
+#define _T_FWD_H
+
+#include "../../proxy.h"
+
+typedef int (*tfwd_f)(struct sip_msg* p_msg , struct proxy_l * proxy );
+
+int t_replicate(struct sip_msg *p_msg, struct proxy_l * proxy );
+char *print_uac_request( struct cell *t, struct sip_msg *i_req,
+    int branch, str *uri, int *len, struct socket_info *send_sock );
+void e2e_cancel( struct sip_msg *cancel_msg, struct cell *t_cancel, struct cell *t_invite );
+int e2e_cancel_branch( struct sip_msg *cancel_msg, struct cell *t_cancel, struct cell *t_invite, int branch );
+int add_uac( struct cell *t, struct sip_msg *request, str *uri, struct proxy_l *proxy );
+int t_forward_nonack( struct cell *t, struct sip_msg* p_msg, struct proxy_l * p );
+int t_forward_ack( struct sip_msg* p_msg );
+
+
+#endif
+
+

+ 9 - 7
modules/tm/t_hooks.c

@@ -2,16 +2,18 @@
  * $Id$
  */
 
-
+#include "stdlib.h"
+#include "../../dprint.h"
+#include "../../error.h"
 #include "t_hooks.h"
 
-static struct tm_callback_s* callback_array[ TMCB_END ] = { NULL, NULL } ;
+static struct tm_callback_s* callback_array[ TMCB_END ] = { 0, 0 } ;
 static int callback_id=0;
 
 /* register a callback function 'f' of type 'cbt'; will be called
    back whenever the event 'cbt' occurs in transaction module
 */
-int register_tmcb( tmcb_type cbt, transaction_cb f )
+int register_tmcb( tmcb_type cbt, transaction_cb f, void *param )
 {
 	struct tm_callback_s *cbs;
 
@@ -30,19 +32,19 @@ int register_tmcb( tmcb_type cbt, transaction_cb f )
 	cbs->id=callback_id;
 	cbs->callback=f;
 	cbs->next=callback_array[ cbt ];
+	cbs->param=param;
 	callback_array[ cbt ]=cbs;
 
 	return callback_id;
 }
 
 void callback_event( tmcb_type cbt , struct cell *trans,
-	struct sip_msg *msg )
+	struct sip_msg *msg, int code )
 {
 	struct tm_callback_s *cbs;
 
-	DBG("DBG: callback type %d entered\n", cbt );
 	for (cbs=callback_array[ cbt ]; cbs; cbs=cbs->next)  {
-		DBG("DBG: callback id %d entered\n", cbs->id );
-		cbs->callback( trans, msg );
+		DBG("DBG: callback type %d, id %d entered\n", cbt, cbs->id );
+		cbs->callback( trans, msg, code, cbs->param );
 	}
 }

+ 79 - 23
modules/tm/t_hooks.h

@@ -5,49 +5,105 @@
 #ifndef _HOOKS_H
 #define _HOOKS_H
 
-#include "h_table.h"
-#include "t_funcs.h"
+struct sip_msg;
+struct cell;
 
 typedef enum { TMCB_REPLY,  TMCB_E2EACK, TMCB_REPLY_IN, 
-	TMCB_REQUEST_OUT, TMCB_END } tmcb_type;
+	TMCB_REQUEST_OUT, TMCB_LOCAL_COMPLETED, TMCB_ON_NEGATIVE,
+	TMCB_END } tmcb_type;
 
 /* 
 	TMCB_REPLY	-  a reply has been sent out
-				  (no chance to change anything; still good enough
-				  for reporting callbacks do not need to change anything
-				  and better do not utilize TMCB_REPLY_IN, which would
-				  resulting in the callback spending its time in
-				  REPLY_LOCK unnecessarily)
-	TMCB_E2EACK - presumably, an end2end ACK was received and
-				  is about to be processed statelessly (note that
-				  we cannot reliably determine that an e2e really
-				  does belong to a transaction -- it is about guessing;
-				  the reason is e2e ACKs are stand-alone transactions
-				  which may have r-uri/via different from INVITE and
-				  take a different path; however, with RR, chances are
-				  good to match (though transaction matching won't work
-				  with spirals -- as all transaction instances "match"
-				  the request and only the first will be taken)
+	  no chance to change anything in the message; 
+	  still good enough for many uses, such as accounting
+	  of completed transactions; note well that the message
+	  passed to the callback may also have value FAKED_REPLY,
+	  i.e., refering to it will segfault
 	TMCB_REPLY_IN - a reply was received and is about to be forwarded;
-	TMCB_REQUEST_OUT - a request was received and is about to be fwd-ed
+	  compared to TMCB_REPLY, it is a very internal callback and
+	  you should use it with lot of caution
+	  - it allows you to change the message (called before printing
+	    the relayed message)
+	  - it is called from a reply lock -- it is mroe dangerous and
+	    anything you do makes the processes spend more time in
+	    the lock, decreasing overall performance
+	  - is is called only for replies >100, <300 (final replies
+	    might be cached on forking, stored in shmem -- then, there
+		is no more easy way to change messages)
+	  - as it is called before printing and forwarding, there is
+	    no guarantee the message will be sent out -- either can
+	    fail
+
+		Note: none of the reply callbacks will be evoked if
+		"silent C timer" hits. Silent C timer is a feature which
+		prevents cancellation of a call in progress by a server
+		in the middle, when C timer expires. On one side, 
+		INVITE transactional state cannot be kept for ever,
+		on the other side you want to allow long ringing 
+		uninterrupted by a proxy server. The silent_c feature
+		-- if circumstances allow -- simply discards transaction
+		state when C timer hits, the transaction can then complete
+		statelessly. Then, however, the stateful callback will
+		NOT be called. If you do not wish this behaviour (e.g.,
+		for sake of transaction accounting, in which you do
+		not desire users to wait until silent C hits and
+		eventually complete an unaccounted transaction), turn
+		silent C off either globaly (TM option "noisy_ctimer"
+		set to 1) or for a specific transaction (you can for
+		example set the transaction member "noisy_timer"
+		from request callback.)
+
+	TMCB_E2EACK - presumably, an end2end ACK was received and
+		is about to be processed statelessly; you better don't
+	    use this callback as there is no reliable way to match
+	    an e2e ACK to an INVITE transaction, we just try it for
+	    those, who believe they can't live without knowing about
+	    the ACK; There are more reasons why the e2e ACK callback
+	    is never triggered: 1) the e2eACK does not pass the server
+	    at all 2) the e2e ACK does not match an INVITE transaction
+		because its r-uri or via is different
+	TMCB_REQUEST_OUT - a request was received and is about to be fwd-ed;
+		it is not called on retransmissions; it is called prior to
+		printing the relayed message, i.e., changes to it can
+		be done
+	TMCB_LOCAL_COMPLETED - a local transaction completed; note that
+	    the callback parameter may be FAKED_REPLY
+	TMCB_MISSED -- transaction was replied with a negative value;
+		called from within a REPLY_LOCK, message may be FAKED_REPLY
+	TMCB_ON_NEGATIVE -- called whenever a transaction is about to complete
+	    with a negative result; it's a great time to introduce a new
+	    uac (serial forking) or change the reply code; be cautions
+	    though -- it is called from within REPLY_LOCK and careless
+	    usage of the callback can easily result in a deadlock; msg
+	    is always 0 (callback refers to whole transaction and not
+	    to individual message), code is the currently lowest status
+	    code
 	TMCB_END	- just a bumper
+
+	see the 'acc' module for an example of callback usage
+
+	note that callbacks MUST be installed before forking
+    (callback lists do not live in shmem and have no access
+	protection)
 */
 
-typedef void (transaction_cb) ( struct cell* t, struct sip_msg* msg );
+typedef void (transaction_cb) ( struct cell* t, struct sip_msg* msg, 
+	int code, void *param );
 
 struct tm_callback_s {
 	int id;
 	transaction_cb* callback;
 	struct tm_callback_s* next;
+	void *param;
 };
 
 
 extern struct tm_callback_s* callback_array[ TMCB_END ];
 
-typedef int (*register_tmcb_f)(tmcb_type cbt, transaction_cb f);
+typedef int (*register_tmcb_f)(tmcb_type cbt, transaction_cb f, void *param);
 
-int register_tmcb( tmcb_type cbt, transaction_cb f );
+int register_tmcb( tmcb_type cbt, transaction_cb f, void *param );
 void callback_event( tmcb_type cbt, struct cell *trans,
-	struct sip_msg *msg );
+	struct sip_msg *msg, int code );
 
 #endif

+ 305 - 386
modules/tm/t_lookup.c

@@ -21,7 +21,7 @@
  *
  * The branch parameter is formed as follows:
  * SYNONYMS  on: hash.synonym.branch
- * SYNONYMS off: md5.hash.branch
+ * SYNONYMS off: hash.md5.branch
  *
  * -jiri
  *
@@ -33,11 +33,14 @@
 #include "../../parser/parser_f.h"
 #include "../../ut.h"
 #include "../../timer.h"
-#include "hash_func.h"
+#include "../../hash_func.h"
+#include "../../globals.h"
+#include "../../forward.h"
 #include "t_funcs.h"
 #include "config.h"
 #include "sip_msg.h"
 #include "t_hooks.h"
+#include "t_lookup.h"
 
 
 #define EQ_LEN(_hf) (t_msg->_hf->body.len==p_msg->_hf->body.len)
@@ -60,9 +63,34 @@
 	 (t_msg->via1->bsize-(t_msg->_via->name.s-(t_msg->_via->hdr.s+t_msg->_via->hdr.len)))\
 	)==0 )
 
+#define HF_LEN(_hf) ((_hf)->body.s+(_hf)->body.len-(_hf)->name.s)
+
 /* presumably matching transaction for an e2e ACK */
 static struct cell *t_ack;
 
+/* this is a global variable which keeps pointer to
+   transaction currently processed by a process; it it
+   set by t_lookup_request or t_reply_matching; don't
+   dare to change it anywhere else as it would
+   break ref_counting
+*/
+
+#ifdef _OBSOLETED
+struct cell      *T;
+#endif
+
+static struct cell *T;
+
+/* number of currently processed message; good to know
+   to be able to doublecheck whether we are still working
+   on a current transaction or a new message arrived;
+   don't even think of changing it
+*/
+unsigned int     global_msg_id;
+
+struct cell *get_t() { return T; }
+void init_t() {global_msg_id=0; T=T_UNDEFINED;}
+
 
 /* function returns:
  *      negative - transaction wasn't found
@@ -87,7 +115,9 @@ int t_lookup_request( struct sip_msg* p_msg , int leave_new_locked )
 	}
 
 	/* start searching into the table */
-	p_msg->hash_index=hash( p_msg->callid->body , get_cseq(p_msg)->number ) ;
+	if (!p_msg->hash_index)
+		p_msg->hash_index=hash( p_msg->callid->body , 
+			get_cseq(p_msg)->number ) ;
 	isACK = p_msg->REQ_METHOD==METHOD_ACK;
 	DBG("t_lookup_request: start searching: hash=%d, isACK=%d\n",
 		p_msg->hash_index,isACK);
@@ -96,7 +126,7 @@ int t_lookup_request( struct sip_msg* p_msg , int leave_new_locked )
 	ret=-1;
 
 	/* lock the hole entry*/
-	lock(&(hash_table->entrys[p_msg->hash_index].mutex));
+	LOCK_HASH(p_msg->hash_index);
 
 	/* all the transactions from the entry are compared */
 	for ( p_cell = hash_table->entrys[p_msg->hash_index].first_cell;
@@ -138,7 +168,7 @@ int t_lookup_request( struct sip_msg* p_msg , int leave_new_locked )
 			if (get_to(t_msg)->uri.len!=get_to(p_msg)->uri.len)
 				continue;
 			/* ... its to-tag compared to reply's tag */
-			if (p_cell->uas.tag->len!=get_to(p_msg)->tag_value.len)
+			if (p_cell->uas.to_tag.len!=get_to(p_msg)->tag_value.len)
 				continue;
 
 			/* we first skip r-uri and Via and proceed with
@@ -151,8 +181,9 @@ int t_lookup_request( struct sip_msg* p_msg , int leave_new_locked )
 			if (!EQ_STR(from)) continue;
 			if (memcmp(get_to(t_msg)->uri.s, get_to(p_msg)->uri.s,
 				get_to(t_msg)->uri.len)!=0) continue;
-			if (memcmp(p_cell->uas.tag->s, get_to(p_msg)->tag_value.s,
-				p_cell->uas.tag->len)!=0) continue;
+			if (p_cell->uas.to_tag.len!=0 /* to-tags empty */
+				|| memcmp(p_cell->uas.to_tag.s, get_to(p_msg)->tag_value.s,
+				p_cell->uas.to_tag.len)!=0) continue;
 	
 			/* ok, now only r-uri or via can mismatch; they must match
 			   for non-2xx; if it is a 2xx, we don't try to match
@@ -187,177 +218,21 @@ int t_lookup_request( struct sip_msg* p_msg , int leave_new_locked )
 
 	/* no transaction found */
 	T = 0;
-	if (!leave_new_locked)
-		unlock(&(hash_table->entrys[p_msg->hash_index].mutex));
-	DBG("DEBUG: t_lookup_request: no transaction found\n");
-	return ret;
-
-found:
-	T=p_cell;
-	T_REF( T );
-	DBG("DEBUG: t_lookup_request: transaction found (T=%p , ref=%x)\n",
-		T,T->ref_bitmap);
-	unlock(&(hash_table->entrys[p_msg->hash_index].mutex));
-	return 1;
-}
-
-
-#ifdef __YOU_DONT_WANT_TO_DO_THIS
-
-/* function returns:
- *      -1 - transaction wasn't found
- *       1  - transaction found
- */
-int t_lookup_request( struct sip_msg* p_msg , int leave_new_locked )
-{
-	struct cell         *p_cell;
-	struct cell         *tmp_cell;
-	unsigned int       isACK;
-	struct sip_msg  *t_msg;
-
-	/* parse all*/
-	if (check_transaction_quadruple(p_msg)==0)
-	{
-		LOG(L_ERR, "ERROR: TM module: t_lookup_request: too few headers\n");
-		T=0;
-		/* stop processing */
-		return 0;
+	if (!leave_new_locked) {
+		UNLOCK_HASH(p_msg->hash_index);
 	}
-
-	/* start searching into the table */
-	p_msg->hash_index=hash( p_msg->callid->body , get_cseq(p_msg)->number ) ;
-	isACK = p_msg->REQ_METHOD==METHOD_ACK;
-	DBG("t_lookup_request: start searching: hash=%d, isACK=%d\n",
-		p_msg->hash_index,isACK);
-
-	/* lock the hole entry*/
-	lock(&(hash_table->entrys[p_msg->hash_index].mutex));
-
-	/* all the transactions from the entry are compared */
-	p_cell   = hash_table->entrys[p_msg->hash_index].first_cell;
-	tmp_cell = 0;
-	while( p_cell )
-	{
-		t_msg = p_cell->uas.request;
-
-		/* is it the wanted transaction ? */
-		if ( !isACK )
-		{ /* is not an ACK request */
-			/* first only the length are checked */
-			if ( /*callied*/EQ_LEN(callid) && /*cseq*/EQ_LEN(cseq)
-			&& /*req URI*/EQ_REQ_URI_LEN && /*VIA*/EQ_VIA_LEN(via1)
-			&& /*from*/EQ_LEN(from) && /*to*/EQ_LEN(to)  )
-				/* so far the lengths are the same
-				-> let's check the contents */
-				if ( /*callid*/EQ_STR(callid) && /*cseq*/EQ_STR(cseq)
-				&& /*req URI*/EQ_REQ_URI_STR && /*VIA*/EQ_VIA_STR(via1)
-				&& /*from*/EQ_STR(from) && /*to*/EQ_STR(to) )
-					{ /* WE FOUND THE GOLDEN EGG !!!! */
-						goto found;
-					}
-		}
-		else
-		{ /* it's a ACK request*/
-			/* first only the length are checked */
-			/* use shortcut; -jiri
-			if ( t_msg->first_line.u.request.method_value==METHOD_INVITE */
-			if (t_msg->REQ_METHOD==METHOD_INVITE
-			/* && (fprintf(stderr,"------Method name OK->testing callid len...\n")) */
-			&& /*callid length*/ EQ_LEN(callid)
-			/* && (fprintf(stderr,"------CallID OK -> testing cseq nr len\n")) */
-			&& get_cseq(t_msg)->number.len==get_cseq(p_msg)->number.len
-			/* && (fprintf(stderr,"------Cseq nr OK -> testing from len\n")) */
-			&& /*from length*/ EQ_LEN(from)
-			/* && (fprintf(stderr,"------from OK -> testing To uri len\n")) */
-			&& /*to uri*/get_to(t_msg)->uri.len==get_to(p_msg)->uri.len
-			/* && (fprintf(stderr,"------To uri OK -> testing To tag len\n")) */
-			&& /*to tag*/p_cell->uas.tag->len==get_to(p_msg)->tag_value.len
-			/* && (fprintf(stderr,"------To tag OK -> testing uri len\n")) */
-
-			/* in ACKs to 200, r-uri and Via may be different than in
-			   original INVITE; we still try to match the transaction
-			   so that we can retransmit an ACK on resent 200 -- different
-			   from SIP spec which kills transaction state after INVITE-200
-			   and considers 200-ACK a new transaction which just happens
-			   to have the same CSeq. -jiri
-			*/
-
-			&& /*req URI*/(p_cell->uas.status==200 || EQ_REQ_URI_LEN )
-			/* && (fprintf(stderr,"------uri OK -> testing via len\n")) */
-			&& /*VIA*/(p_cell->uas.status==200 || EQ_VIA_LEN(via1)) )
-				/* so far the lengths are the same
-				-> let's check the contents */
-				if ( /* fprintf(stderr,"------callid |%.*s| |%.*s|\n",
-					p_msg->callid->body.len,p_msg->callid->body.s,
-					t_msg->callid->body.len,t_msg->callid->body.s)
-				&& */ /*callid*/!memcmp( t_msg->callid->body.s,
-					p_msg->callid->body.s,p_msg->callid->body.len)
-				/* && fprintf(stderr,"------cseq |%.*s| |%.*s|\n",
-					get_cseq(p_msg)->number.len,get_cseq(p_msg)->number.s,
-					get_cseq(t_msg)->number.len,get_cseq(t_msg)->number.s) */
-				&& /*cseq nr*/!memcmp(get_cseq(t_msg)->number.s,
-					get_cseq(p_msg)->number.s,get_cseq(p_msg)->number.len)
-				/* &&  fprintf(stderr,"------from |%.*s| |%.*s|\n",
-					p_msg->from->body.len, translate_pointer(p_msg->orig,
-						p_msg->buf,p_msg->from->body.s),
-					t_msg->from->body.len,t_msg->from->body.s) */
-				&& /*from*/EQ_STR(from)
-				/* && fprintf(stderr,"------to uri |%.*s| |%.*s|\n",
-					get_to(p_msg)->uri.len,get_to(p_msg)->uri.s,
-					get_to(t_msg)->uri.len,get_to(t_msg)->uri.s) */
-				&& /*to uri*/!memcmp(get_to(t_msg)->uri.s,
-					get_to(p_msg)->uri.s,get_to(t_msg)->uri.len)
-				/* && fprintf(stderr,"------to tag |%.*s| |%.*s|\n",
-                    get_to(p_msg)->tag_value.len,get_to(p_msg)->tag_value.s,
-                    p_cell->uas.tag->len, p_cell->uas.tag->s) */
-				&& /*to tag*/!memcmp(p_cell->uas.tag->s,
-					get_to(p_msg)->tag_value.s,p_cell->uas.tag->len)
-				/* && fprintf(stderr,"------URI %d |%.*s| |%.*s|\n",
-					p_cell->uas.status,p_msg->first_line.u.request.uri.len,
-					translate_pointer(p_msg->orig, p_msg->buf,
-						p_msg->first_line.u.request.uri.s),
-					t_msg->first_line.u.request.uri.len,
-					t_msg->first_line.u.request.uri.s) */
-				&& /*req URI*/(p_cell->uas.status==200 || EQ_REQ_URI_STR)
-				/* && fprintf(stderr,"------VIA %d |%.*s| |%.*s|\n",
-					p_cell->uas.status, 
-					(p_msg->via1->bsize-(p_msg->via1->name.s-
-						(p_msg->via1->hdr.s+p_msg->via1->hdr.len))),
-					translate_pointer(p_msg->orig,p_msg->buf,
-						p_msg->via1->name.s),
-                    (t_msg->via1->bsize-(t_msg->via1->name.s-
-						(t_msg->via1->hdr.s+t_msg->via1->hdr.len))),
-					t_msg->via1->name.s) */
-				&& /*VAI*/(p_cell->uas.status==200 ||EQ_VIA_STR(via1)) )
-					{ /* WE FOUND THE GOLDEN EGG !!!! */
-						goto found;
-					}
-		}
-		/* next transaction */
-		tmp_cell = p_cell;
-		p_cell = p_cell->next_cell;
-	} /* synonym loop */
-
-	/* no transaction found */
-	T = 0;
-	if (!leave_new_locked)
-		unlock(&(hash_table->entrys[p_msg->hash_index].mutex));
 	DBG("DEBUG: t_lookup_request: no transaction found\n");
-	/* DON'T FORGET TO REMOVE IT!!!!! bogdan */
-	//if (isACK) assert(0);
-	return -1;
+	return ret;
 
 found:
 	T=p_cell;
-	T_REF( T );
-	DBG("DEBUG: t_lookup_request: transaction found (T=%p , ref=%x)\n",
-		T,T->ref_bitmap);
-	unlock(&(hash_table->entrys[p_msg->hash_index].mutex));
+	REF_UNSAFE( T );
+	T->kr|=REQ_EXIST;
+	UNLOCK_HASH( p_msg->hash_index );
+	DBG("DEBUG: t_lookup_request: transaction found (T=%p)\n",T);
 	return 1;
 }
 
-#endif
-
 
 
 /* function returns:
@@ -365,99 +240,67 @@ found:
  *       T - transaction found
  */
 struct cell* t_lookupOriginalT(  struct s_table* hash_table ,
-													struct sip_msg* p_msg )
+	struct sip_msg* p_msg )
 {
 	struct cell     *p_cell;
-	struct cell     *tmp_cell;
-	unsigned int     hash_index=0;
-	struct sip_msg  *t_msg=0;
+	unsigned int     hash_index;
+	struct sip_msg  *t_msg;
 
 
-	/* start searching into the table */
+	/* start searching in the table */
 	hash_index = p_msg->hash_index;
+	LOCK_HASH(hash_index);
 	DBG("DEBUG: t_lookupOriginalT: searching on hash entry %d\n",hash_index );
 
 	/* all the transactions from the entry are compared */
-	p_cell   = hash_table->entrys[hash_index].first_cell;
-	tmp_cell = 0;
-	while( p_cell )
+	for (p_cell=hash_table->entrys[hash_index].first_cell;
+		p_cell; p_cell = p_cell->next_cell )
 	{
 		t_msg = p_cell->uas.request;
 
-		/* is it the wanted transaction ? */
-		/* first only the length are checked */
-		if ( /* fprintf(stderr,"starting\n")  && */ p_cell->uas.request->REQ_METHOD!=METHOD_CANCEL
-			/* && fprintf(stderr,"checking callid length....\n") */
-			&& /*callid length*/ EQ_LEN(callid)
-			/* && fprintf(stderr,"OK. checking cseg nr len....\n")	 */
-			&& get_cseq(t_msg)->number.len==get_cseq(p_msg)->number.len
-			/* && fprintf(stderr,"OK. checking REQ_URI len.... \n") */
-			&& EQ_REQ_URI_LEN
-			/* && fprintf(stderr,"OK. checking VIA %d %d....\n",
-				(p_msg->via1->bsize-(p_msg->via1->name.s-
-					(p_msg->via1->hdr.s+p_msg->via1->hdr.len))),
-				(t_msg->via1->bsize-(t_msg->via1->name.s-
-					(t_msg->via1->hdr.s+t_msg->via1->hdr.len)))) */
-            /* && fprintf(stderr,"OK. VIA |%.*s| |%.*s|\n",
-                (p_msg->via1->bsize-(p_msg->via1->name.s-
-                     (p_msg->via1->hdr.s+p_msg->via1->hdr.len))),
-                translate_pointer(p_msg->orig,p_msg->buf,
-                       p_msg->via1->name.s),
-                (t_msg->via1->bsize-(t_msg->via1->name.s-
-                      (t_msg->via1->hdr.s+t_msg->via1->hdr.len))),
-                t_msg->via1->name.s) */
-			&& EQ_VIA_LEN(via1) 
-			/* && fprintf(stderr,"OK. checking FROM len... \n") */
-			&& EQ_LEN(from)
-			/* && fprintf(stderr,"OK. checking TO len... \n") */
-			&& EQ_LEN(to)
-			/* && fprintf(stderr,"OK\n") */ )
-				/* so far the lengths are the same
-				 let's check the contents */
-				if (
-                	/* fprintf(stderr,"checking callid |%.*s| |%.*s|\n",
-                    	p_msg->callid->body.len, translate_pointer(p_msg->orig,
-                        	p_msg->buf,p_msg->callid->body.s),
-                    	t_msg->callid->body.len,t_msg->callid->body.s) 
-					&& *//*callid*/ EQ_STR(callid)
-					/* && fprintf(stderr,"OK. cseq nr |%.*s| |%.*s|\n",
-						get_cseq(p_msg)->number.len,get_cseq(p_msg)->number.s,
-						get_cseq(t_msg)->number.len,get_cseq(t_msg)->number.s) */
-					&& /*cseq_nr*/ !memcmp(get_cseq(t_msg)->number.s,
-						get_cseq(p_msg)->number.s,get_cseq(p_msg)->number.len)
-	                /* && fprintf(stderr,"OK. URI %d |%.*s| |%.*s|\n",
-                    	p_cell->uas.status,p_msg->first_line.u.request.uri.len,
-                    	translate_pointer(p_msg->orig, p_msg->buf,
-                        	p_msg->first_line.u.request.uri.s),
-                    	t_msg->first_line.u.request.uri.len,
-                    	t_msg->first_line.u.request.uri.s) */
-					&& EQ_REQ_URI_STR
-	                /* && fprintf(stderr,"OK. VIA |%.*s| |%.*s|\n",
-        	            (p_msg->via1->bsize-(p_msg->via1->name.s-
-            	            (p_msg->via1->hdr.s+p_msg->via1->hdr.len))),
-                	    translate_pointer(p_msg->orig,p_msg->buf,
-                    	    p_msg->via1->name.s),
-           				(t_msg->via1->bsize-(t_msg->via1->name.s-
-                    	    (t_msg->via1->hdr.s+t_msg->via1->hdr.len))),
-						t_msg->via1->name.s) */
-					&& EQ_VIA_STR(via1)
-	                /* && fprintf(stderr,"OK. from |%.*s| |%.*s|\n",
-                        p_msg->from->body.len, translate_pointer(p_msg->orig,
-                            p_msg->buf,p_msg->from->body.s),
-                        t_msg->from->body.len,t_msg->from->body.s) */
-					&& EQ_STR(from)
-					/* && fprintf(stderr,"OK\n") */ )
-					{ /* WE FOUND THE GOLDEN EGG !!!! */
-						DBG("DEBUG: t_lookupOriginalT: canceled transaction"
-							" found (%p)! \n",p_cell );
-						return p_cell;
-					}
-		/* next transaction */
-		tmp_cell = p_cell;
-		p_cell = p_cell->next_cell;
+		/* we don't cancel CANCELs ;-) */
+		if (p_cell->uas.request->REQ_METHOD==METHOD_CANCEL)
+			continue;
+
+		/* check lengths now */	
+		if (!EQ_LEN(callid))
+			continue;
+		if (get_cseq(t_msg)->number.len!=get_cseq(p_msg)->number.len)
+			continue;
+		if (!EQ_LEN(from))
+			continue;
+		if (!EQ_LEN(to))
+			continue;
+		if (!EQ_REQ_URI_LEN)
+			continue;
+		if (!EQ_VIA_LEN(via1))
+			continue;
+
+		/* check the content now */
+		if (!EQ_STR(callid))
+			continue;
+		if (memcmp(get_cseq(t_msg)->number.s,
+			get_cseq(p_msg)->number.s,get_cseq(p_msg)->number.len)!=0)
+			continue;
+		if (!EQ_STR(from))
+			continue;
+		if (!EQ_STR(to))
+			continue;
+		if (!EQ_REQ_URI_STR)
+			continue;
+		if (!EQ_VIA_STR(via1))
+			continue;
+
+		/* found */
+		REF( p_cell );
+		UNLOCK_HASH(hash_index);
+		DBG("DEBUG: t_lookupOriginalT: canceled transaction"
+			" found (%p)! \n",p_cell );
+		return p_cell;
 	}
 
 	/* no transaction found */
+	UNLOCK_HASH(hash_index);
 	DBG("DEBUG: t_lookupOriginalT: no CANCEL maching found! \n" );
 	return 0;
 }
@@ -468,8 +311,7 @@ struct cell* t_lookupOriginalT(  struct s_table* hash_table ,
 /* Returns 0 - nothing found
  *         1  - T found
  */
-int t_reply_matching( struct sip_msg *p_msg , int *p_branch ,
-											 int *local_cancel)
+int t_reply_matching( struct sip_msg *p_msg , int *p_branch )
 {
 	struct cell*  p_cell;
 	int hash_index   = 0;
@@ -478,13 +320,21 @@ int t_reply_matching( struct sip_msg *p_msg , int *p_branch ,
 	char  *hashi, *branchi, *p, *n;
 	int hashl, branchl;
 	int scan_space;
-#ifndef USE_SYNONIM
+	str cseq_method;
+	str req_method;
+
 	char *loopi;
 	int loopl;
-#else
 	char *syni;
 	int synl;
-#endif
+	
+	short is_cancel;
+
+	/* make compiler warnnings happy */
+	loopi=0;
+	loopl=0;
+	syni=0;
+	synl=0;
 
 	/* split the branch into pieces: loop_detection_check(ignored),
 	 hash_table_id, synonym_id, branch_id */
@@ -495,15 +345,6 @@ int t_reply_matching( struct sip_msg *p_msg , int *p_branch ,
 	p=p_msg->via1->branch->value.s;
 	scan_space=p_msg->via1->branch->value.len;
 
-#ifndef USE_SYNONIM
-	/* loop detection ... ignore */
-	n=eat_token2_end( p, p+scan_space, BRANCH_SEPARATOR );
-	loopl = n-p;
-	scan_space-= loopl;
-	if (n==p || scan_space<2 || *n!=BRANCH_SEPARATOR) goto nomatch2;
-	loopi=p;
-	p=n+1; scan_space--;
-#endif
 
 	/* hash_id */
 	n=eat_token2_end( p, p+scan_space, BRANCH_SEPARATOR);
@@ -513,15 +354,25 @@ int t_reply_matching( struct sip_msg *p_msg , int *p_branch ,
 	hashi=p;
 	p=n+1;scan_space--;
 
-#ifdef USE_SYNONIM
-	/* sequence id */
-	n=eat_token2_end( p, p+scan_space, BRANCH_SEPARATOR);
-	synl=n-p;
-	scan_space-=synl;
-	if (!synl || scan_space<2 || *n!=BRANCH_SEPARATOR) goto nomatch2;
-	syni=p;
-	p=n+1;scan_space--;
-#endif
+	if (!syn_branch) {
+		/* md5 value */
+		n=eat_token2_end( p, p+scan_space, BRANCH_SEPARATOR );
+		loopl = n-p;
+		scan_space-= loopl;
+		if (n==p || scan_space<2 || *n!=BRANCH_SEPARATOR) 
+			goto nomatch2;
+		loopi=p;
+		p=n+1; scan_space--;
+	} else {
+		/* synonym id */
+		n=eat_token2_end( p, p+scan_space, BRANCH_SEPARATOR);
+		synl=n-p;
+		scan_space-=synl;
+		if (!synl || scan_space<2 || *n!=BRANCH_SEPARATOR) 
+			goto nomatch2;
+		syni=p;
+		p=n+1;scan_space--;
+	}
 
 	/* branch id  -  should exceed the scan_space */
 	n=eat_token_end( p, p+scan_space );
@@ -530,13 +381,12 @@ int t_reply_matching( struct sip_msg *p_msg , int *p_branch ,
 	branchi=p;
 
 	/* sanity check */
-	if ((hash_index=reverse_hex2int(hashi, hashl))<0||hash_index>=TABLE_ENTRIES
-		|| (branch_id=reverse_hex2int(branchi, branchl))<0||branch_id>=MAX_FORK
-#ifdef USE_SYNONIM
-		|| (entry_label=reverse_hex2int(syni, synl))<0
-#else
-		|| loopl!=MD5_LEN
-#endif
+	if ((hash_index=reverse_hex2int(hashi, hashl))<0
+		||hash_index>=TABLE_ENTRIES
+		|| (branch_id=reverse_hex2int(branchi, branchl))<0
+		||branch_id>=MAX_BRANCHES
+		|| (syn_branch ? (entry_label=reverse_hex2int(syni, synl))<0 
+			: loopl!=MD5_LEN )
 	) {
 		DBG("DEBUG: t_reply_matching: poor reply lables %d label %d "
 			"branch %d\n",hash_index, entry_label, branch_id );
@@ -551,51 +401,52 @@ int t_reply_matching( struct sip_msg *p_msg , int *p_branch ,
 	/* search the hash table list at entry 'hash_index'; lock the
 	   entry first 
 	*/
-	lock(&(hash_table->entrys[hash_index].mutex));
+	cseq_method=get_cseq(p_msg)->method;
+	is_cancel=cseq_method.len==CANCEL_LEN 
+		&& memcmp(cseq_method.s, CANCEL, CANCEL_LEN)==0;
+	LOCK_HASH(hash_index);
 	for (p_cell = hash_table->entrys[hash_index].first_cell; p_cell; 
 		p_cell=p_cell->next_cell) {
 
-		/* does method match ? */
-		if (get_cseq(p_msg)->method.len==
-			  get_cseq(p_cell->uas.request)->method.len 
-			&& get_cseq(p_msg)->method.s[0]==
-			  get_cseq(p_cell->uas.request)->method.s[0]) {
-				*local_cancel=0;
-		/* or is it perhaps a CANCEL ? */
-		} else if ( p_cell->uas.request->REQ_METHOD==METHOD_INVITE 
-			&& get_cseq(p_msg)->method.len==CANCEL_LEN 
-			&& memcmp( get_cseq(p_msg)->method.s, CANCEL, CANCEL_LEN )==0 
-			&& p_cell->uac[branch_id].request.cancel!=NO_CANCEL 
-			&& p_cell->uac[branch_id].request.cancel!=EXTERNAL_CANCEL ) {
-				*local_cancel=1;
-		} else { /* method mismatched */
-			continue;
-		};
-		#ifdef USE_SYNONIM
-		if (p_cell->label != entry_label) 
-			continue;
-		#else
-		if ( p_cell->uas.request->add_to_branch_len<MD5_LEN 
-			 || memcmp(p_cell->uas.request->add_to_branch_s,loopi,MD5_LEN)!=0)
+		/* first look if branch matches */
+
+		if (syn_branch) {
+			if (p_cell->label != entry_label) 
 				continue;
-		#endif
+		} else {
+			if ( memcmp(p_cell->md5, loopi,MD5_LEN)!=0)
+					continue;
+		}
+
 		/* sanity check ... too high branch ? */
 		if ( branch_id>=p_cell->nr_of_outgoings )
 			continue;
+
+		/* does method match ? (remember -- CANCELs have the same branch
+		   as cancelled transactions) */
+		req_method=p_cell->method;
+		if ( /* method match */
+			! ((cseq_method.len==req_method.len 
+			&& memcmp( cseq_method.s, req_method.s, cseq_method.len )==0)
+			/* or it is a local cancel */
+			|| (is_cancel && p_cell->is_invite 
+				&& p_cell->uac[branch_id].local_cancel.buffer )))
+			continue;
+
+
 		/* we passed all disqualifying factors .... the transaction has been
 		   matched !
 		*/
 		T=p_cell;
 		*p_branch = branch_id;
-		T_REF( T );
-		unlock(&(hash_table->entrys[hash_index].mutex));
-		DBG("DEBUG: t_reply_matching: reply matched (T=%p,ref=%x)!\n",
-			T,T->ref_bitmap);
+		REF_UNSAFE( T );
+		UNLOCK_HASH(hash_index);
+		DBG("DEBUG: t_reply_matching: reply matched (T=%p)!\n",T);
 		return 1;
 	} /* for cycle */
 
 	/* nothing found */
-	unlock(&(hash_table->entrys[hash_index].mutex));
+	UNLOCK_HASH(hash_index);
 	DBG("DEBUG: t_reply_matching: no matching transaction exists\n");
 
 nomatch2:
@@ -612,10 +463,9 @@ nomatch2:
   * for current message exists;
   * it returns 1 if found, 0 if not found, -1 on error
   */
-int t_check( struct sip_msg* p_msg , int *param_branch, int *param_cancel)
+int t_check( struct sip_msg* p_msg , int *param_branch )
 {
 	int local_branch;
-	int local_cancel;
 
 	/* is T still up-to-date ? */
 	DBG("DEBUG: t_check: msg id=%d global id=%d T start=%p\n", 
@@ -631,12 +481,29 @@ int t_check( struct sip_msg* p_msg , int *param_branch, int *param_cancel)
 				return -1;
 			t_lookup_request( p_msg , 0 /* unlock before returning */ );
 		} else {
-			if ( parse_headers(p_msg, HDR_VIA1|HDR_TO|HDR_CSEQ, 0 )==-1
-			|| !p_msg->via1 || !p_msg->to || !p_msg->cseq )
+			/* we need Via for branch and Cseq method to distinguish
+			   replies with the same branch/cseqNr (CANCEL)
+			*/
+			if ( parse_headers(p_msg, HDR_VIA1|HDR_CSEQ, 0 )==-1
+			|| !p_msg->via1 || !p_msg->cseq ) {
+				LOG(L_ERR, "ERROR: reply cannot be parsed\n");
 				return -1;
+			}
+
+			/* if that is an INVITE, we will also need to-tag
+			   for later ACK matching
+			*/
+            if ( get_cseq(p_msg)->method.len==INVITE_LEN 
+				&& memcmp( get_cseq(p_msg)->method.s, INVITE, INVITE_LEN )==0 ) {
+					if (parse_headers(p_msg, HDR_TO, 0)==-1
+						|| !p_msg->to)  {
+						LOG(L_ERR, "ERROR: INVITE reply cannot be parsed\n");
+						return -1;
+					}
+			}
+
 			t_reply_matching( p_msg ,
-				((param_branch!=0)?(param_branch):(&local_branch)),
-				((param_cancel!=0)?(param_cancel):(&local_cancel)));
+				param_branch!=0?param_branch:&local_branch );
 
 		}
 #ifdef EXTRA_DEBUG
@@ -658,59 +525,39 @@ int t_check( struct sip_msg* p_msg , int *param_branch, int *param_cancel)
 	return ((T)?1:0) ;
 }
 
-
-
-/* append appropriate branch labels for fast reply-transaction matching
-   to outgoing requests
-*/
-int add_branch_label( struct cell *trans, struct sip_msg *p_msg, int branch )
+int init_rb( struct retr_buf *rb, struct sip_msg *msg )
 {
-	char *begin;
-	int size, orig_size;
-
-	/* this is actually a hack made by Bogdan; I wanted to have a structure
-	   to which anybody can append some branch stuff which may be utilizied
-	   during reply processing; Bogdan ignored that and resets it all the
-	   time to construct multiple branches for multiple via's during
-	   forking (otherwise, the next branch would be now appended to
-	   previous branch)
-
-	   keywords: HACK
-	*/
-     	
-	p_msg->add_to_branch_len = 0; /*bogdan*/
-
-
-	begin=p_msg->add_to_branch_s+p_msg->add_to_branch_len;
-	orig_size = size=MAX_BRANCH_PARAM_LEN - p_msg->add_to_branch_len;
-
-#ifndef USE_SYNONIM
-	if (memcpy(begin,trans->md5,MD5_LEN)) {begin+=MD5_LEN;size-=MD5_LEN;} else return -1;
-	if (size) { *begin=BRANCH_SEPARATOR; begin++; size--; } else return -1;
-#endif
-	if (int2reverse_hex( &begin, &size, trans->hash_index)==-1) return -1;
-#ifdef USE_SYNONIM
-	if (size) { *begin=BRANCH_SEPARATOR; begin++; size--; } else return -1;
-	if (int2reverse_hex( &begin, &size, trans->label)==-1) return -1;
-#endif
-	if (size) { *begin=BRANCH_SEPARATOR; begin++; size--; } else return -1;
-	if (int2reverse_hex( &begin, &size, branch)==-1) return -1;
-
-	p_msg->add_to_branch_len+=(orig_size-size);
-	DBG("DEBUG: XXX branch label created now: %.*s\n",
-		p_msg->add_to_branch_len, p_msg->add_to_branch_s );
-	return 0;
+	struct socket_info* send_sock;
+	struct via_body* via;
 
+	if (!reply_to_via) {
+		update_sock_struct_from_ip( &rb->to, msg );
+	} else {
+		via=msg->via1;
+		/*init retrans buffer*/
+		if (update_sock_struct_from_via( &(rb->to),via )==-1) {
+			LOG(L_ERR, "ERROR: init_rb: cannot lookup reply dst: %.*s\n",
+				via->host.len, via->host.s );
+			ser_error=E_BAD_VIA;
+			return 0;
+		}
+	}
+	send_sock=get_send_socket(&rb->to);
+	if (send_sock==0) {
+		LOG(L_ERR, "ERROR: init_rb: cannot fwd to af %d "
+			"no socket\n", rb->to.s.sa_family);
+		ser_error=E_BAD_VIA;
+		return 0;
+	}
+	rb->send_sock=send_sock;
+    return 1;
 }
 
 
+
 /* atomic "new_tran" construct; it returns:
 
-	-1	if	a request matched a transaction
-		- if that was an ack, the calling function
-		  shall reset timers
-		- otherwise the calling function shall 
-		  attempt to retransmit
+	<0	on error
 
 	+1	if a request did not match a transaction
 		- it that was an ack, the calling function
@@ -719,71 +566,143 @@ int add_branch_label( struct cell *trans, struct sip_msg *p_msg, int branch )
 		  introduced and the calling function
 		  shall reply/relay/whatever_appropriate
 
-	0 on error
+	0 on retransmission
 */
 int t_newtran( struct sip_msg* p_msg )
 {
 
 	int ret, lret;
 	struct cell *new_cell;
+	struct sip_msg *shm_msg;
+	int a,b,c;
+
+	ret=1;
 
 	/* is T still up-to-date ? */
 	DBG("DEBUG: t_addifnew: msg id=%d , global msg id=%d ,"
 		" T on entrance=%p\n",p_msg->id,global_msg_id,T);
 
-	if ( !(p_msg->id != global_msg_id || T==T_UNDEFINED 
-		/* if someone tried to do something previously by mistake with
-		   a transaction which did not exist yet, try to look-up
-		   the transacion too */
-		|| T==T_NULL)) 
-	{
-		LOG(L_ERR, "ERROR: t_newtran: alreaddy processing this message"
-			", T %s found\n", T ? "" : "not" );
-		return 0;
+	if ( T && T!=T_UNDEFINED  ) {
+		LOG(L_ERR, "ERROR: t_newtran: "
+			"transaction already in process %p\n", T );
+		return E_SCRIPT;
 	}
 
 	global_msg_id = p_msg->id;
 	T = T_UNDEFINED;
-	/* transaction lookup */
-	/* force parsing all the needed headers*/
-	if (parse_headers(p_msg, HDR_EOH, 0 )==-1)
-		return 0;
-	lret = t_lookup_request( p_msg, 1 /* leave locked */ );
+	/* first of all, parse everything -- we will store
+	   in shared memory and need to have all headers
+	   ready for generating potential replies later;
+	   parsing later on demand is not an option since
+	   the request will be in shmem and applying 
+	   parse_headers to it would intermix shmem with
+	   pkg_mem
+	*/
+	
+	if (parse_headers(p_msg, HDR_EOH, 0 )) {
+		LOG(L_ERR, "ERROR: t_newtran: parse_headers failed\n");
+		return E_BAD_REQ;
+	}
+	if ((p_msg->parsed_flag & HDR_EOH)!=HDR_EOH) {
+			LOG(L_ERR, "ERROR: t_newtran: EoH not parsed\n");
+			return E_OUT_OF_MEM;
+	}
+	/* t_lookup_requests attmpts to find the transaction; 
+	   it also calls check_transaction_quadruple -> it is
+	   safe to assume we have from/callid/cseq/to
+	*/ 
+	lret = t_lookup_request( p_msg, 1 /* leave locked if not found */ );
 	/* on error, pass the error in the stack ... */
-	if (lret==0) return 0;
+	if (lret==0) return E_BAD_TUPEL;
 	if (lret<0) {
+		new_cell=0;
 		/* transaction not found, it's a new request;
 		   establish a new transaction (unless it is an ACK) */
-		ret=1;
 		if ( p_msg->REQ_METHOD!=METHOD_ACK ) {
 			/* add new transaction */
 			new_cell = build_cell( p_msg ) ;
 			if  ( !new_cell ){
 				LOG(L_ERR, "ERROR: t_addifnew: out of mem:\n");
-				ret = 0;
+				ret = E_OUT_OF_MEM;
 			} else {
 				insert_into_hash_table_unsafe( hash_table , new_cell );
 				T=new_cell;
-				T_REF(T);
+				INIT_REF_UNSAFE(T);
+				/* init pointers to headers needed to construct local
+				   requests such as CANCEL/ACK
+				*/
+
+				shm_msg=new_cell->uas.request;
+				new_cell->from.s=shm_msg->from->name.s;
+				new_cell->from.len=HF_LEN(shm_msg->from);
+				new_cell->to.s=shm_msg->to->name.s;
+				new_cell->to.len=HF_LEN(shm_msg->to);
+				new_cell->callid.s=shm_msg->callid->name.s;
+				new_cell->callid.len=HF_LEN(shm_msg->callid);
+				new_cell->cseq_n.s=shm_msg->cseq->name.s;
+				new_cell->cseq_n.len=get_cseq(shm_msg)->number.s
+					+get_cseq(shm_msg)->number.len
+					-shm_msg->cseq->name.s;
+
+				new_cell->method=new_cell->uas.request->first_line.u.request.method;
+				new_cell->is_invite=p_msg->REQ_METHOD==METHOD_INVITE;
 			}
+
 		}
 
 		/* was it an e2e ACK ? if so, trigger a callback */
 		if (lret==-2) {
-				T_REF(t_ack);
-				unlock(&(hash_table->entrys[p_msg->hash_index].mutex));
-				callback_event( TMCB_E2EACK, t_ack, p_msg );
-				T_UNREF(t_ack);
+				REF_UNSAFE(t_ack);
+				UNLOCK_HASH(p_msg->hash_index);
+				callback_event( TMCB_E2EACK, t_ack, p_msg, p_msg->REQ_METHOD );
+				UNREF(t_ack);
 		} else { /* not e2e ACK */
-			unlock(&(hash_table->entrys[p_msg->hash_index].mutex));
+			UNLOCK_HASH(p_msg->hash_index);
+			/* now, when the transaction state exists, check if
+		 	   there is a meaningful Via and calculate it; better
+		 	   do it now than later: state is established so that
+		 	   subsequent retransmissions will be absorbed and will
+	 		   not possibly block during Via DNS resolution; doing
+			   it later would only burn more CPU as if there is an
+			   error, we cannot relay later whatever comes out of the
+  			   the transaction 
+			*/
+			if (new_cell && p_msg->REQ_METHOD!=METHOD_ACK) {
+				if (!init_rb( &T->uas.response, p_msg)) {
+					LOG(L_ERR, "ERROR: t_newtran: unresolveable via1\n");
+					put_on_wait( T );
+					t_unref(p_msg);
+					ret=E_BAD_VIA;
+				}
+			}
 		}
 
 		return ret;
 	} else {
-		/* transaction found, it's a retransmission  or ACK */
-			return -1;
+		/* transaction found, it's a retransmission  or hbh ACK */
+		if (p_msg->REQ_METHOD==METHOD_ACK) {
+			t_release_transaction(T);
+		} else {
+			t_retransmit_reply(T);
+		}
+		/* things are done -- return from script */
+		return 0;
 	}
+
 }
 
 
+int t_unref( struct sip_msg* p_msg  )
+{
+	if (T==T_UNDEFINED || T==T_NULL)
+		return -1;
+	if (T->kr==0 
+		||(p_msg->REQ_METHOD==METHOD_ACK && !(T->kr & REQ_RLSD))) {
+		LOG(L_WARN, "WARNING: script writer didn't release transaction\n");
+		t_release_transaction(T);
+	}
+	UNREF( T );
+	T=T_UNDEFINED;
+	return 1;
+}
 

+ 47 - 0
modules/tm/t_lookup.h

@@ -0,0 +1,47 @@
+/*
+ * $Id$
+ */
+
+
+#ifndef _T_LOOKUP_H
+#define _T_LOOKUP_H
+
+#include "config.h"
+#include "t_funcs.h"
+
+#define T_UNDEFINED  ( (struct cell*) -1 )
+#define T_NULL       ( (struct cell*) 0 )
+
+#ifdef _OBSOLETED
+extern struct cell      *T;
+#endif
+
+extern unsigned int     global_msg_id;
+
+void init_t();
+int init_rb( struct retr_buf *rb, struct sip_msg *msg );
+struct cell* t_lookupOriginalT(  struct s_table* hash_table,
+	struct sip_msg* p_msg );
+int t_reply_matching( struct sip_msg* , int* );
+int t_lookup_request( struct sip_msg* p_msg , int leave_new_locked );
+int t_newtran( struct sip_msg* p_msg );
+
+int _add_branch_label( struct cell *trans,
+    char *str, int *len, int branch );
+int add_branch_label( struct cell *trans, 
+	struct sip_msg *p_msg, int branch );
+
+/* releases T-context */
+int t_unref( struct sip_msg *p_msg);
+
+/* function returns:
+ *      -1 - transaction wasn't found
+ *       1 - transaction found
+ */
+int t_check( struct sip_msg* , int *branch );
+
+struct cell *get_t();
+
+
+#endif
+

+ 201 - 212
modules/tm/t_msgbuilder.c

@@ -4,13 +4,16 @@
  * message printing
  */
 
-#include "hash_func.h"
+#include "../../hash_func.h"
+#include "../../globals.h"
 #include "t_funcs.h"
 #include "../../dprint.h"
 #include "../../config.h"
 #include "../../parser/parser_f.h"
 #include "../../ut.h"
 #include "../../parser/msg_parser.h"
+#include "t_msgbuilder.h"
+#include "uac.h"
 
 
 
@@ -19,270 +22,256 @@
 			memcpy((_d),(_s),(_len));\
 			(_d) += (_len);\
 		}while(0);
+#define append_str( _p, _str ) \
+		do { \
+			memcpy((_p), (_str).s, (_str).len); \
+			(_p)+=(_str).len; \
+		} while(0);
 
 
-/* Builds a CANCEL request based on an INVITE request. CANCEL is send
- * to same address as the INVITE */
-int t_build_and_send_CANCEL(struct cell *Trans,unsigned int branch)
+/* Build a local request based on a previous request; main
+   customers of this function are local ACK and local CANCEL
+ */
+char *build_local(struct cell *Trans,unsigned int branch,
+	unsigned int *len, char *method, int method_len, str *to)
 {
-	struct sip_msg      *p_msg;
-	struct hdr_field    *hdr;
 	char                *cancel_buf, *p, *via;
-	unsigned int         len, via_len;
-
-	if ( !Trans->uac[branch].rpl_received )
-	{
-		DBG("DEBUG: t_build_and_send_CANCEL: no response ever received"
-			" : dropping local cancel! \n");
-		return 1;
-	}
+	unsigned int         via_len;
+	struct hdr_field    *hdr;
+	char branch_buf[MAX_BRANCH_PARAM_LEN];
+	int branch_len;
 
-	if (Trans->uac[branch].request.cancel!=NO_CANCEL)
+	if ( Trans->uac[branch].last_received<100)
 	{
-		DBG("DEBUG: t_build_and_send_CANCEL: branch (%d)was already canceled"
-			" : dropping local cancel! \n",branch);
-		return 1;
+		DBG("DEBUG: build_local: no response ever received"
+			" : dropping local request! \n");
+		goto error;
 	}
 
-	cancel_buf = 0;
-	via = 0;
-	p_msg = Trans->uas.request;
-
-	/* method, separators, version */
-	len=SIP_VERSION_LEN + CANCEL_LEN + 2 /* spaces */ + CRLF_LEN;
-	/* if URL was overridden .... */
-	if (Trans->uac[branch].uri.s)
-		len+=Trans->uac[branch].uri.len;
-	else
-	/* ... otherwise use the inbound URL */
-		len+=REQ_LINE(p_msg).uri.len;
+	/* method, separators, version: "CANCEL sip:[email protected] SIP/2.0" */
+	*len=SIP_VERSION_LEN + method_len + 2 /* spaces */ + CRLF_LEN;
+	*len+=Trans->uac[branch].uri.len;
 
 	/*via*/
-	if ( add_branch_label(Trans,p_msg,branch)==-1 )
+	if (!t_calc_branch(Trans,  branch, 
+		branch_buf, &branch_len ))
 		goto error;
-	via = via_builder(p_msg , &via_len, Trans->uac[branch].request.send_sock );
+	via=via_builder(&via_len, Trans->uac[branch].request.send_sock,
+		branch_buf, branch_len );
 	if (!via)
 	{
 		LOG(L_ERR, "ERROR: t_build_and_send_CANCEL: "
 			"no via header got from builder\n");
 		goto error;
 	}
-	len+= via_len;
+	*len+= via_len;
 	/*headers*/
-	for ( hdr=p_msg->headers ; hdr ; hdr=hdr->next ) {
-		if (hdr->type==HDR_FROM || hdr->type==HDR_CALLID 
-			|| hdr->type==HDR_TO )
-			len += ((hdr->body.s+hdr->body.len ) - hdr->name.s ) + CRLF_LEN ;
-		else if (hdr->type==HDR_CSEQ)
-			len += hdr->name.len + 2 + ((struct cseq_body*)hdr->parsed)->number.len +
-				1+CANCEL_LEN+CRLF_LEN;
+	*len+=Trans->from.len+CRLF_LEN
+		+Trans->callid.len+CRLF_LEN
+		+to->len+CRLF_LEN
+		/* CSeq: 101 CANCEL */
+		+Trans->cseq_n.len+1+method_len+CRLF_LEN; 
+
+	/* copy'n'paste Route headers */
+	if (!Trans->local) {
+		for ( hdr=Trans->uas.request->headers ; hdr ; hdr=hdr->next )
+			 if (hdr->type==HDR_ROUTE)
+				len+=((hdr->body.s+hdr->body.len ) - hdr->name.s ) + 
+					CRLF_LEN ;
+	}
+
+	/* User Agent */
+	if (server_signature) {
+		*len += USER_AGENT_LEN + CRLF_LEN;
 	}
-	/* User Agent, Conteny Length, EoM */
-	len += USER_AGENT_LEN + CRLF_LEN +
-		CONTENT_LEN_LEN + CRLF_LEN +
-		CRLF_LEN;
+	/* Content Length, EoM */
+	*len+=CONTENT_LEN_LEN + CRLF_LEN + CRLF_LEN;
 
-	cancel_buf=sh_malloc( len+1 );
+	cancel_buf=shm_malloc( *len+1 );
 	if (!cancel_buf)
 	{
 		LOG(L_ERR, "ERROR: t_build_and_send_CANCEL: cannot allocate memory\n");
-		goto error;
+		goto error01;
 	}
 	p = cancel_buf;
 
-	append_mem_block( p, CANCEL " ", CANCEL_LEN +1 );
-	if (Trans->uac[branch].uri.s) {
-		append_mem_block( p, Trans->uac[branch].uri.s, 
-			Trans->uac[branch].uri.len);
-	} else {
-		append_mem_block(p,REQ_LINE(p_msg).uri.s,
-			REQ_LINE(p_msg).uri.len );
-	}
+	append_mem_block( p, method, method_len );
+	append_mem_block( p, " ", 1 );
+	append_str( p, Trans->uac[branch].uri );
 	append_mem_block( p, " " SIP_VERSION CRLF, 1+SIP_VERSION_LEN+CRLF_LEN );
 
 	/* insert our via */
 	append_mem_block(p,via,via_len);
 
 	/*other headers*/
-	for ( hdr=p_msg->headers ; hdr ; hdr=hdr->next )
-	{
-		if(hdr->type==HDR_FROM||hdr->type==HDR_CALLID||hdr->type==HDR_TO)
-		{
-			append_mem_block(p,hdr->name.s,
-				((hdr->body.s+hdr->body.len)-hdr->name.s) );
-			append_mem_block(p, CRLF, CRLF_LEN );
-		} else if ( hdr->type==HDR_CSEQ )
-		{
-			append_mem_block(p,hdr->name.s, hdr->name.len );
-			append_mem_block(p,": ", 2 );
-			append_mem_block(p, ((struct cseq_body*)hdr->parsed)->number.s,
-				((struct cseq_body*)hdr->parsed)->number.len );
-			append_mem_block(p, " " CANCEL CRLF, 1+CANCEL_LEN+CRLF_LEN);
-		}
+	append_str( p, Trans->from );
+	append_mem_block( p, CRLF, CRLF_LEN );
+	append_str( p, Trans->callid );
+	append_mem_block( p, CRLF, CRLF_LEN );
+	append_str( p, *to );
+	append_mem_block( p, CRLF, CRLF_LEN );
+	append_str( p, Trans->cseq_n );
+	append_mem_block( p, " ", 1 );
+	append_mem_block( p, method, method_len );
+	append_mem_block( p, CRLF, CRLF_LEN );
+
+	if (!Trans->local)  {
+		for ( hdr=Trans->uas.request->headers ; hdr ; hdr=hdr->next )
+			if(hdr->type==HDR_ROUTE) {
+				append_mem_block(p, hdr->name.s,
+					hdr->body.s+hdr->body.len-hdr->name.s );
+				append_mem_block(p, CRLF, CRLF_LEN );
+			}
 	}
 
-	/* User Agent header, Content Length, EoM */
-	append_mem_block(p,USER_AGENT CRLF CONTENT_LEN CRLF CRLF ,
-		USER_AGENT_LEN + CRLF_LEN + CONTENT_LEN_LEN + CRLF_LEN + CRLF_LEN);
-	*p=0;
-
-	if (Trans->uac[branch].request.cancel) {
-		shm_free( cancel_buf );
-		LOG(L_WARN, "send_cancel: Warning: CANCEL already sent out\n");
-		goto error;
+	/* User Agent header */
+	if (server_signature) {
+		append_mem_block(p,USER_AGENT CRLF, USER_AGENT_LEN+CRLF_LEN );
 	}
-
-	Trans->uac[branch].request.activ_type = TYPE_LOCAL_CANCEL;
-	Trans->uac[branch].request.cancel = cancel_buf;
-	Trans->uac[branch].request.cancel_len = len;
-
-	/*sets and starts the FINAL RESPONSE timer */
-	set_timer(hash_table,&(Trans->uac[branch].request.fr_timer),FR_TIMER_LIST);
-	/* sets and starts the RETRANS timer */
-	Trans->uac[branch].request.retr_list = RT_T1_TO_1;
-	set_timer(hash_table,&(Trans->uac[branch].request.retr_timer),RT_T1_TO_1);
-	DBG("DEBUG: T_build_and_send_CANCEL : sending cancel...\n");
-	SEND_CANCEL_BUFFER( &(Trans->uac[branch].request) );
+	/* Content Length, EoM */
+	append_mem_block(p, CONTENT_LEN CRLF CRLF ,
+		CONTENT_LEN_LEN + CRLF_LEN + CRLF_LEN);
+	*p=0;
 
 	pkg_free(via);
-	return 1;
+	return cancel_buf;
+error01:
+	pkg_free(via);
 error:
-	if (via) pkg_free(via);
-	return -1;
+	return NULL;
 }
 
 
-/* Builds an ACK request based on an INVITE request. ACK is send
- * to same address */
-char *build_ack(struct sip_msg* rpl,struct cell *trans,int branch,int *ret_len)
-{
-	struct sip_msg      *p_msg , *r_msg;
-	struct hdr_field    *hdr;
-	char                *ack_buf, *p, *via;
-	unsigned int         len, via_len;
-
-	ack_buf = 0;
-	via =0;
-	p_msg = trans->uas.request;
-	r_msg = rpl;
 
-	if ( parse_headers(rpl,HDR_TO, 0)==-1 || !rpl->to )
-	{
-		LOG(L_ERR, "ERROR: t_build_ACK: "
-			"cannot generate a HBH ACK if key HFs in reply missing\n");
+char *build_uac_request(  str msg_type, str dst,
+    	str headers, str body, int branch, 
+		struct cell *t, int *len)
+{
+	char *via;
+	int via_len;
+	char content_len[10];
+	int content_len_len;
+	char *buf;
+	char *w;
+	int dummy;
+
+	char branch_buf[MAX_BRANCH_PARAM_LEN];
+	int branch_len;
+
+	static int from_len=0;
+
+	buf=0;
+	if (from_len==0) from_len=strlen(uac_from);
+	
+	*len=SIP_VERSION_LEN+msg_type.len+2/*spaces*/+CRLF_LEN+
+		dst.len;
+
+	if (!t_calc_branch(t, branch, branch_buf, &branch_len )) {
+		LOG(L_ERR, "ERROR: build_uac_request: branch calculation failed\n");
 		goto error;
 	}
-
-	len = USER_AGENT_LEN + CRLF_LEN;
-	/*first line's len */
-	len += 4/*reply code and one space*/+
-		p_msg->first_line.u.request.version.len+CRLF_LEN;
-	/*uri's len*/
-	if (trans->uac[branch].uri.s)
-		len += trans->uac[branch].uri.len +1;
-	else
-		len += p_msg->first_line.u.request.uri.len +1;
-	/*adding branch param*/
-	if ( add_branch_label( trans , trans->uas.request , branch)==-1 )
-		goto error;
-	/*via*/
-	via = via_builder(p_msg , &via_len, trans->uac[branch].request.send_sock );
-	if (!via)
-	{
-		LOG(L_ERR, "ERROR: t_build_ACK: "
-			"no via header got from builder\n");
+	via=via_builder(&via_len, t->uac[branch].request.send_sock,
+		branch_buf, branch_len );
+	
+	if (!via) {
+		LOG(L_ERR, "ERROR: build_uac_request: via building failed\n");
 		goto error;
 	}
-	len+= via_len;
-	/*headers*/
-	for ( hdr=p_msg->headers ; hdr ; hdr=hdr->next )
-		if (hdr->type==HDR_FROM||hdr->type==HDR_CALLID||hdr->type==HDR_CSEQ)
-			len += ((hdr->body.s+hdr->body.len ) - hdr->name.s ) + CRLF_LEN ;
-		else if ( hdr->type==HDR_TO )
-			len += ((r_msg->to->body.s+r_msg->to->body.len ) -
-				r_msg->to->name.s ) + CRLF_LEN ;
-	/* CSEQ method : from INVITE-> ACK */
-	len -= 3  ;
-	/* end of message */
-	len += CRLF_LEN; /*new line*/
-
-	ack_buf = sh_malloc(len+1);
-	if (!ack_buf)
-	{
-		LOG(L_ERR, "ERROR: t_build_and_ACK: cannot allocate memory\n");
+	*len+=via_len;
+	/* content length */
+	content_len_len=snprintf(
+		content_len, sizeof(content_len), "%d", body.len );
+	/* header names and separators */
+	*len+=
+		+CSEQ_LEN+CRLF_LEN
+		+TO_LEN+CRLF_LEN
+		+CALLID_LEN+CRLF_LEN
+		+CONTENT_LENGTH_LEN+CRLF_LEN
+		+ (server_signature ? USER_AGENT_LEN + CRLF_LEN : 0 )
+		+FROM_LEN+CRLF_LEN
+		+CRLF_LEN; /* EoM */
+	/* header field value and body length */
+	*len+= msg_type.len+1+UAC_CSEQNR_LEN /* CSeq: method, delimitor, number  */
+		+ dst.len /* To */
+		+ RAND_DIGITS+1+MAX_PID_LEN+1+MAX_SEQ_LEN /* call-id */
+		+ from_len+FROMTAG_LEN+MD5_LEN+
+		+ content_len_len
+		+ headers.len
+		+ body.len;
+	
+	buf=shm_malloc( *len+1 );
+	if (!buf) {
+		LOG(L_ERR, "ERROR: t_uac: no shmem\n");
 		goto error1;
 	}
-	p = ack_buf;
-
-	/* first line */
-	memcpy( p , "ACK " , 4);
-	p += 4;
-	/* uri */
-	if ( trans->uac[branch].uri.s )
-	{
-		memcpy(p,trans->uac[branch].uri.s,trans->uac[branch].uri.len);
-		p +=trans->uac[branch].uri.len;
-	}else{
-		memcpy(p,p_msg->orig+(p_msg->first_line.u.request.uri.s-p_msg->buf),
-			p_msg->first_line.u.request.uri.len );
-		p += p_msg->first_line.u.request.uri.len;
+	w=buf;
+	memapp( w, msg_type.s, msg_type.len ); 
+	memapp( w, " ", 1); 
+	t->uac[branch].uri.s=w; t->uac[branch].uri.len=dst.len;
+	memapp( w, dst.s, dst.len ); 
+	memapp( w, " " SIP_VERSION CRLF, 1+SIP_VERSION_LEN+CRLF_LEN );
+	memapp( w, via, via_len );
+	t->cseq_n.s=w; t->cseq_n.len=CSEQ_LEN+UAC_CSEQNR_LEN;
+	memapp( w, CSEQ UAC_CSEQNR " ", CSEQ_LEN + UAC_CSEQNR_LEN+ 1 );
+	memapp( w, msg_type.s, msg_type.len );
+	t->to.s=w+CRLF_LEN; t->to.len=TO_LEN+dst.len;
+	memapp( w, CRLF TO, CRLF_LEN + TO_LEN  );
+	memapp( w, dst.s, dst.len );
+	t->callid.s=w+CRLF_LEN; t->callid.len=CALLID_LEN+RAND_DIGITS+1+
+		MAX_PID_LEN+1+MAX_SEQ_LEN;
+	memapp( w, CRLF CALLID, CRLF_LEN + CALLID_LEN  );
+	memapp( w, call_id, RAND_DIGITS+1+MAX_PID_LEN+1+MAX_SEQ_LEN );
+	memapp( w, CRLF CONTENT_LEN, CRLF_LEN + CONTENT_LEN_LEN);
+	memapp( w, content_len, content_len_len );
+	if (server_signature) {
+		memapp( w, CRLF USER_AGENT CRLF FROM, 
+			CRLF_LEN+USER_AGENT_LEN+CRLF_LEN+FROM_LEN);
+	} else {
+		memapp( w, CRLF  FROM, 
+			CRLF_LEN+FROM_LEN);
 	}
-	/* SIP version */
-	*(p++) = ' ';
-	memcpy(p,p_msg->orig+(p_msg->first_line.u.request.version.s-p_msg->buf),
-		p_msg->first_line.u.request.version.len );
-	p += p_msg->first_line.u.request.version.len;
-	memcpy( p, CRLF, CRLF_LEN );
-	p+=CRLF_LEN;
-
-	/* insert our via */
-	memcpy( p , via , via_len );
-	p += via_len;
-
-	/*other headers*/
-	for ( hdr=p_msg->headers ; hdr ; hdr=hdr->next )
-	{
-		if ( hdr->type==HDR_FROM || hdr->type==HDR_CALLID  )
-		{
-			memcpy( p , p_msg->orig+(hdr->name.s-p_msg->buf) ,
-				((hdr->body.s+hdr->body.len ) - hdr->name.s ) );
-			p += ((hdr->body.s+hdr->body.len ) - hdr->name.s );
-			memcpy( p, CRLF, CRLF_LEN );
-			p+=CRLF_LEN;
-		}
-		else if ( hdr->type==HDR_TO )
-		{
-			memcpy( p , r_msg->orig+(r_msg->to->name.s-r_msg->buf) ,
-				((r_msg->to->body.s+r_msg->to->body.len)-r_msg->to->name.s));
-			p+=((r_msg->to->body.s+r_msg->to->body.len)-r_msg->to->name.s);
-			memcpy( p, CRLF, CRLF_LEN );
-			p+=CRLF_LEN;
-		}
-		else if ( hdr->type==HDR_CSEQ )
-		{
-			memcpy( p , p_msg->orig+(hdr->name.s-p_msg->buf) ,
-				((((struct cseq_body*)hdr->parsed)->method.s)-hdr->name.s));
-			p+=((((struct cseq_body*)hdr->parsed)->method.s)-hdr->name.s);
-			memcpy( p , "ACK" CRLF, 3+CRLF_LEN );
-			p += 3+CRLF_LEN;
-		}
+	t->from.s=w-FROM_LEN; t->from.len=FROM_LEN+from_len+FROMTAG_LEN+MD5_LEN;
+	memapp( w, uac_from, from_len );
+	memapp( w, FROMTAG, FROMTAG_LEN );
+	memapp( w, from_tag, MD5_LEN );
+	memapp( w, CRLF, CRLF_LEN );
+
+	memapp( w, headers.s, headers.len );
+	/* EoH */
+	memapp( w, CRLF, CRLF_LEN );
+	if ( body.s ) {
+		memapp( w, body.s, body.len );
 	}
-
-	/* end of message */
-	memcpy( p , USER_AGENT CRLF CRLF , USER_AGENT_LEN + CRLF_LEN + CRLF_LEN );
-	p +=  USER_AGENT_LEN + CRLF_LEN + CRLF_LEN;
-
-	pkg_free( via );
-	DBG("DEBUG: t_build_ACK: ACK generated\n");
-
-	*(ret_len) = p-ack_buf;
-	return ack_buf;
-
+	/* ugly HACK -- debugging has shown len shorter by one */
+	dummy=*len+1;
+	*len=dummy;
+#	ifdef EXTRA_DEBUG
+	if (w-buf != *len ) abort();
+#	endif
+	
+	
 error1:
-	pkg_free(via );
+	pkg_free(via);	
 error:
-	return 0;
+	return buf;
+	
 }
 
 
+int t_calc_branch(struct cell *t, 
+	int b, char *branch, int *branch_len)
+{
+	return syn_branch ?
+		branch_builder( t->hash_index,
+			t->label, 0,
+			b, branch, branch_len )
+		: branch_builder( t->hash_index,
+			0, t->md5,
+			b, branch, branch_len );
+}
 
+int t_setbranch( struct cell *t, struct sip_msg *msg, int b )
+{
+	return t_calc_branch( t, b, 
+		msg->add_to_branch_s, &msg->add_to_branch_len );
+}

+ 58 - 0
modules/tm/t_msgbuilder.h

@@ -0,0 +1,58 @@
+/*
+ * $Id$
+ *
+ */
+
+#ifndef _MSGBUILDER_H
+#define _MSGBUILDER_H
+
+#define CSEQ "CSeq: "
+#define CSEQ_LEN 6
+#define TO "To: "
+#define TO_LEN 4
+#define CALLID "Call-ID: "
+#define CALLID_LEN 9
+#define CONTENT_LENGTH "Content-Length: "
+#define CONTENT_LENGTH_LEN 16
+#define FROM "From: "
+#define FROM_LEN 6
+#define FROMTAG ";tag="
+#define FROMTAG_LEN 5
+
+#define UAC_CSEQNR "1"
+#define UAC_CSEQNR_LEN 1
+
+#define UAC_CSEQNR "1"
+#define UAC_CSEQNR_LEN 1
+
+/* convenience macros */
+#define memapp(_d,_s,_len) \
+	do{\
+		memcpy((_d),(_s),(_len));\
+		(_d) += (_len);\
+	}while(0);
+
+#define  append_mem_block(_d,_s,_len) \
+	do{\
+		memcpy((_d),(_s),(_len));\
+		(_d) += (_len);\
+	}while(0);
+#define append_str( _p, _str ) \
+	do { \
+		memcpy((_p), (_str).s, (_str).len); \
+		(_p)+=(_str).len); \
+	} while(0);
+
+char *build_local(struct cell *Trans, unsigned int branch,
+	unsigned int *len, char *method, int method_len, str *to);
+
+char *build_uac_request(  str msg_type, str dst,
+	str headers, str body, int branch,
+	struct cell *t, int *len);
+
+int t_calc_branch(struct cell *t,
+	int b, char *branch, int *branch_len);
+int t_setbranch( struct cell *t, struct sip_msg *msg, int b );
+
+
+#endif

+ 690 - 401
modules/tm/t_reply.c

@@ -4,204 +4,479 @@
  */
 
 
-#include "hash_func.h"
+#include "../../hash_func.h"
 #include "t_funcs.h"
 #include "../../dprint.h"
 #include "../../config.h"
 #include "../../parser/parser_f.h"
 #include "../../ut.h"
 #include "../../timer.h"
+#include "../../error.h"
+#include "../../action.h"
+#include "../../dset.h"
 
 #include "t_hooks.h"
+#include "t_funcs.h"
+#include "t_reply.h"
+#include "t_cancel.h"
+#include "t_msgbuilder.h"
+#include "t_lookup.h"
+#include "t_fwd.h"
+#include "fix_lumps.h"
+
+/* where to go if there is no positive reply */
+static int goto_on_negative=0;
+
+/* we store the reply_route # in private memory which is
+   then processed during t_relay; we cannot set this value
+   before t_relay creates transaction context or after
+   t_relay when a reply may arrive after we set this
+   value; that's why we do it how we do it, i.e.,
+   *inside*  t_relay using hints stored in private memory
+   before t_reay is called
+*/
+  
+  
+int t_on_negative( unsigned int go_to )
+{
+	goto_on_negative=go_to;
+	return 1;
+}
 
 
-
-
-inline int check_for_no_response( struct cell *Trans ,int code, int relay)
+unsigned int get_on_negative()
 {
-	if ( code/100>=3 && Trans->uac[Trans->nr_of_outgoings].uri.s )
-	{
-		forward_serial_branch( Trans , Trans->nr_of_outgoings );
-		return -1;
-	}
-	return relay;
+	return goto_on_negative;
 }
 
 
+/* the main code of stateful replying */
+static int _reply( struct cell *t, struct sip_msg* p_msg, unsigned int code,
+    char * text, int lock );
 
 /* Retransmits the last sent inbound reply.
  * input: p_msg==request for which I want to retransmit an associated reply
  * Returns  -1 - error
  *           1 - OK
  */
-int t_retransmit_reply( /* struct sip_msg* p_msg    */ )
+int t_retransmit_reply( struct cell *t )
 {
 	static char b[BUF_SIZE];
 	int len;
 
-	if (!T->uas.response.buffer)
+	/* first check if we managed to resolve topmost Via -- if
+	   not yet, don't try to retransmit
+	*/
+	if (!t->uas.response.send_sock) {
+		LOG(L_ERR, "ERROR: no resolved dst to retransmit\n");
 		return -1;
+	}
+
+	/* we need to lock the transaction as messages from
+	   upstream may change it continuously
+	*/
+	LOCK_REPLIES( t );
+
+	if (!t->uas.response.buffer) {
+		DBG("DBG: t_retransmit_reply: nothing to retransmit\n");
+		goto error;
+	}
 
-	if ( (len=T->uas.response.buffer_len)==0 || len>BUF_SIZE ) {
-		UNLOCK_REPLIES( T );
-		return -2;
+	len=t->uas.response.buffer_len;
+	if ( len==0 || len>BUF_SIZE )  {
+		DBG("DBG: t_retransmit_reply: "
+			"zero length or too big to retransmit: %d\n", len);
+		goto error;
 	}
-	memcpy( b, T->uas.response.buffer, len );
-	UNLOCK_REPLIES( T );
-	SEND_PR_BUFFER( & T->uas.response, b, len );
+	memcpy( b, t->uas.response.buffer, len );
+	UNLOCK_REPLIES( t );
+	SEND_PR_BUFFER( & t->uas.response, b, len );
+	DBG("DEBUG: reply retransmitted. buf=%p: %.9s..., shmem=%p: %.9s\n", 
+		b, b, t->uas.response.buffer, t->uas.response.buffer );
 	return 1;
+
+error:
+	UNLOCK_REPLIES(t);
+	return -1;
 }
 
 
 
+int t_reply( struct cell *t, struct sip_msg* p_msg, unsigned int code, 
+	char * text )
+{
+	return _reply( t, p_msg, code, text, 1 /* lock replies */ );
+}
+
+int t_reply_unsafe( struct cell *t, struct sip_msg* p_msg, unsigned int code, 
+	char * text )
+{
+	return _reply( t, p_msg, code, text, 0 /* don't lock replies */ );
+}
+
 
 
-/* Force a new response into inbound response buffer.
-  * returns 1 if everything was OK or -1 for error
-  */
-int t_send_reply( struct sip_msg* p_msg, unsigned int code, 
-	char * text, unsigned int branch)
+/* send a UAS reply
+ * returns 1 if everything was OK or -1 for error
+ */
+static int _reply( struct cell *trans, struct sip_msg* p_msg, 
+	unsigned int code, char * text, int lock )
 {
 	unsigned int len, buf_len=0;
 	char * buf;
 	struct retr_buf *rb;
-	int relay, save_clone;
-	struct socket_info* send_sock;
 
-	buf = build_res_buf_from_sip_req(code,text,T->uas.tag->s,
-		T->uas.tag->len, T->uas.request,&len);
-	DBG("DEBUG: t_send_reply: buffer computed\n");
+	branch_bm_t cancel_bitmap;
+
+	if (code>=200) trans->kr|=REQ_RPLD;
+	/*
+	buf = build_res_buf_from_sip_req(code,text,trans->uas.tag->s,
+		trans->uas.tag->len, trans->uas.request,&len);
+	*/
+	cancel_bitmap=0;
+	/* compute the buffer in private memory prior to entering lock */
+	buf = build_res_buf_from_sip_req(code,text, 0,0, /* no to-tag */
+		p_msg,&len);
+	DBG("DEBUG: t_reply: buffer computed\n");
 	if (!buf)
 	{
-		DBG("DEBUG: t_send_reply: response building failed\n");
+		DBG("DEBUG: t_reply: response building failed\n");
+		/* determine if there are some branches to be cancelled */
+		if (trans->is_invite) {
+			if (lock) LOCK_REPLIES( trans );
+			which_cancel(trans, &cancel_bitmap );
+			if (lock) UNLOCK_REPLIES( trans );
+		}
+		/* and clean-up, including cancellations, if needed */
 		goto error;
 	}
 
-	LOCK_REPLIES( T );
-	relay = t_should_relay_response(T, code, branch, &save_clone);
-
-	if (save_clone)
-	{
-		T->uac[branch].status = code;
+	if (lock) LOCK_REPLIES( trans );
+	if (trans->is_invite) which_cancel(trans, &cancel_bitmap );
+	if (trans->uas.status>=200) {
+		LOG( L_ERR, "ERROR: t_reply: can't generate replies"
+			"when a final was sent out\n");
+		goto error2;
 	}
-
-	rb = & T->uas.response;
-	if (relay >=0 && (relay=check_for_no_response(T,code,relay))>=0 )
-	{
-		if (!rb->buffer) {
-			/* initialize retransmission structure */
-			if (update_sock_struct_from_via(  &(rb->to),  p_msg->via1 )==-1)
-			{
-				UNLOCK_REPLIES( T );
-				LOG(L_ERR,"ERROR: t_send_reply: cannot lookup reply dst: %s\n",
-					p_msg->via1->host.s );
-				goto error2;
-			}
-			send_sock=get_send_socket(&rb->to);
-			if (send_sock==0) {
-				LOG(L_ERR, "ERROR: t_send_reply: cannot fwd to af %d "
-					"no socket\n", rb->to.s.sa_family);
-				ser_error=E_NO_SOCKET;
-				goto error2;
-			}
-			rb->send_sock=send_sock;
-			/* rb->to.sin_family = AF_INET; */
-			rb->activ_type = code;
-			buf_len = len + REPLY_OVERBUFFER_LEN;
-		}else{
-			buf_len = len;
-		}
-		/* puts the reply's buffer to uas.response */
-		if (! (rb->buffer = (char*)shm_resize( rb->buffer, buf_len )))
-		{
-			UNLOCK_REPLIES( T );
-			LOG(L_ERR, "ERROR: t_send_reply: cannot allocate shmem buffer\n");
+	rb = & trans->uas.response;
+	rb->activ_type=code;
+
+	buf_len = rb->buffer ? len : len + REPLY_OVERBUFFER_LEN;
+	rb->buffer = (char*)shm_resize( rb->buffer, buf_len );
+	/* puts the reply's buffer to uas.response */
+	if (! rb->buffer ) {
+			LOG(L_ERR, "ERROR: t_reply: cannot allocate shmem buffer\n");
 			goto error2;
-		}
-		rb->buffer_len = len ;
-		memcpy( rb->buffer , buf , len );
-		T->uas.status = code;
-		/* needs to be protected too because what timers are set depends
-		   on current transactions status */
-		t_update_timers_after_sending_reply( rb );
-	} /* if realy */
-
-	UNLOCK_REPLIES( T );
+	}
+	rb->buffer_len = len ;
+	memcpy( rb->buffer , buf , len );
+	trans->uas.status = code;
+	/* needs to be protected too because what timers are set depends
+	   on current transactions status */
+	/* t_update_timers_after_sending_reply( rb ); */
+
+	if (lock) UNLOCK_REPLIES( trans );
+	
+	/* do UAC cleanup procedures in case we generated
+	   a final answer whereas there are pending UACs */
+	if (code>=200) {
+		cleanup_uac_timers( trans );
+		if (trans->is_invite) cancel_uacs( trans, cancel_bitmap );
+		set_final_timer( /* hash_table, */ trans );
+	}
 
-	if (relay>=0) SEND_PR_BUFFER( rb, buf, len );
+	/* send it out */
+	/* first check if we managed to resolve topmost Via -- if
+	   not yet, don't try to retransmit
+	*/
+	if (!trans->uas.response.send_sock) {
+		LOG(L_ERR, "ERROR: _reply: no resolved dst to send reply to\n");
+	} else {
+		SEND_PR_BUFFER( rb, buf, len );
+		DBG("DEBUG: reply sent out. buf=%p: %.9s..., shmem=%p: %.9s\n", 
+			buf, buf, rb->buffer, rb->buffer );
+	}
 	pkg_free( buf ) ;
-	DBG("DEBUG: t_send_reply: finished\n");
+	DBG("DEBUG: t_reply: finished\n");
 	return 1;
 
 error2:
+	if (lock) UNLOCK_REPLIES( trans );
 	pkg_free ( buf );
 error:
+	/* do UAC cleanup */
+	cleanup_uac_timers( trans );
+	if (trans->is_invite) cancel_uacs( trans, cancel_bitmap );
+	/* we did not succeed -- put the transaction on wait */
+	put_on_wait(trans);
 	return -1;
 }
 
+void set_final_timer( /* struct s_table *h_table, */ struct cell *t )
+{
+	if ( !t->local 
+		&& t->uas.request->REQ_METHOD==METHOD_INVITE 
+		&& t->uas.status>=300  ) {
+			/* crank timers for negative replies */
+			start_retr( &t->uas.response );
+	} else put_on_wait(t);
+}
+
+void cleanup_uac_timers( struct cell *t )
+{
+	int i;
 
+	/* reset FR/retransmission timers */
+	for (i=0; i<t->nr_of_outgoings; i++ )  {
+		reset_timer( hash_table, &t->uac[i].request.retr_timer );
+		reset_timer( hash_table, &t->uac[i].request.fr_timer );
+	}
+	DBG("DEBUG: cleanup_uacs: RETR/FR timers reset\n");
+}
 
-#if 0
-/* Push a previously stored reply from UA Client to UA Server
- * and send it out */
-static int push_reply( struct cell* trans , unsigned int branch ,
-												char *buf, unsigned int len)
+int store_reply( struct cell *trans, int branch, struct sip_msg *rpl)
 {
-	unsigned int buf_len;
-	struct retrans_buff *rb;
-
-	DBG("DEBUG: push_reply_from_uac_to_uas: start\n");
-	rb= & trans->outbound_response;
-	/* if there is a reply, release the buffer (everything else stays same) */
-	if ( ! rb->retr_buffer ) {
-		/*init retrans buffer*/
-		memset( rb , 0 , sizeof (struct retrans_buff) );
-		if (update_sock_struct_from_via(  &(rb->to),
-			trans->inbound_response[branch]->via2 )==-1) {
-				LOG(L_ERR, "ERROR: push_reply_from_uac_to_uas: "
-					"cannot lookup reply dst: %s\n",
-				trans->inbound_response[branch]->via2->host.s );
-				goto error;
+#		ifdef EXTRA_DEBUG
+		if (trans->uac[branch].reply) {
+			LOG(L_ERR, "ERROR: replacing stored reply; aborting\n");
+			abort();
 		}
-		rb->retr_timer.tg=TG_RT;
-		rb->fr_timer.tg=TG_FR;
-		rb->retr_timer.payload = rb;
-		rb->fr_timer.payload =  rb;
-		rb->to.sin_family = AF_INET;
-		rb->my_T = trans;
-		rb->status = trans->inbound_response[branch]->REPLY_STATUS;
-	};
-
-	/* if this is a first reply (?100), longer replies will probably follow;
-	try avoiding shm_resize by higher buffer size */
-	buf_len = rb->retr_buffer ? len : len + REPLY_OVERBUFFER_LEN;
-	if (! (rb->retr_buffer = (char*)shm_resize( rb->retr_buffer, buf_len )))
+#		endif
+
+		/* when we later do things such as challenge aggregation,
+	   	   we should parse the message here before we conservate
+		   it in shared memory; -jiri
+		*/
+		if (rpl==FAKED_REPLY)
+			trans->uac[branch].reply=FAKED_REPLY;
+		else
+			trans->uac[branch].reply = sip_msg_cloner( rpl );
+
+		if (! trans->uac[branch].reply ) {
+			LOG(L_ERR, "ERROR: store_reply: can't alloc' clone memory\n");
+			return 0;
+		}
+
+		return 1;
+}
+
+/* this is the code which decides what and when shall be relayed
+   upstream; note well -- it assumes it is entered locked with 
+   REPLY_LOCK and it returns unlocked!
+*/
+enum rps relay_reply( struct cell *t, struct sip_msg *p_msg, int branch, 
+	unsigned int msg_status, branch_bm_t *cancel_bitmap )
+{
+	int relay;
+	int save_clone;
+	char *buf;
+	/* length of outbound reply */
+	unsigned int res_len;
+	int relayed_code;
+	struct sip_msg *relayed_msg;
+	str	to_tag;
+	enum rps reply_status;
+	/* retransmission structure of outbound reply and request */
+	struct retr_buf *uas_rb;
+
+	/* keep compiler warnings about use of uninit vars silent */
+	res_len=0;
+	buf=0;
+	relayed_msg=0;
+	relayed_code=0;
+
+
+	/* remember, what was sent upstream to know whether we are
+	   forwarding a first final reply or not */
+
+	/* *** store and relay message as needed *** */
+	reply_status = t_should_relay_response(t, msg_status, branch, 
+		&save_clone, &relay, cancel_bitmap );
+	DBG("DEBUG: relay_reply: branch=%d, save=%d, relay=%d\n",
+		branch, save_clone, relay );
+
+	/* store the message if needed */
+	if (save_clone) /* save for later use, typically branch picking */
 	{
-		LOG(L_ERR, "ERROR: t_push: cannot allocate shmem buffer\n");
-		goto error1;
+		if (!store_reply( t, branch, p_msg ))
+			goto error01;
 	}
-	rb->bufflen = len ;
-	memcpy( rb->retr_buffer , buf , len );
 
-	/* update the status*/
-	trans->status = trans->inbound_response[branch]->REPLY_STATUS;
-	if ( trans->inbound_response[branch]->REPLY_STATUS>=200 &&
-		trans->relaied_reply_branch==-1 ) {
+	uas_rb = & t->uas.response;
+	if (relay >= 0 ) {
+
+		/* initialize sockets for outbound reply */
+		uas_rb->activ_type=msg_status;
+		/* only messages known to be relayed immediately will be
+		   be called on; we do not evoke this callback on messages
+		   stored in shmem -- they are fixed and one cannot change them
+		   anyway 
+        */
+		if (msg_status<300 && branch==relay) {
+			callback_event( TMCB_REPLY_IN, t, p_msg, msg_status );
+		}
+		/* try bulding the outbound reply from either the current
+	       or a stored message */
+		relayed_msg = branch==relay ? p_msg :  t->uac[relay].reply;
+		if (relayed_msg ==FAKED_REPLY) {
+			relayed_code = branch==relay
+				? msg_status : t->uac[relay].last_received;
+			buf = build_res_buf_from_sip_req( relayed_code,
+				error_text(relayed_code), 0,0, /* no to-tag */
+				t->uas.request, &res_len );
+		} else {
+			relayed_code=relayed_msg->REPLY_STATUS;
+			buf = build_res_buf_from_sip_res( relayed_msg, &res_len );
+			/* if we build a message from shmem, we need to remove
+			   via delete lumps which are now stirred in the shmem-ed
+			   structure
+			*/
+			if (branch!=relay) {
+				free_via_lump(&relayed_msg->repl_add_rm);
+			}
+		}
+		if (!buf) {
+			LOG(L_ERR, "ERROR: relay_reply: "
+				"no mem for outbound reply buffer\n");
+			goto error02;
+		}
+
+		/* attempt to copy the message to UAS's shmem:
+		   - copy to-tag for ACK matching as well
+		   -  allocate little a bit more for provisionals as
+		      larger messages are likely to follow and we will be 
+		      able to reuse the memory frag 
+		*/
+		uas_rb->buffer = (char*)shm_resize( uas_rb->buffer, res_len +
+			(msg_status<200 ?  REPLY_OVERBUFFER_LEN : 0));
+		if (!uas_rb->buffer) {
+			LOG(L_ERR, "ERROR: relay_reply: cannot alloc reply shmem\n");
+			goto error03;
+		}
+		uas_rb->buffer_len = res_len;
+		memcpy( uas_rb->buffer, buf, res_len );
+		/* to tag now */
+		if (relayed_code>=300 && t->is_invite) {
+			if (relayed_msg!=FAKED_REPLY) {
+				to_tag=get_to(relayed_msg)->tag_value;
+				t->uas.to_tag.s=(char *)shm_resize( t->uas.to_tag.s,
+					to_tag.len );
+				if (!t->uas.to_tag.s) {
+					LOG(L_ERR, "ERROR: no shmem for to-tag\n");
+					goto error04;
+				}
+				t->uas.to_tag.len=to_tag.len;
+				memcpy(t->uas.to_tag.s, to_tag.s, to_tag.len );
+			} else {
+				if (t->uas.to_tag.s) shm_free(t->uas.to_tag.s);
+				t->uas.to_tag.s=0;
+				t->uas.to_tag.len=0;
+			}
+		}
+
+		/* update the status ... */
+		t->uas.status = relayed_code;
+		t->relaied_reply_branch = relay;
+	}; /* if relay ... */
+
+	UNLOCK_REPLIES( t );
 
-		memcpy( & trans->ack_to, & trans->outbound_request[ branch ]->to,
-			sizeof( struct sockaddr_in ) );
-		trans->relaied_reply_branch = branch;
+	/* send it now (from the private buffer) */
+	if (relay >= 0) {
+		SEND_PR_BUFFER( uas_rb, buf, res_len );
+		DBG("DEBUG: reply relayed. buf=%p: %.9s..., shmem=%p: %.9s\n", 
+			buf, buf, uas_rb->buffer, uas_rb->buffer );
+		callback_event( TMCB_REPLY, t, relayed_msg, relayed_code );
+		pkg_free( buf );
 	}
 
-	/*send the reply*/
-	SEND_BUFFER( rb );
-	return 1;
+	/* success */
+	return reply_status;
+
+error04:
+	shm_free( uas_rb->buffer );
+	uas_rb->buffer=0;
+error03:
+	pkg_free( buf );
+error02:
+	if (save_clone) {
+		if (t->uac[branch].reply!=FAKED_REPLY)
+			sip_msg_free( t->uac[branch].reply );
+		t->uac[branch].reply = NULL;	
+	}
+error01:
+	UNLOCK_REPLIES(t);
+	if (t->is_invite) cancel_uacs( t, *cancel_bitmap );
+	/* a serious error occured -- attempt to send an error reply;
+	   it will take care of clean-ups 
+	*/
+	t_reply( t, t->uas.request, 500, "Reply processing error" );
+
+	/* failure */
+	return RPS_ERROR;
+}
+
+/* this is the "UAC" above transaction layer; if a final reply
+   is received, it triggers a callback; note well -- it assumes
+   it is entered locked with REPLY_LOCK and it returns unlocked!
+*/
+enum rps local_reply( struct cell *t, struct sip_msg *p_msg, int branch, 
+	unsigned int msg_status, branch_bm_t *cancel_bitmap)
+{
+	/* how to deal with replies for local transaction */
+	int local_store, local_winner;
+	enum rps reply_status;
+	struct sip_msg *winning_msg;
+	int winning_code;
+	/* branch_bm_t cancel_bitmap; */
+
+	/* keep warning 'var might be used un-inited' silent */	
+	winning_msg=0;
+	winning_code=0;
+
+	*cancel_bitmap=0;
+
+	reply_status=t_should_relay_response( t, msg_status, branch,
+		&local_store, &local_winner, cancel_bitmap );
+	DBG("DEBUG: local_reply: branch=%d, save=%d, winner=%d\n",
+		branch, local_store, local_winner );
+	if (local_store) {
+		if (!store_reply(t, branch, p_msg))
+			goto error;
+	}
+	if (local_winner>=0) {
+		winning_msg= branch==local_winner 
+			? p_msg :  t->uac[local_winner].reply;
+		if (winning_msg==FAKED_REPLY) {
+			winning_code = branch==local_winner
+				? msg_status : t->uac[local_winner].last_received;
+		} else {
+			winning_code=winning_msg->REPLY_STATUS;
+		}
+		t->uas.status = winning_code;
+	}
+	UNLOCK_REPLIES(t);
+	if (local_winner>=0 && winning_code>=200 ) {
+		DBG("DEBUG: local transaction completed\n");
+		callback_event( TMCB_LOCAL_COMPLETED, t, winning_msg, winning_code );
+		if (t->completion_cb) 
+			t->completion_cb( t, winning_msg, winning_code, 0 /* empty param */);
+	}
+	return reply_status;
 
-error1:
 error:
-	return -1;
+	which_cancel(t, cancel_bitmap);
+	UNLOCK_REPLIES(t);
+	cleanup_uac_timers(t);
+	if ( get_cseq(p_msg)->method.len==INVITE_LEN 
+		&& memcmp( get_cseq(p_msg)->method.s, INVITE, INVITE_LEN)==0)
+		cancel_uacs( t, *cancel_bitmap );
+	put_on_wait(t);
+	return RPS_ERROR;
 }
-#endif
+
+
+
 
 
 /*  This function is called whenever a reply for our module is received; 
@@ -211,262 +486,130 @@ error:
   */
 int t_on_reply( struct sip_msg  *p_msg )
 {
-	int branch, msg_status, msg_class, save_clone;
-	int local_cancel;
-	int relay;
-	int start_fr = 0;
-	int is_invite;
-	/* retransmission structure of outbound reply and request */
-	struct retr_buf *rb=0;
-	char *buf=0, *ack=0;
-	/* length of outbound reply */
-	unsigned int res_len, ack_len;
-	/* buffer length (might be somewhat larger than message size */
-	unsigned int alloc_len;
-	str *str_foo;
-	struct socket_info* send_sock;
 
+	int msg_status;
+	char *ack;
+	unsigned int ack_len;
+	int branch;
+	/* has the transaction completed now and we need to clean-up? */
+	int reply_status;
+	branch_bm_t cancel_bitmap;
+	struct ua_client *uac;
+	struct cell *t;
 
-	/* make sure we know the assosociated tranaction ... */
-	if (t_check( p_msg  , &branch , &local_cancel)==-1)
-		return 1;
-	/* ... if there is no such, tell the core router to forward statelessly */
-	if ( T<=0 ) return 1;
 
-	DBG("DEBUG: t_on_reply: org. status uas=%d, uac[%d]=%d loca_cancel=%d)\n",
-		T->uas.status, branch, T->uac[branch].status, local_cancel);
+	/* make sure we know the assosociated transaction ... */
+	if (t_check( p_msg  , &branch )==-1)
+		return 1;
+	/*... if there is none, tell the core router to fwd statelessly */
+	t=get_t();
+	if ( t<=0 ) return 1;
 
-	/* special cases (local cancel reply) -bogdan */
-	if (local_cancel==1)
-	{
-		reset_timer( hash_table, &(T->uac[branch].request.retr_timer));
-		if ( p_msg->REPLY_STATUS>=200 )
-			reset_timer(hash_table,&(T->uac[branch].request.fr_timer));
-		goto error;
-	}
+	cancel_bitmap=0;
+	msg_status=p_msg->REPLY_STATUS;
 
-	/* do we have via2 ? - maybe we'll need it for forwarding -bogdan*/
-	if ((p_msg->via2==0) || (p_msg->via2->error!=PARSE_OK)){
-		/* no second via => error */
-		LOG(L_ERR, "ERROR: t_on_reply: no 2nd via found in reply\n");
-		goto error;
+	uac=&t->uac[branch];
+	DBG("DEBUG: t_on_reply: org. status uas=%d, uac[%d]=%d local=%d)\n",
+		t->uas.status, branch, uac->last_received, t->local);
+
+	/* it's a cancel ... ? */
+	if (get_cseq(p_msg)->method.len==CANCEL_LEN 
+		&& memcmp( get_cseq(p_msg)->method.s, CANCEL, CANCEL_LEN)==0
+		/* .. which is not e2e ? ... */
+		&& t->is_invite ) {
+			/* ... then just stop timers */
+			reset_timer( hash_table, &uac->local_cancel.retr_timer);
+			if ( msg_status >= 200 )
+				reset_timer( hash_table, &uac->local_cancel.fr_timer);
+			DBG("DEBUG: reply to local CANCEL processed\n");
+			goto done;
 	}
 
-	msg_status=p_msg->REPLY_STATUS;
-	msg_class=REPLY_CLASS(p_msg);
-	is_invite= T->uas.request->REQ_METHOD==METHOD_INVITE;
-
-#ifdef _DONT_DO_IT_MAN
-/*  generate the retrans buffer, make a simplified
-	assumption everything but 100 will be fwd-ed;
-	sometimes it will result in useless CPU cycles
-	but mostly the assumption holds and allows the
-	work to be done out of criticial lock region */
-	if (msg_status==100 && T->uac[branch].status)
-		buf=0;
-	else {
-		/* buf maybe allo'ed*/
-		buf = build_res_buf_from_sip_res ( p_msg, &res_len);
-		if (!buf) {
-			LOG(L_ERR, "ERROR: t_on_reply_received: "
-			"no mem for outbound reply buffer\n");
-			goto error;
-		}
-	}
-#endif
 
 	/* *** stop timers *** */
 	/* stop retransmission */
-	reset_timer( hash_table, &(T->uac[branch].request.retr_timer));
+	reset_timer( hash_table, &uac->request.retr_timer);
 	/* stop final response timer only if I got a final response */
-	if ( msg_class>1 )
-		reset_timer( hash_table, &(T->uac[branch].request.fr_timer));
-
-	LOCK_REPLIES( T );
-	/* if a got the first prov. response for an INVITE ->
-	   change FR_TIME_OUT to INV_FR_TIME_UT */
-	start_fr = !T->uac[branch].rpl_received && msg_class==1 && is_invite;
-
-	/* *** store and relay message as needed *** */
-	relay = t_should_relay_response( T , msg_status, branch, &save_clone );
-	DBG("DEBUG: t_on_reply: branch=%d, save=%d, relay=%d\n",
-		branch, save_clone, relay );
-
-	if (save_clone)
-	{
-		str_foo = &(T->uac[branch].tag);
-		str_foo->s = shm_resize(str_foo->s, (str_foo?0:TAG_OVERBUFFER_LEN) +
-			get_to(p_msg)->tag_value.len);
-		if (!str_foo->s)
-		{
-			LOG( L_ERR , "ERROR: t_on_reply: connot alocate memory!\n");
-			goto error1;
-		}
-		/* when forking, replies greater then 300 are saved */
-		if ((T->nr_of_outgoings>1 || T->uac[T->nr_of_outgoings].uri.s)
-			&& msg_status>=300 )
-		{
-			DBG("DEBUG: t_on_reply: saving reply! \n");
-			str_foo = &(T->uac[branch].rpl_buffer);
-			str_foo->s = shm_resize(str_foo->s, res_len+
-				(str_foo->s?0:REPLY_OVERBUFFER_LEN) );
-			if (!str_foo->s)
-			{
-				LOG( L_ERR , "ERROR: t_on_reply: connot alocate memory!\n");
-				goto error1;
-			}
-			memcpy(str_foo->s,buf,res_len);
-			str_foo->len = res_len;
-		}
-		/*copy the TO tag from reply*/
-		T->uac[branch].tag.len = get_to(p_msg)->tag_value.len;
-		memcpy( T->uac[branch].tag.s, get_to(p_msg)->tag_value.s,
-			T->uac[branch].tag.len );
-		T->uac[branch].rpl_received = 1;
-		T->uac[branch].status = msg_status;
+	if ( msg_status >= 200 )
+		reset_timer( hash_table, &uac->request.fr_timer);
+
+	LOCK_REPLIES( t );
+	if (t->local) {
+		reply_status=local_reply( t, p_msg, branch, msg_status, &cancel_bitmap );
+	} else {
+		reply_status=relay_reply( t, p_msg, branch, msg_status, 
+			&cancel_bitmap );
 	}
 
-	rb = & T->uas.response;
-	if (relay >= 0  && (relay=check_for_no_response(T,msg_status,relay))>=0 ) {
+	if (reply_status==RPS_ERROR)
+		goto done;
 
-		buf = build_res_buf_from_sip_res ( p_msg, &res_len);
-		if (!buf) {
-			UNLOCK_REPLIES( T );
-			start_fr = 1;
-			LOG(L_ERR, "ERROR: t_on_reply_received: "
-				"no mem for outbound reply buffer\n");
-			goto error1;
+	/* acknowledge negative INVITE replies */	
+	if (t->is_invite && (msg_status>=300 || (t->local && msg_status>=200))) {
+		ack = build_ack( p_msg, t, branch , &ack_len);
+		if (ack) {
+			SEND_PR_BUFFER( &uac->request, ack, ack_len );
+			shm_free(ack);
 		}
-		callback_event( TMCB_REPLY_IN, T, p_msg );
-
-		if (relay!=branch)
-		{
-			str_foo = &(T->uac[relay].rpl_buffer);
-			if (buf) pkg_free(buf);
-			buf = (char*)pkg_malloc(str_foo->len);
-			if (!buf)
-			{
-				UNLOCK_REPLIES( T );
-				start_fr = 1;
-				LOG(L_ERR, "ERROR: t_on_reply: cannot alloc pkg mem\n");
-				goto error1;
-			}
-			memcpy( buf , str_foo->s , str_foo->len );
-			res_len = str_foo->len;
-		}
-		/* if there is no reply yet, initialize the structure */
-		if ( ! rb->buffer ) {
-			/*init retrans buffer*/
-			if (update_sock_struct_from_via( &(rb->to),p_msg->via2 )==-1) {
-				UNLOCK_REPLIES( T );
-				start_fr = 1;
-				LOG(L_ERR, "ERROR: t_on_reply: cannot lookup reply dst: %s\n",
-					p_msg->via2->host.s );
-				goto error1;
-			}
-			send_sock=get_send_socket(&rb->to);
-			if (send_sock==0) {
-				UNLOCK_REPLIES( T );
-				LOG(L_ERR, "ERROR: t_on_reply: cannot fwd to af %d "
-					"no socket\n", rb->to.s.sa_family);
-				start_fr=1;
-				goto error1;
-			}
-			/* rb->to.sin_family = AF_INET; */
-			rb->send_sock=send_sock;
-			rb->activ_type = p_msg->REPLY_STATUS;
-			/* allocate something more for the first message;
-			   subsequent messages will be longer and buffer
-			   reusing will save us a malloc lock */
-			alloc_len = res_len + REPLY_OVERBUFFER_LEN ;
-		}else{
-			alloc_len = res_len;
-		}
-		/* puts the reply's buffer to uas.response */
-		if (! (rb->buffer = (char*)shm_resize( rb->buffer, alloc_len ))) {
-			UNLOCK_REPLIES( T );
-			start_fr = 1;
-			LOG(L_ERR, "ERROR: t_on_reply: cannot alloc shmem\n");
-			goto error1;
-		}
-		rb->buffer_len = res_len;
-		memcpy( rb->buffer, buf, res_len );
-		/* update the status ... */
-		T->uas.status = p_msg->REPLY_STATUS;
-		T->uas.tag=&(T->uac[relay].tag);
-		if (T->uas.status >=200 && T->relaied_reply_branch==-1 )
-				T->relaied_reply_branch = relay;
-	}; /* if relay ... */
-
-	UNLOCK_REPLIES( T );
-
-	if (relay >= 0) {
-		SEND_PR_BUFFER( rb, buf, res_len );
-		t_update_timers_after_sending_reply( rb );
-		callback_event( TMCB_REPLY, T, p_msg );
-	}
-
-	/* *** ACK handling *** */
-	if ( is_invite ) {
-		if ( T->uac[branch].request.ack_len )
-		{   /*retransmit*/
-			/* I don't need any additional syncing here -- after ack
-			   is introduced it's never changed */
-			DBG("DEBUG: localy cached ACK retranmitted\n");
-			SEND_ACK_BUFFER( &(T->uac[branch].request) );
-		} else if (msg_class>2 ) {
-			/*on a non-200 reply to INVITE*/
-			DBG("DEBUG: t_on_reply_received: >=3xx reply to INVITE:"
-				"send ACK\n");
-			ack = build_ack( p_msg, T, branch , &ack_len);
-			if (ack) {
-				SEND_PR_BUFFER( &(T->uac[branch].request), ack, ack_len );
-				/* append to transaction structure */
-				attach_ack( T, branch, ack , ack_len );
-			} else {
-				/* restart FR */
-				start_fr=1;
-				DBG("ERROR: t_on_reply: build_ack failed\n");
-			}
+	} /* ack-ing negative INVITE replies */
+
+	/* clean-up the transaction when transaction completed */
+	if (reply_status==RPS_COMPLETED) {
+		/* no more UAC FR/RETR (if I received a 2xx, there may
+		   be still pending branches ...
+		*/
+		cleanup_uac_timers( t );	
+		if (t->is_invite) cancel_uacs( t, cancel_bitmap );
+		/* FR for negative INVITES, WAIT anything else */
+		set_final_timer( /* hash_table,*/ t );
+	} 
+
+	/* update FR/RETR timers on provisional replies */
+	if (msg_status<200) { /* provisional now */
+		if (t->is_invite) { 
+			/* invite: change FR to longer FR_INV, do not
+			   attempt to restart retransmission any more
+			*/
+			set_timer( hash_table, & uac->request.fr_timer,
+				FR_INV_TIMER_LIST );
+		} else {
+			/* non-invite: restart retransmisssions (slow now) */
+			uac->request.retr_list=RT_T2;
+			set_timer( hash_table, 
+				& uac->request.retr_timer, RT_T2 );
 		}
-	} /* is_invite */
+	} /* provisional replies */
 
-	/* restart retransmission if a provisional response came for
-	   a non_INVITE -> retrasmit at RT_T2*/
-	if ( msg_class==1 && !is_invite )
-	{
-		rb->retr_list = RT_T2;
-		set_timer( hash_table, &(rb->retr_timer), RT_T2 );
-	}
-error1:
-	if (start_fr)
-		set_timer( hash_table, &(rb->fr_timer), FR_INV_TIMER_LIST );
-	if (buf) pkg_free( buf );
-error:
-	T_UNREF( T );
-	/* don't try to relay statelessly on error; on troubles, simply do nothing;
-	   that will make the other party to retransmit; hopefuly, we'll then 
-	   be better off */
+done:
+#ifdef _OBSOLETED
+	/* moved to  script callback */
+	UNREF( t );
+	T=T_UNDEFINED;
+#endif
+	/* don't try to relay statelessly neither on success
+       (we forwarded statefuly) nor on error; on troubles, 
+	   simply do nothing; that will make the other party to 
+	   retransmit; hopefuly, we'll then be better off */
 	return 0;
 }
 
 
-/* Checks if the new reply (with new_code status) should be sent or not
+/* This is the neuralgical point of reply processing -- called
+ * from within a REPLY_LOCK, t_should_relay_response decides
+ * how a reply shall be processed and how transaction state is
+ * affected.
+ *
+ * Checks if the new reply (with new_code status) should be sent or not
  *  based on the current
  * transactin status.
  * Returns 	- branch number (0,1,...) which should be relayed
  *         -1 if nothing to be relayed
  */
-int t_should_relay_response( struct cell *Trans , int new_code,
-									int branch , int *should_store )
+enum rps t_should_relay_response( struct cell *Trans , int new_code,
+	int branch , int *should_store, int *should_relay,
+	branch_bm_t *cancel_bitmap )
 {
-	//int T_code;
-	int b, lowest_b, lowest_s;
-
-	//if (Trans->uas.request->REQ_METHOD==METHOD_INVITE)
-	//	T_code = Trans->uac[branch].status;
-	//else
-	//T_code = Trans->uas.status;
+	int b, lowest_b, lowest_s, dummy;
 
 	/* note: this code never lets replies to CANCEL go through;
 	   we generate always a local 200 for CANCEL; 200s are
@@ -480,58 +623,204 @@ int t_should_relay_response( struct cell *Trans , int new_code,
 		if (new_code>=200 && new_code < 300  && 
 			Trans->uas.request->REQ_METHOD==METHOD_INVITE) {
 			DBG("DBG: t_should_relay: 200 INV after final sent\n");
-			*should_store=1;
-			return branch;
+			*should_store=0;
+			Trans->uac[branch].last_received=new_code;
+			*should_relay=branch;
+			return RPS_PUSHED_AFTER_COMPLETION;
 		} else {
+			/* except the exception above, too late  messages will
+			   be discarded */
 			*should_store=0;
-			return -1;
+			*should_relay=-1;
+			return RPS_DISCARDED;
 		}
-	} else { /* no final response sent yet */
-		/* negative replies subject to fork picking */
-		if (new_code >=300 ) {
-			/* dirty hack by Jiri -- subject to clean up as all the
-			   reply_processing crap; if there are no branches at
-			   all, I guess TM wants to reply itself and allow that
-			*/
-			if (Trans->nr_of_outgoings==0)
-				return 0;
-			*should_store=1;
-			/* if all_final return lowest */
-			lowest_b=-1; lowest_s=999;
-			for ( b=0; b<Trans->nr_of_outgoings ; b++ ) {
-				/* "fake" for the currently processed branch */
-				if (b==branch) {
-					if (new_code<lowest_s) {
-						lowest_b=b;
-						lowest_s=new_code;
-					}
-					continue;
-				}
-				/* there is still an unfinished UAC transaction; wait now! */
-				if ( Trans->uac[b].status<200 )
-					return -1;
-				if ( Trans->uac[b].status<lowest_s )
-				{
-					lowest_b =b;
-					lowest_s = T->uac[b].status;
+	} 
+
+	/* no final response sent yet */
+	/* negative replies subject to fork picking */
+	if (new_code >=300 ) {
+		/* negative reply received after we have received
+		   a final reply previously -- discard , unless
+		   a recoverable error occured, in which case
+		   retry
+	    */
+		if (Trans->uac[branch].last_received>=200) {
+			/* then drop! */
+			*should_store=0;
+			*should_relay=-1;
+			return RPS_DISCARDED;
+		}
+
+		Trans->uac[branch].last_received=new_code;
+		/* if all_final return lowest */
+		lowest_b=-1; lowest_s=999;
+		for ( b=0; b<Trans->nr_of_outgoings ; b++ ) {
+			/* "fake" for the currently processed branch */
+			if (b==branch) {
+				if (new_code<lowest_s) {
+					lowest_b=b;
+					lowest_s=new_code;
 				}
+				continue;
+			}
+			/* skip 'empty branches' */
+			if (!Trans->uac[b].request.buffer) continue;
+			/* there is still an unfinished UAC transaction; wait now! */
+			if ( Trans->uac[b].last_received<200 ) {
+				*should_store=1;	
+				*should_relay=-1;
+				return RPS_STORE;
+			}
+			if ( Trans->uac[b].last_received<lowest_s )
+			{
+				lowest_b =b;
+				lowest_s = Trans->uac[b].last_received;
+			}
+		} /* find lowest branch */
+		if (lowest_b==-1) {
+			LOG(L_CRIT, "ERROR: t_should_relay_response: lowest==-1\n");
+		}
+		/* no more pending branches -- try if that changes after
+		   a callback
+		*/
+		callback_event( TMCB_ON_NEGATIVE, Trans, 0, lowest_s );
+		/* look if the callback introduced new branches ... */
+		init_branch_iterator();
+		if (next_branch(&dummy)) {
+			if (t_forward_nonack(Trans, Trans->uas.request, 
+						(struct proxy_l *) 0 ) <0) {
+				/* error ... behave as if we did not try to
+				   add a new branch */
+				*should_store=0;
+				*should_relay=lowest_b;
+				return RPS_COMPLETED;
 			}
-			return lowest_b;
-		/* 1xx except 100 and 2xx will be relayed */
-		} else if (new_code>100) {
+			/* we succeded to launch new branches -- await
+			   result
+			*/
 			*should_store=1;
-			return branch;
+			*should_relay=-1;
+			return RPS_STORE;
 		}
-		/* 100 won't be relayed */
-		else {
-			if (!T->uac[branch].rpl_received) *should_store=1;
-				else *should_store=0;
-			if (Trans->uas.status==0) return branch;
-				else return -1;
+		/* look if the callback perhaps replied transaction */
+		if (Trans->uas.status >= 200) {
+			*should_store=0;
+			*should_relay=-1;
+			/* this might deserve an improvement -- if something
+			   was already replied, it was put on wait and then,
+			   returning RPS_COMPLETED will make t_on_reply
+			   put it on wait again; perhaps splitting put_on_wait
+			   from send_reply or a new RPS_ code would be healthy
+			*/
+			return RPS_COMPLETED;
 		}
+		/* really no more pending branches -- return lowest code */
+		*should_store=0;
+		*should_relay=lowest_b;
+		/* we dont need 'which_cancel' here -- all branches 
+		   known to have completed */
+		/* which_cancel( Trans, cancel_bitmap ); */
+		return RPS_COMPLETED;
+	} 
+
+	/* not >=300 ... it must be 2xx or provisional 1xx */
+	if (new_code>=100) {
+		/* 1xx and 2xx except 100 will be relayed */
+		Trans->uac[branch].last_received=new_code;
+		*should_store=0;
+		*should_relay= new_code==100? -1 : branch;
+		if (new_code>=200 ) {
+			which_cancel( Trans, cancel_bitmap );
+			return RPS_COMPLETED;
+		} else return RPS_PROVISIONAL;
+	}
+
+	/* reply_status didn't match -- it must be something weird */
+	LOG(L_CRIT, "ERROR: Oh my gooosh! We don't know whether to relay %d\n",
+		new_code);
+	*should_store=0;
+	*should_relay=-1;
+	return RPS_DISCARDED;
+}
+
+char *build_ack(struct sip_msg* rpl,struct cell *trans,int branch,
+	int *ret_len)
+{
+	str to;
+
+    if ( parse_headers(rpl,HDR_TO, 0)==-1 || !rpl->to )
+    {
+        LOG(L_ERR, "ERROR: t_build_ACK: "
+            "cannot generate a HBH ACK if key HFs in reply missing\n");
+        return NULL;
+    }
+	to.len=rpl->to->body.s+rpl->to->body.len-rpl->to->name.s;
+	to.s=rpl->orig+(rpl->to->name.s-rpl->buf);
+    return build_local( trans, branch, ret_len,
+        ACK, ACK_LEN, &to );
+}
+
+void on_negative_reply( struct cell* t, struct sip_msg* msg, 
+	int code, void *param )
+{
+	int act_ret;
+	struct sip_msg faked_msg;
+
+	/* nobody cares about a negative transaction -- ok, return */
+	if (!t->on_negative) {
+		DBG("DBG: on_negative_reply: no on_negative\n");
+		return;
+	}
+
+	DBG("DBG: on_negative_reply processed for transaction %p\n", t);
+
+	/* create faked environment  -- uri rewriting stuff needs the
+	   original uri
+	*/
+	memset( &faked_msg, 0, sizeof( struct sip_msg ));
+	/* original URI doesn't change -- feel free to refer to shmem */
+	faked_msg.first_line.u.request.uri=
+		t->uas.request->first_line.u.request.uri;
+	/* new_uri can change -- make a private copy */
+	if (t->uas.request->new_uri.s!=0 && t->uas.request->new_uri.len!=0) {
+		faked_msg.new_uri.s=pkg_malloc(t->uas.request->new_uri.len+1);
+		if (!faked_msg.new_uri.s) return;
+		faked_msg.new_uri.len=t->uas.request->new_uri.len;
+		memcpy( faked_msg.new_uri.s, t->uas.request->new_uri.s, 
+			faked_msg.new_uri.len);
+		faked_msg.new_uri.s[faked_msg.new_uri.len]=0;
+	} else { faked_msg.new_uri.s=0; faked_msg.new_uri.len=0; }
+	faked_msg.flags=t->uas.request->flags;	
+	/* if we set msg_id to something different from current's message
+       id, the first t_fork will properly clean new branch URIs
+	*/
+	faked_msg.id=t->uas.request->id-1;
+
+	act_ret=run_actions(reply_rlist[t->on_negative], &faked_msg );
+
+	if (act_ret<0) {
+		LOG(L_ERR, "on_negative_reply: Error in do_action\n");
 	}
 
-	LOG(L_CRIT, "ERROR: Oh my gooosh! We don't know whether to relay\n");
-	abort();
+#ifdef _OBSOLETED
+	/* this didn't work becaue URI is a part of shmem "monoblock";
+	   I could split it but it does not seem to be worth the
+	   effor
+	*/
+	/* project changes in faked message back to shmem copy */
+	t->uas.request->flags=faked_msg.flags;
+	if (faked_msg.new_uri.s) {
+		t->uas.request->new_uri.s=shm_resize(t->uas.request->new_uri.s,
+			faked_msg.new_uri.len);
+		if (!t->uas.request->new_uri.s) goto done;
+		memcpy(t->uas.request->new_uri.s, faked_msg.new_uri.s, 
+			faked_msg.new_uri.len );
+		t->uas.request->new_uri.len=faked_msg.new_uri.len;
+	}
+done:
+#endif
+	/* destroy faked environment, new_uri in particular */
+	if (faked_msg.new_uri.s) pkg_free(faked_msg.new_uri.s);
 }
 
+

+ 97 - 0
modules/tm/t_reply.h

@@ -0,0 +1,97 @@
+/*
+ * $Id$
+ */
+
+
+#ifndef _T_REPLY_H
+#define _T_REPLY_H
+
+#include "h_table.h"
+
+/* reply processing status */
+enum rps {
+	/* something bad happened */
+	RPS_ERROR=0,	
+	/* transaction completed but we still accept the reply */
+	RPS_PUSHED_AFTER_COMPLETION,
+	/* reply dscarded */
+	RPS_DISCARDED,
+	/* reply stored for later processing */
+	RPS_STORE,
+	/* transaction completed */
+	RPS_COMPLETED,
+	/* provisional reply not affecting transaction state */
+	RPS_PROVISIONAL
+};
+
+/* branch bitmap type */
+typedef unsigned int branch_bm_t;
+
+/* reply export types */
+typedef int (*treply_f)( struct sip_msg* p_msg,
+	unsigned int code, char * text );
+
+#define LOCK_REPLIES(_t) lock(&(_t)->reply_mutex )
+#define UNLOCK_REPLIES(_t) unlock(&(_t)->reply_mutex )
+
+/* This function is called whenever a reply for our module is received;
+ * we need to register this function on module initialization;
+ * Returns :   0 - core router stops
+ *             1 - core router relay statelessly
+ */
+int t_on_reply( struct sip_msg  *p_msg ) ;
+
+
+/* Retransmits the last sent inbound reply.
+ * Returns  -1 - error
+ *           1 - OK
+ */
+int t_retransmit_reply( /* struct sip_msg * */  );
+
+
+/* send a UAS reply
+ * returns 1 if everything was OK or -1 for erro
+ */
+int t_reply( struct cell *t, struct sip_msg * , unsigned int , char * );
+/* the same as t_reply, except it does not claim
+   REPLY_LOCK -- useful to be called within reply
+   processing
+*/
+int t_reply_unsafe( struct cell *t, struct sip_msg * , unsigned int , char * );
+
+
+enum rps t_should_relay_response( struct cell *Trans, int new_code, 
+	int branch, int *should_store, int *should_relay, 
+	branch_bm_t *cancel_bitmap  );
+
+void cleanup_after_final( struct s_table *h_table, struct cell *t,
+	unsigned int status );
+
+enum rps relay_reply( struct cell *t, struct sip_msg *p_msg, int branch, 
+	unsigned int msg_status, branch_bm_t *cancel_bitmap );
+
+enum rps local_reply( struct cell *t, struct sip_msg *p_msg, int branch,
+    unsigned int msg_status, branch_bm_t *cancel_bitmap );
+
+int store_reply( struct cell *trans, int branch, struct sip_msg *rpl);
+
+void set_final_timer( /* struct s_table *h_table,*/ struct cell *t );
+
+void cleanup_uac_timers( struct cell *t );
+
+char *build_ack( struct sip_msg* rpl, struct cell *trans, int branch ,
+	int *ret_len);
+
+void on_negative_reply( struct cell* t, struct sip_msg* msg,
+	int code, void *param  );
+
+/* set which 'reply' structure to take if only negative
+   replies arrive 
+*/
+int t_on_negative( unsigned int go_to );
+unsigned int get_on_negative();
+
+int t_retransmit_reply( struct cell *t );
+
+#endif
+

+ 282 - 65
modules/tm/t_thandlers.c

@@ -4,15 +4,159 @@
  * Timer handlers
  */
 
-#include "hash_func.h"
-#include "t_funcs.h"
+#include "../../hash_func.h"
 #include "../../dprint.h"
 #include "../../config.h"
 #include "../../parser/parser_f.h"
 #include "../../ut.h"
-//#include "../../timer.h"
+#include "t_funcs.h"
+#include "t_reply.h"
+#include "t_cancel.h"
 
+int noisy_ctimer=0;
 
+static void unlink_timers( struct cell *t )
+{
+	int i;
+	int remove_fr, remove_retr;
+
+	remove_fr=0; remove_retr=0;
+
+	/* first look if we need to remove timers and play with
+	   costly locks at all
+
+	    note that is_in_timer_list2 is unsafe but it does not
+	    hurt -- transaction is already dead (wait state) so that
+	    noone else will install a FR/RETR timer and it can only
+	    be removed from timer process itself -> it is safe to
+	    use it without any protection
+	*/
+	if (is_in_timer_list2(&t->uas.response.fr_timer)) remove_fr=1; 
+	else for (i=0; i<t->nr_of_outgoings; i++)
+		if (is_in_timer_list2(&t->uac[i].request.fr_timer)
+			|| is_in_timer_list2(&t->uac[i].local_cancel.fr_timer)) {
+				remove_fr=1;
+				break;
+		}
+	if (is_in_timer_list2(&t->uas.response.retr_timer)) remove_retr=1; 
+	else for (i=0; i<t->nr_of_outgoings; i++)
+		if (is_in_timer_list2(&t->uac[i].request.retr_timer)
+			|| is_in_timer_list2(&t->uac[i].local_cancel.fr_timer)) {
+				remove_retr=1;
+				break;
+		}
+
+	/* do what we have to do....*/
+	if (remove_retr) {
+		/* RT_T1 lock is shared by all other RT timer
+		   lists -- we can safely lock just one
+		*/
+		lock(hash_table->timers[RT_T1_TO_1].mutex);
+		remove_timer_unsafe(&t->uas.response.retr_timer);
+		for (i=0; i<t->nr_of_outgoings; i++) {
+			remove_timer_unsafe(&t->uac[i].request.retr_timer);
+			remove_timer_unsafe(&t->uac[i].local_cancel.retr_timer);
+		}
+		unlock(hash_table->timers[RT_T1_TO_1].mutex);
+	}
+	if (remove_fr) {
+		/* FR lock is shared by all other FR timer
+		   lists -- we can safely lock just one
+		*/
+		lock(hash_table->timers[FR_TIMER_LIST].mutex);
+		remove_timer_unsafe(&t->uas.response.fr_timer);
+		for (i=0; i<t->nr_of_outgoings; i++) {
+			remove_timer_unsafe(&t->uac[i].request.fr_timer);
+			remove_timer_unsafe(&t->uac[i].local_cancel.fr_timer);
+		}
+		unlock(hash_table->timers[FR_TIMER_LIST].mutex);
+	}
+}
+
+/* delete_cell attempt to delete a transaction of not refered
+   by any process; if so, it is put on a delete timer which will
+   try the same later; it assumes it is safe to read ref_count --
+   either the hash entry is locked or the transaction has been
+   removed from hash table (i.e., other processes can only
+   decrease ref_count)
+
+   it is static as it is safe to be called only from WAIT/DELETE
+   timers, the only valid place from which a transaction can be
+   removed
+*/
+
+static void delete_cell( struct cell *p_cell, int unlock )
+{
+
+	int i;
+
+	/* there may still be FR/RETR timers, which have been reset
+	   (i.e., time_out==TIMER_DELETED) but are stilled linked to
+	   timer lists and must be removed from there before the
+	   structures are released
+	*/
+	unlink_timers( p_cell );
+
+#ifdef EXTRA_DEBUG
+
+	if (is_in_timer_list2(& p_cell->wait_tl )) {
+		LOG( L_ERR, "ERROR: transaction %p scheduled for deletion and"
+			" still on WAIT, timeout=%d\n", p_cell, p_cell->wait_tl.time_out);
+		abort();
+	}
+	if (is_in_timer_list2(& p_cell->uas.response.retr_timer )) {
+		LOG( L_ERR, "ERROR: transaction %p scheduled for deletion and"
+			" still on RETR (rep), timeout=%d\n",
+			p_cell, p_cell->uas.response.retr_timer.time_out);
+		abort();
+	}
+	if (is_in_timer_list2(& p_cell->uas.response.fr_timer )) {
+		LOG( L_ERR, "ERROR: transaction %p scheduled for deletion and"
+			" still on FR (rep), timeout=%d\n", p_cell,
+			p_cell->uas.response.fr_timer.time_out);
+		abort();
+	}
+	for (i=0; i<p_cell->nr_of_outgoings; i++) {
+		if (is_in_timer_list2(& p_cell->uac[i].request.retr_timer)) {
+			LOG( L_ERR, "ERROR: transaction %p scheduled for deletion and"
+				" still on RETR (req %d), timeout %d\n", p_cell, i,
+				p_cell->uac[i].request.retr_timer.time_out);
+			abort();
+		}
+		if (is_in_timer_list2(& p_cell->uac[i].request.fr_timer)) {
+			LOG( L_ERR, "ERROR: transaction %p scheduled for deletion and"
+				" still on FR (req %d), timeout %d\n", p_cell, i,
+				p_cell->uac[i].request.fr_timer.time_out);
+			abort();
+		}
+		if (is_in_timer_list2(& p_cell->uac[i].local_cancel.retr_timer)) {
+			LOG( L_ERR, "ERROR: transaction %p scheduled for deletion and"
+				" still on RETR/cancel (req %d), timeout %d\n", p_cell, i,
+				p_cell->uac[i].request.retr_timer.time_out);
+			abort();
+		}
+		if (is_in_timer_list2(& p_cell->uac[i].local_cancel.fr_timer)) {
+			LOG( L_ERR, "ERROR: transaction %p scheduled for deletion and"
+				" still on FR/cancel (req %d), timeout %d\n", p_cell, i,
+				p_cell->uac[i].request.fr_timer.time_out);
+			abort();
+		}
+	}
+	/* reset_retr_timers( hash_table, p_cell ); */
+#endif
+	/* still in use ... don't delete */
+	if ( IS_REFFED_UNSAFE(p_cell) ) {
+		if (unlock) UNLOCK_HASH(p_cell->hash_index);
+		DBG("DEBUG: delete_cell %p: can't delete -- still reffed\n",
+			p_cell);
+		/* it's added to del list for future del */
+		set_timer( hash_table, &(p_cell->dele_tl), DELETE_LIST );
+	} else {
+		if (unlock) UNLOCK_HASH(p_cell->hash_index);
+		DBG("DEBUG: delete transaction %p\n", p_cell );
+		free_cell( p_cell );
+	}
+}
 
 
 
@@ -32,18 +176,17 @@ inline void retransmission_handler( void *attr)
 
 	/*the transaction is already removed from RETRANSMISSION_LIST by timer*/
 	/* retransmision */
-	DBG("DEBUG: retransmission_handler : resending (t=%p)\n", r_buf->my_T);
-	switch ( r_buf->activ_type )
-	{
-		case (TYPE_REQUEST):
+	if ( r_buf->activ_type==TYPE_LOCAL_CANCEL 
+		|| r_buf->activ_type==0 ) {
 			SEND_BUFFER( r_buf );
-			break;
-		case (TYPE_LOCAL_CANCEL):
-			SEND_CANCEL_BUFFER( r_buf );
-			break;
-		default:
-			T=r_buf->my_T;
-			t_retransmit_reply();
+			DBG("DEBUG: retransmission_handler : "
+				"request resending (t=%p, %.9s ... )\n", 
+				r_buf->my_T, r_buf->buffer);
+	} else {
+			DBG("DEBUG: retransmission_handler : "
+				"reply resending (t=%p, %.9s ... )\n", 
+				r_buf->my_T, r_buf->buffer);
+			t_retransmit_reply(r_buf->my_T);
 	}
 
 	id = r_buf->retr_list;
@@ -59,66 +202,126 @@ inline void retransmission_handler( void *attr)
 
 inline void final_response_handler( void *attr)
 {
-	struct retr_buf* r_buf = (struct retr_buf*)attr;
+	int silent;
+	struct retr_buf* r_buf;
+	enum rps reply_status;
+	struct cell *t;
+	branch_bm_t cancel_bitmap;
+	short do_cancel_branch;
 
-#ifdef EXTRA_DEBUG
-	if (r_buf->my_T->damocles) 
+	r_buf = (struct retr_buf*)attr;
+	t=r_buf->my_T;
+
+#	ifdef EXTRA_DEBUG
+	if (t->damocles) 
 	{
 		LOG( L_ERR, "ERROR: transaction %p scheduled for deletion and"
 			" called from FR timer\n",r_buf->my_T);
 		abort();
 	}
-#endif
+#	endif
+
+	reset_timer( hash_table , &(r_buf->retr_timer) );
 
 	/* the transaction is already removed from FR_LIST by the timer */
+
+	/* FR for local cancels.... */
 	if (r_buf->activ_type==TYPE_LOCAL_CANCEL)
 	{
-		DBG("DEBUG: FR_handler: stop retransmission for Local Cancel\n");
-		reset_timer( hash_table , &(r_buf->retr_timer) );
+		DBG("DEBUG: FR_handler: stop retr for Local Cancel\n");
 		return;
 	}
-	/* send a 408 */
-	if ( r_buf->my_T->uac[r_buf->branch].status<200
-#ifdef SILENT_FR
-	&& (r_buf->my_T->nr_of_outgoings>1     /*if we have forked*/
-		|| r_buf->my_T->uas.request->first_line.u.request.method_value!=
-			METHOD_INVITE                  /*if is not an INVITE */
-		|| r_buf->my_T->uac[r_buf->my_T->nr_of_outgoings].uri.s
-		                                   /*if "no on no response" was set*/
-		|| r_buf->my_T->uac[r_buf->branch].rpl_received==0
-											/*if no reply was received*/
-	)
-#endif
-	)
-	{
-		DBG("DEBUG: FR_handler:stop retr. and send CANCEL (%p)\n",r_buf->my_T);
-		reset_timer( hash_table, &(r_buf->retr_timer) );
-		/* BUG -- we want CANCELs actually ONLY for INVITE; BTW, the
-		   CANCEL construction method results for non-INVITEs in
-		   bizzar messages such as "CANCELS" for OPTIONS; -Jiri
+
+	/* FR for replies (negative INVITE replies) */
+	if (r_buf->activ_type>0) {
+#		ifdef EXTRA_DEBUG
+		if (t->uas.request->REQ_METHOD!=METHOD_INVITE
+			|| t->uas.status < 300 ) {
+			LOG(L_ERR, "ERROR: FR timer: uknown type reply buffer\n");
+			abort();
+		}
+#		endif
+		put_on_wait( t );
+		return;
+	};
+
+	/* lock reply processing to determine how to proceed reliably */
+	LOCK_REPLIES( t );
+	/* now it can be only a request retransmission buffer;
+	   try if you can simply discard the local transaction 
+	   state without compellingly removing it from the
+	   world */
+	silent=
+		/* not for UACs */
+		!t->local
+		/* invites only */
+		&& t->is_invite
+		/* parallel forking does not allow silent state discarding */
+		&& t->nr_of_outgoings==1
+		/* on_no_reply handler not installed -- serial forking could occur 
+		   otherwise */
+		&& t->on_negative==0
+		/* something received -- we will not be silent on error */
+		&& t->uac[r_buf->branch].last_received>0
+		/* don't go silent if disallowed globally ... */
+		&& noisy_ctimer==0
+		/* ... or for this particular transaction */
+		&& t->noisy_ctimer==0;
+	if (silent) {
+		UNLOCK_REPLIES(t);
+		DBG("DEBUG: FR_handler: transaction silently dropped (%p)\n",t);
+		put_on_wait( t );
+		return;
+	}
+
+	DBG("DEBUG: FR_handler:stop retr. and send CANCEL (%p)\n", t);
+	do_cancel_branch=t->is_invite && 
+		should_cancel_branch(t, r_buf->branch);
+
+#ifdef _OBSOLETED
+	/* set global environment for currently processed transaction */
+	T=t;
+	global_msg_id=T->uas.request->id;
+#endif 
+
+	cancel_bitmap=do_cancel_branch ? 1<<r_buf->branch : 0;
+	if (t->local) {
+		reply_status=local_reply( t, FAKED_REPLY, r_buf->branch, 
+			408, &cancel_bitmap );
+	} else {
+		reply_status=relay_reply( t, FAKED_REPLY, r_buf->branch, 408, 
+			&cancel_bitmap );
+	}
+	/* now when out-of-lock do the cancel I/O */
+	if (do_cancel_branch) cancel_branch(t, r_buf->branch );
+	/* it's cleaned up on error; if no error occured and transaction
+	   completed regularly, I have to clean-up myself
+	*/
+	if (reply_status==RPS_COMPLETED) {
+		/* don't need to cleanup uac_timers -- they were cleaned
+		   branch by branch and this last branch's timers are
+		   reset now too
 		*/
-		if (r_buf->my_T->uas.request->first_line.u.request.method_value==METHOD_INVITE)
-			t_build_and_send_CANCEL( r_buf->my_T ,r_buf->branch);
-		/* dirty hack:t_send_reply would increase ref_count which would indeed
-		result in refcount++ which would not -- until timer processe's
-		T changes again; currently only on next call to t_send_reply from
-		FR timer; thus I fake the values now to avoid recalculating T
-		and refcount++ JKU */
-		T=r_buf->my_T;
-		global_msg_id=T->uas.request->id;
-		DBG("DEBUG: FR_handler: send 408 (%p)\n", r_buf->my_T);
-		t_send_reply( r_buf->my_T->uas.request, 408, "Request Timeout",
-			r_buf->branch);
-	}else{
-		/* put it on WT_LIST - transaction is over */
-		DBG("DEBUG: final_response_handler:-> put on wait"
-			" (t=%p)\n", r_buf->my_T);
-		t_put_on_wait(  r_buf->my_T );
+		/* don't need to issue cancels -- local cancels have been
+		   issued branch by branch and this last branch was
+		   cancelled now too
+		*/
+		/* then the only thing to do now is to put the transaction
+		   on FR/wait state 
+		*/
+		set_final_timer( /* hash_table, */ t );
 	}
 	DBG("DEBUG: final_response_handler : done\n");
 }
 
-
+void cleanup_localcancel_timers( struct cell *t )
+{
+	int i;
+	for (i=0; i<t->nr_of_outgoings; i++ )  {
+		reset_timer( hash_table, &t->uac[i].local_cancel.retr_timer );
+		reset_timer( hash_table, &t->uac[i].local_cancel.fr_timer );
+	}
+}
 
 
 inline void wait_handler( void *attr)
@@ -131,20 +334,23 @@ inline void wait_handler( void *attr)
 			" called from WAIT timer\n",p_cell);
 		abort();
 	}	
+	DBG("DEBUG: ---------- WAIT timer hit ------- \n");
 #endif
 
+	/* stop cancel timers if any running */
+	if (p_cell->is_invite) cleanup_localcancel_timers( p_cell );
+
 	/* the transaction is already removed from WT_LIST by the timer */
-	/* the cell is removed from the hash table */
+	/* remove the cell from the hash table */
 	DBG("DEBUG: wait_handler : removing %p from table \n", p_cell );
-	remove_from_hash_table( hash_table, p_cell );
+	LOCK_HASH( p_cell->hash_index );
+	remove_from_hash_table_unsafe( hash_table, p_cell );
 	/* jku: no more here -- we do it when we put a transaction on wait */
-	DBG("DEBUG: wait_handler : stopping all timers\n");
-	reset_retr_timers(hash_table,p_cell) ; 
-	/* put it on DEL_LIST - sch for del */
 #ifdef EXTRA_DEBUG
 	p_cell->damocles = 1;
 #endif
-	delete_cell( p_cell );
+	/* delete (returns with UNLOCK-ed_HASH) */
+	delete_cell( p_cell, 1 /* unlock on return */ );
 	DBG("DEBUG: wait_handler : done\n");
 }
 
@@ -163,7 +369,17 @@ inline void delete_handler( void *attr)
 		abort();
 	}	
 #endif
-	delete_cell( p_cell );
+
+	/* we call delete now without any locking on hash/ref_count;
+	   we can do that because delete_handler is only entered after
+	   the delete timer was installed from wait_handler, which
+	   removed transaction from hash table and did not destroy it
+	   because some processes were using it; that means that the
+	   processes currently using the transaction can unref and no
+	   new processes can ref -- we can wait until ref_count is
+	   zero safely without locking
+	*/
+	delete_cell( p_cell, 0 /* don't unlock on return */ );
     DBG("DEBUG: delete_handler : done\n");
 }
 
@@ -178,7 +394,8 @@ inline void delete_handler( void *attr)
 		(_tl)->next_tl = (_tl)->prev_tl = 0;\
 		DBG("DEBUG: timer routine:%d,tl=%p next=%p\n",\
 			id,(_tl),tmp_tl);\
-		(_handler)( (_tl)->payload );\
+		if ((_tl)->time_out>TIMER_DELETED) \
+			(_handler)( (_tl)->payload );\
 		(_tl) = tmp_tl;\
 	}
 

+ 1 - 1
modules/tm/test.c

@@ -1,4 +1,4 @@
-#include "hash_func.h"
+#include "../../hash_func.h"
 #include "t_funcs.h"
 #include "../../dprint.h"
 #include "../../config.h"

+ 153 - 2
modules/tm/timer.c

@@ -1,7 +1,74 @@
 /*
  * $Id$
+ *
  */
 
+/* 
+  timer.c is where we implement TM timers. It has been designed
+  for high performance using some techniques of which timer users
+  need to be aware.
+
+	One technique is "fixed-timer-length". We maintain separate 
+	timer lists, all of them include elements of the same time
+	to fire. That allows *appending* new events to the list as
+	opposed to inserting them by time, which is costly due to
+	searching time spent in a mutex. The performance benefit is
+	noticeable. The limitation is you need a new timer list for
+	each new timer length.
+
+	Another technique is the timer process slices off expired elements
+	from the list in a mutex, but executes the timer after the mutex
+	is left. That saves time greatly as whichever process wants to
+	add/remove a timer, it does not have to wait until the current
+	list is processed. However, be aware the timers may hit in a delayed
+	manner; you have no guarantee in your process that after resetting a timer, 
+	it will no more hit. It might have been removed by timer process,
+    and is waiting to be executed.  The following example shows it:
+
+			PROCESS1				TIMER PROCESS
+
+	0.								timer hits, it is removed from queue and
+									about to be executed
+	1.	process1 decides to
+		reset the timer 
+	2.								timer is executed now
+	3.	if the process1 naively
+		thinks the timer could not 
+		have been executed after 
+		resetting the timer, it is
+		WRONG -- it was (step 2.)
+
+	So be careful when writing the timer handlers. Currently defined timers 
+	don't hurt if they hit delayed, I hope at least. Retransmission timer 
+	may results in a useless retransmission -- not too bad. FR timer not too
+	bad either as timer processing uses a REPLY mutex making it safe to other
+	processing affecting transaction state. Wait timer not bad either -- processes
+	putting a transaction on wait don't do anything with it anymore.
+
+		Example when it does not hurt:
+
+			P1						TIMER
+	0.								RETR timer removed from list and
+									scheduled for execution
+	1. 200/BYE received->
+	   reset RETR, put_on_wait
+	2.								RETR timer executed -- too late but it does
+									not hurt
+	3.								WAIT handler executed
+
+	The rule of thumb is don't touch data you put under a timer. Create data,
+    put them under a timer, and let them live until they are safely destroyed from
+    wait/delete timer.  The only safe place to manipulate the data is 
+    from timer process in which delayed timers cannot hit (all timers are
+    processed sequentially).
+
+	A "bad example" -- rewriting content of retransmission buffer
+	in an unprotected way is bad because a delayed retransmission timer might 
+	hit. Thats why our reply retransmission procedure is enclosed in 
+	a REPLY_LOCK.
+
+*/
+
 
 #include "config.h"
 #include "h_table.h"
@@ -84,6 +151,10 @@ void remove_timer_unsafe(  struct timer_link* tl )
 	};
 #endif
 	if (is_in_timer_list2( tl )) {
+#ifdef EXTRA_DEBUG
+		DBG("DEBUG: unlinking timer: tl=%p, timeout=%d, group=%d\n", 
+			tl, tl->time_out, tl->tg);
+#endif
 		tl->prev_tl->next_tl = tl->next_tl;
 		tl->next_tl->prev_tl = tl->prev_tl;
 		tl->next_tl = 0;
@@ -98,7 +169,7 @@ void remove_timer_unsafe(  struct timer_link* tl )
 /* put a new cell into a list nr. list_id within a hash_table;
    set initial timeout */
 void add_timer_unsafe( struct timer *timer_list, struct timer_link *tl,
-													unsigned int time_out )
+	unsigned int time_out )
 {
 #ifdef EXTRA_DEBUG
 	if (timer_list->last_tl.prev_tl==0) {
@@ -128,7 +199,7 @@ void add_timer_unsafe( struct timer *timer_list, struct timer_link *tl,
 
 /* detach items passed by the time from timer list */
 struct timer_link  *check_and_split_time_list( struct timer *timer_list,
-																int time )
+	int time )
 {
 	struct timer_link *tl , *end, *ret;
 
@@ -174,3 +245,83 @@ struct timer_link  *check_and_split_time_list( struct timer *timer_list,
 
 
 
+/* stop timer */
+void reset_timer( struct s_table *hash_table,
+	struct timer_link* tl )
+{
+	/* disqualify this timer from execution by setting its time_out
+	   to zero; it will stay in timer-list until the timer process
+	   starts removing outdated elements; then it will remove it
+	   but not execute; there is a race condition, though -- see
+	   timer.c for more details
+	*/
+	tl->time_out = TIMER_DELETED;
+#ifdef EXTRA_DEBUG
+	DBG("DEBUG: reset_timer (group %d, tl=%p)\n", tl->tg, tl );
+#endif
+#ifdef _OBSOLETED
+	/* lock(timer_group_lock[ tl->tg ]); */
+	/* hack to work arround this timer group thing*/
+	lock(hash_table->timers[timer_group[tl->tg]].mutex);
+	remove_timer_unsafe( tl );
+	unlock(hash_table->timers[timer_group[tl->tg]].mutex);
+	/*unlock(timer_group_lock[ tl->tg ]);*/
+#endif
+}
+
+
+
+
+/* determine timer length and put on a correct timer list */
+void set_timer( struct s_table *hash_table,
+	struct timer_link *new_tl, enum lists list_id )
+{
+	unsigned int timeout;
+	struct timer* list;
+
+
+	if (list_id<FR_TIMER_LIST || list_id>=NR_OF_TIMER_LISTS) {
+		LOG(L_CRIT, "ERROR: set_timer: unkown list: %d\n", list_id);
+#ifdef EXTRA_DEBUG
+		abort();
+#endif
+		return;
+	}
+	timeout = timer_id2timeout[ list_id ];
+	list= &(hash_table->timers[ list_id ]);
+
+	lock(list->mutex);
+	/* make sure I'm not already on a list */
+	remove_timer_unsafe( new_tl );
+	add_timer_unsafe( list, new_tl, get_ticks()+timeout);
+	unlock(list->mutex);
+}
+
+/* similar to set_timer, except it allows only one-time
+   timer setting and all later attempts are ignored */
+void set_1timer( struct s_table *hash_table,
+	struct timer_link *new_tl, enum lists list_id )
+{
+	unsigned int timeout;
+	struct timer* list;
+
+
+	if (list_id<FR_TIMER_LIST || list_id>=NR_OF_TIMER_LISTS) {
+		LOG(L_CRIT, "ERROR: set_timer: unkown list: %d\n", list_id);
+#ifdef EXTRA_DEBUG
+		abort();
+#endif
+		return;
+	}
+	timeout = timer_id2timeout[ list_id ];
+	list= &(hash_table->timers[ list_id ]);
+
+	lock(list->mutex);
+	if (!(new_tl->time_out>TIMER_DELETED)) {
+		/* make sure I'm not already on a list */
+		/* remove_timer_unsafe( new_tl ); */
+		add_timer_unsafe( list, new_tl, get_ticks()+timeout);
+	}
+	unlock(list->mutex);
+}
+

+ 16 - 2
modules/tm/timer.h

@@ -5,7 +5,10 @@
 #ifndef _TIMER_H
 #define _TIMER_H
 
-
+/* timer timestamp value indicating a timer has been 
+   deactived and shall not be executed
+*/
+#define TIMER_DELETED	1
 
 
 
@@ -32,6 +35,7 @@ extern unsigned int timer_id2timeout[NR_OF_TIMER_LISTS];
 struct timer;
 
 #include "lock.h"
+#include "t_funcs.h"
 
 
 /* all you need to put a cell in a timer list
@@ -40,7 +44,7 @@ typedef struct timer_link
 {
 	struct timer_link *next_tl;
 	struct timer_link *prev_tl;
-	unsigned int       time_out;
+	volatile unsigned int       time_out;
 	void              *payload;
 	struct timer      *timer_list;
 	enum timer_groups  tg;
@@ -64,4 +68,14 @@ void remove_timer_unsafe(  struct timer_link* tl ) ;
 void add_timer_unsafe( struct timer*, struct timer_link*, unsigned int);
 struct timer_link  *check_and_split_time_list( struct timer*, int);
 
+void reset_timer( struct s_table *hash_table,
+	struct timer_link* tl );
+/* determine timer length and put on a correct timer list */
+void set_timer( struct s_table *hash_table,
+	struct timer_link *new_tl, enum lists list_id );
+/* similar to set_timer, except it allows only one-time
+   timer setting and all later attempts are ignored */
+void set_1timer( struct s_table *hash_table,
+	struct timer_link *new_tl, enum lists list_id );
+
 #endif

+ 0 - 565
modules/tm/tm.c

@@ -1,565 +0,0 @@
-/*
- * $Id$
- *
- * TM module
- *
- */
-
-#include <stdio.h>
-#include <string.h>
-#include <netdb.h>
-
-#include "../../sr_module.h"
-#include "../../dprint.h"
-#include "../../error.h"
-#include "../../ut.h"
-
-#include "sip_msg.h"
-#include "h_table.h"
-#include "t_funcs.h"
-#include "t_hooks.h"
-#include "tm_load.h"
-#include "t_fork.h"
-#include "ut.h"
-
-
-
-inline static int w_t_check(struct sip_msg* msg, char* str, char* str2);
-inline static int w_t_send_reply(struct sip_msg* msg, char* str, char* str2);
-inline static int w_t_release(struct sip_msg* msg, char* str, char* str2);
-inline static int fixup_t_send_reply(void** param, int param_no);
-inline static int w_t_unref( struct sip_msg* p_msg, char* foo, char* bar );
-inline static int w_t_retransmit_reply(struct sip_msg* p_msg, char* foo, char* bar );
-/*
-inline static int w_t_add_transaction(struct sip_msg* p_msg, char* foo, char* bar );
-*/
-
-inline static int w_t_newtran(struct sip_msg* p_msg, char* foo, char* bar );
-
-inline static int t_relay( struct sip_msg  *p_msg ,  char* , char* );
-inline static int w_t_relay_to( struct sip_msg  *p_msg , char *proxy, char *);
-static int t_relay_to( struct sip_msg  *p_msg , struct proxy_l *proxy );
-/*
-inline static int w_t_forward_ack(struct sip_msg* msg, char* proxy, char* );
-*/
-inline static int w_t_forward_nonack(struct sip_msg* msg, char* str, char* );
-inline static int fixup_hostport2proxy(void** param, int param_no);
-
-inline static int w_t_add_fork_ip(struct sip_msg* msg, char* proxy, char* );
-inline static int w_t_add_fork_uri(struct sip_msg* msg, char* str, char* );
-inline static int w_t_add_fork_on_no_rpl(struct sip_msg* msg,char* str,char* );
-inline static int w_t_clear_forks(struct sip_msg* msg, char* , char* );
-
-inline static int fixup_uri2fork(void** param, int param_no);
-
-inline static void w_onbreak(struct sip_msg* msg) { t_unref(); }
-
-static int mod_init(void);
-
-#ifdef STATIC_TM
-struct module_exports tm_exports = {
-#else
-struct module_exports exports= {
-#endif
-	"tm",
-	/* -------- exported functions ----------- */
-	(char*[]){			
-/*		- obsoleted by atomic t_newtran 
-				"t_add_transaction",
-*/
-				"t_newtran",
-				"t_lookup_request",
-				"t_send_reply",
-				"t_retransmit_reply",
-				"t_release",
-				"t_unref",
-				"t_relay_to",
-				"t_relay",
-				"t_forward_nonack",
-/*
-				"t_forward_ack",
-*/
-				"t_fork_to_ip",
-				"t_fork_to_uri",
-				"t_clear_forks",
-				"t_fork_on_no_response",
-				"register_tmcb",
-				"load_tm"
-			},
-	(cmd_function[]){
-/*
-					w_t_add_transaction,
-*/					w_t_newtran,
-					w_t_check,
-					w_t_send_reply,
-					w_t_retransmit_reply,
-					w_t_release,
-					w_t_unref,
-					w_t_relay_to,
-					t_relay,
-					w_t_forward_nonack,
-/*
-					w_t_forward_ack,
-*/
-					w_t_add_fork_ip,
-					w_t_add_fork_uri,
-					w_t_clear_forks,
-					w_t_add_fork_on_no_rpl,
-					(cmd_function) register_tmcb,
-					(cmd_function) load_tm
-					},
-	(int[]){
-				0, /* t_newtran */
-				0, /* t_lookup_request */
-				2, /* t_send_reply */
-				0, /* t_retransmit_reply */
-				0, /* t_release */
-				0, /* t_unref */
-				2, /* t_relay_to */
-				0, /* t_relay */
-				2, /* t_forward_nonack */
-/* 				2,*/ /* t_forward_ack */
-
-				2, /* t_fork_to_ip */
-				1, /* t_fork_to_uri */
-				0, /* t_clear_forks */
-				1,  /* t_add_fork_on_no_response */
-				2 /* register_tmcb */,
-				1 /* load_tm */
-			},
-	(fixup_function[]){
-				0,						/* t_newtran */
-				0,						/* t_lookup_request */
-				fixup_t_send_reply,		/* t_send_reply */
-				0,						/* t_retransmit_reply */
-				0,						/* t_release */
-				0,						/* t_unref */
-				fixup_hostport2proxy,	/* t_relay_to */
-				0,						/* t_relay */
-				fixup_hostport2proxy,	/* t_forward_nonack */
-				/* fixup_hostport2proxy,	*/ /* t_forward_ack */
-				fixup_hostport2proxy,	/* t_fork_to_ip */
-				fixup_uri2fork,   		/* t_fork_to_uri */
-				0,						/* t_clear_forks */
-				fixup_uri2fork,		/* t_add_fork_on_no_response */
-				0,						/* register_tmcb */
-				0						/* load_tm */
-	
-		},
-	15,
-
-	/* ------------ exported variables ---------- */
-	(char *[]) { /* Module parameter names */
-		"fr_timer",
-		"fr_inv_timer",
-		"wt_timer",
-		"delete_timer",
-		"retr_timer1p1",
-		"retr_timer1p2",
-		"retr_timer1p3",
-		"retr_timer2"
-	},
-	(modparam_t[]) { /* variable types */
-		INT_PARAM,
-		INT_PARAM,
-		INT_PARAM,
-		INT_PARAM,
-		INT_PARAM,
-		INT_PARAM,
-		INT_PARAM,
-		INT_PARAM
-	},
-	(void *[]) { /* variable pointers */
-		&(timer_id2timeout[FR_TIMER_LIST]),
-		&(timer_id2timeout[FR_INV_TIMER_LIST]),
-		&(timer_id2timeout[WT_TIMER_LIST]),
-		&(timer_id2timeout[DELETE_LIST]),
-		&(timer_id2timeout[RT_T1_TO_1]),
-		&(timer_id2timeout[RT_T1_TO_2]),
-		&(timer_id2timeout[RT_T1_TO_3]),
-		&(timer_id2timeout[RT_T2])
-	},
-	8,      /* Number of module paramers */
-
-	mod_init, /* module initialization function */
-	(response_function) t_on_reply,
-	(destroy_function) tm_shutdown,
-	w_onbreak,
-	0  /* per-child init function */
-};
-
-
-
-static int mod_init(void)
-{
-
-	DBG( "TM - initializing...\n");
-	if (tm_startup()==-1) return -1;
-	return 0;
-}
-
-
-/* (char *hostname, char *port_nr) ==> (struct proxy_l *, -)  */
-
-inline static int fixup_hostport2proxy(void** param, int param_no)
-{
-	unsigned int port;
-	char *host;
-	int err;
-	struct proxy_l *proxy;
-	
-	DBG("TM module: fixup_t_forward(%s, %d)\n", (char*)*param, param_no);
-	if (param_no==1){
-		DBG("TM module: fixup_t_forward: param 1.. do nothing, wait for #2\n");
-		return 0;
-	} else if (param_no==2) {
-
-		host=(char *) (*(param-1)); 
-		port=str2s(*param, strlen(*param), &err);
-		if (err!=0) {
-			LOG(L_ERR, "TM module:fixup_t_forward: bad port number <%s>\n",
-				(char*)(*param));
-			 return E_UNSPEC;
-		}
-		proxy=mk_proxy(host, port);
-		if (proxy==0) {
-			LOG(L_ERR, "ERROR: fixup_t_forwardv6: bad host name in URI <%s>\n",
-				host );
-			return E_BAD_ADDRESS;
-		}
-		/* success -- fix the first parameter to proxy now ! */
-		free( *(param-1));
-		*(param-1)=proxy;
-		return 0;
-	} else {
-		LOG(L_ERR, "ERROR: fixup_t_forwardv6 called with parameter #<>{1,2}\n");
-		return E_BUG;
-	}
-}
-
-
-/* (char *code, char *reason_phrase)==>(int code, r_p as is) */
-
-
-inline static int fixup_t_send_reply(void** param, int param_no)
-{
-	unsigned int code;
-	int err;
-
-	if (param_no==1){
-		code=str2s(*param, strlen(*param), &err);
-		if (err==0){
-			free(*param);
-			*param=(void*)code;
-			return 0;
-		}else{
-			LOG(L_ERR, "TM module:fixup_t_send_reply: bad  number <%s>\n",
-					(char*)(*param));
-			return E_UNSPEC;
-		}
-	}
-	/* second param => no conversion*/
-	return 0;
-}
-
-
-
-
-inline static int w_t_check(struct sip_msg* msg, char* str, char* str2)
-{
-	return t_check( msg , 0 , 0 ) ? 1 : -1;
-}
-
-
-
-#ifdef _TOO_OLD
-inline static int w_t_forward_ack(struct sip_msg* msg, char* proxy, char* _foo)
-{
-	if (t_check( msg , 0 , 0 )==-1) return -1;
-	if (!T) {
-		DBG("DEBUG: t_forward_ack: no transaction found \n");
-		return -1;
-	}
-	return t_forward_ack(msg /*, ( struct proxy_l *) proxy */ );
-}
-
-#endif
-
-
-inline static int w_t_forward_nonack(struct sip_msg* msg, char* proxy, char* _foo)
-{
-	if (t_check( msg , 0 , 0)==-1) return -1;
-	if (!T) {
-		DBG("DEBUG: t_forward_nonack: no transaction found\n");
-		return -1;
-	}
-	return t_forward_nonack(msg, ( struct proxy_l *) proxy );
-}
-
-
-
-inline static int w_t_send_reply(struct sip_msg* msg, char* str, char* str2)
-{
-	if (t_check( msg , 0 , 0)==-1) return -1;
-	if (!T) {
-		LOG(L_ERR, "ERROR: t_send_reply: cannot send a t_reply to a message "
-			"for which no T-state has been established\n");
-		return -1;
-	}
-	return t_send_reply(msg, (unsigned int) str, str2, 0);
-}
-
-
-
-
-inline static int w_t_release(struct sip_msg* msg, char* str, char* str2)
-{
-	if (t_check( msg  , 0 , 0 )==-1) return 1;
-	if ( T && T!=T_UNDEFINED ) 
-		return t_put_on_wait( T );
-	return 1;
-}
-
-
-
-
-inline static int w_t_unref( struct sip_msg* p_msg, char* foo, char* bar )
-{
-	if (T==T_UNDEFINED || T==T_NULL)
-		return -1;
-    return t_unref( /* p_msg */ );
-}
-
-
-
-
-inline static int w_t_retransmit_reply( struct sip_msg* p_msg, char* foo, char* bar)
-{
-	if (t_check( p_msg  , 0 , 0 )==-1) 
-		return 1;
-	if (T)
-		return t_retransmit_reply( p_msg );
-	else 
-		return -1;
-	return 1;
-}
-
-
-
-
-/* inline static int w_t_add_transaction( struct sip_msg* p_msg, 
-	char* foo, char* bar ) {
-*/
-inline static int w_t_newtran( struct sip_msg* p_msg, char* foo, char* bar ) {
-	if (t_check( p_msg , 0 , 0 )==-1) return -1;
-	if (T) {
-		LOG(L_ERR,"ERROR: t_add_transaction: won't add a retransmission\n");
-		return -1;
-	}
-	return t_newtran( p_msg );
-}
-
-
-
-
-inline static int w_t_add_fork_ip(struct sip_msg* msg, char* proxy, char* _foo )
-{
-	struct proxy_l *p;
-	union sockaddr_union to;
-
-	p=(struct proxy_l *) proxy;
-	hostent2su(&to, &p->host, p->addr_idx, (p->port)?htons(p->port):htons(SIP_PORT));
-	return t_add_fork( to, 0,0,DEFAULT,0);
-}
-
-
-
-
-inline static int fixup_uri2fork (void** param, int param_no)
-{
-	struct fork* fork_pack;
-	struct proxy_l *p;
-
-	if (param_no==1)
-	{
-		fork_pack = (struct fork*)pkg_malloc(sizeof(struct fork));
-		if (!fork_pack)
-		{
-			LOG(L_ERR, "TM module:fixup_t_add_fork_uri: \
-				cannot allocate memory!\n");
-			return E_UNSPEC;
-		}
-		fork_pack->uri.len = strlen(*param);
-		fork_pack->uri.s = *param;
-
-		p=uri2proxy( & fork_pack->uri );
-		if (p==0) {
-			pkg_free( fork_pack );
-			return E_CFG;
-		}
-		hostent2su(&fork_pack->to, &p->host, p->addr_idx,
-        	(p->port)?htons(p->port):htons(SIP_PORT));
-
-		free_proxy(p); free(p);
-		*param=(void*)fork_pack;
-		return 0;
-	} else {
-		LOG(L_ERR, "Error: URI should not be followed by another parameter\n");
-		/* second param => no conversion*/
-		return E_BUG;
-	}
-}
-
-
-
-
-inline static int w_t_add_fork_uri(struct sip_msg* msg, char* str, char* _foo)
-{
-	struct fork *fp;
-	fp=(struct fork *) str;
-	return t_add_fork( fp->to, fp->uri.s, fp->uri.len, DEFAULT, 0);
-}
-
-inline static int w_t_add_fork_on_no_rpl(struct sip_msg* msg, char* str, char* _foo )
-{
-	struct fork *fp;
-	fp=(struct fork *) str;
-	return t_add_fork(  fp->to, fp->uri.s, fp->uri.len, NO_RESPONSE, 0);
-}
-
-inline static int w_t_clear_forks(struct sip_msg* msg, char* _foo, char* _bar )
-{
-	return t_clear_forks();
-}
-
-
-inline static int w_t_relay_to( struct sip_msg  *p_msg , 
-						 char *proxy, /* struct proxy_l *proxy expected */
-						 char *_foo       /* nothing expected */ )
-{
-	return t_relay_to( p_msg, ( struct proxy_l *) proxy );
-}
-
-static int t_relay_to( struct sip_msg  *p_msg , struct proxy_l *proxy )
-{
-	int ret;
-	int new_tran;
-	char err_buffer[128];
-	int sip_err;
-	int reply_ret;
-
-	ret=0;
-
-	new_tran = t_newtran( p_msg );
-
-	/* parsing error, memory alloc, whatever ... return -1;
-	   note -- this is a difference to writing a real script --
-	   we translate t_newtran 0 error to a negative value allowign
-	   some error handling; a script breaks on 0
-    */
-	if (new_tran==0) {
-		ret=E_UNSPEC;
-		goto done;
-	}
-
-	/* nothing new -- it is an existing transaction */
-	if (new_tran==-1) {
-		if ( p_msg->REQ_METHOD==METHOD_ACK) {
-			DBG( "SER: ACK received -> t_release\n");
-			if ( !t_release_transaction( p_msg ) )
-			{
-				DBG( "SER: WARNING: bad t_release\n");
-			}
-			/* ack fwd-ing not needed -- only hbh ACK
-			   match (new_tran==1) and do not need to
-			   be fwd-ed */
-			ret = 1;
-		} else { /* non-ACK -- retransmit */
-			ret=t_retransmit_reply( p_msg );
-			/* look at ret for better debugging output ... */
-			if (ret>0) DBG("DEBUG: reply retransmitted (status %d)\n", ret);
-			else if (ret==-1) DBG("DEBUG: no reply to retransmit; "
-				"probably a non-INVITE transaction which was not replied\n");
-			else LOG(L_ERR, "ERROR: reply retranmission failed\n");
-			/* do not worry and proceed whatever happened to you...*/
-			ret = 1;
-		}
-	} else { /* it is a new transaction */
-		if ( p_msg->REQ_METHOD==METHOD_ACK) {
-			/* ACKs does not establish a transaction and is
-			   fwd-ed statelessly */
-			DBG( "SER: forwarding ACK  statelessly \n");
-			ret=forward_request( p_msg , proxy ) ;
-		} else {
-			ret=t_forward_nonack(p_msg, proxy);
-			if (ret<=0)
-			{
-				DBG( "SER:ERROR: t_forward \n");
-				/* we reply statefuly and enter WAIT state since error might 
-				   have occured in middle of forking and we do not 
-				   want to put the forking burden on upstream client;
-				   howver, it may fail too due to lack of memory */
-				err2reason_phrase( ser_error, &sip_err,
-					err_buffer, sizeof(err_buffer), "TM" );
-				reply_ret=t_send_reply( p_msg , sip_err, err_buffer,0);
-				t_release_transaction( p_msg );
-				if (reply_ret>0) {
-					/* we have taken care of all -- do nothing in
-				  	script */
-					DBG("ERROR: generation of a stateful reply "
-						"on error succeeded\n");
-					ret=0;
-				}  else {
-					DBG("ERROR: generation of a stateful reply "
-						"on error failed\n");
-				};
-			} else { /* let upstream know, I forwarded downstream */
-				if ( p_msg->REQ_METHOD==METHOD_CANCEL)
-				{
-					DBG( "SER: new CANCEL\n");
-					if ( !t_send_reply( p_msg , 200, "glad to cancel", 0) )
-						DBG( "SER:ERROR: t_send_reply\n");
-				} else if (p_msg->REQ_METHOD==METHOD_INVITE)
-				{
-					DBG( "SER: new INVITE\n");
-					if (!t_send_reply( p_msg , 100 ,
-					"trying -- your call is important to us",0))
-						DBG("SER: ERROR: t_send_reply (100)\n");
-				} else {
-					DBG( "SER: new transaction\n");
-				}
-			} /* forward_nonack succeeded */
-		} /* a new non-ACK trancation */
-	} /* a new transaction */
-
-
-done:
-	if (T!=T_UNDEFINED && T!=T_NULL) {
-		T_UNREF( T );
-	}
-	return ret;
-}
-
-
-static int t_relay( struct sip_msg  *p_msg , char* _foo , char* _bar )
-{
-	str           *uri;
-	struct proxy_l *p;
-	int ret;
-
-	/* the original uri has been changed? */
-	if (p_msg->new_uri.s==0 || p_msg->new_uri.len==0)
-		uri = &(p_msg->first_line.u.request.uri);
-	else
-		uri = &(p_msg->new_uri);
-
-	p=uri2proxy( uri );
-	if (p==0) return E_BAD_ADDRESS;
-
-	/* relay now */
-	ret=t_relay_to( p_msg, p );
-	free_proxy( p );
-	free( p );
-	return ret;
-}
-

+ 22 - 10
modules/tm/tm_load.c

@@ -3,28 +3,40 @@
  */
 
 #include "tm_load.h"
+#include "uac.h"
+
+#define LOAD_ERROR "ERROR: tm_bind: TM module function "
 
 int load_tm( struct tm_binds *tmb)
 {
-	if (!( tmb->register_tmcb=(register_tmcb_f) find_export("register_tmcb", 2)) ) {
-		LOG(L_ERR, "ERROR: tm_bind: TM module function 'register_tmcb' not found\n");
+	if (!( tmb->register_tmcb=(register_tmcb_f) 
+		find_export("register_tmcb", NO_SCRIPT)) ) {
+		LOG(L_ERR, LOAD_ERROR "'register_tmcb' not found\n");
 		return -1;
 	}
 
-	if (!( tmb->t_relay_to=find_export("t_relay_to", 2)) ) {
-		LOG(L_ERR, "ERROR: tm_bind: TM module function 't_relay_to' not found\n");
+	if (!( tmb->t_relay_to=find_export(T_RELAY_TO, 2)) ) {
+		LOG(L_ERR, LOAD_ERROR "'t_relay_to' not found\n");
+		return -1;
+	}
+	if (!( tmb->t_relay=find_export(T_RELAY, 0)) ) {
+		LOG(L_ERR, LOAD_ERROR "'t_relay' not found\n");
+		return -1;
+	}
+	if (!(tmb->t_uac=(tuac_f)find_export(T_UAC, NO_SCRIPT)) ) {
+		LOG( L_ERR, LOAD_ERROR "'t_uac' not found\n");
 		return -1;
 	}
-	if (!( tmb->t_relay=find_export("t_relay", 0)) ) {
-		LOG(L_ERR, "ERROR: tm_bind: TM module function 't_relay' not found\n");
+	if (!(tmb->t_reply=(treply_f)find_export(T_REPLY, 2)) ) {
+		LOG( L_ERR, LOAD_ERROR "'t_reply' not found\n");
 		return -1;
 	}
-	if (!( tmb->t_fork_to_uri=find_export("t_fork_to_uri", 1)) ) {
-		LOG(L_ERR, "ERROR: tm_bind: TM module function 't_fork_to_uri' not found\n");
+	if (!(tmb->t_reply_unsafe=(treply_f)find_export(T_REPLY_UNSAFE, 2)) ) {
+		LOG( L_ERR, LOAD_ERROR "'t_reply_unsafe' not found\n");
 		return -1;
 	}
-	if (!( tmb->t_fork_on_no_response=find_export("t_fork_on_no_response", 1)) ) {
-		LOG(L_ERR, "ERROR: tm_bind: TM module function 't_fork_on_no_response' not found\n");
+	if (!(tmb->t_forward_nonack=(tfwd_f)find_export(T_FORWARD_NONACK , 2)) ) {
+		LOG( L_ERR, LOAD_ERROR "'t_forward_nonack' not found\n");
 		return -1;
 	}
 

+ 19 - 9
modules/tm/tm_load.h

@@ -7,20 +7,30 @@
 
 #include "../../sr_module.h"
 #include "t_hooks.h"
+#include "uac.h"
+#include "t_fwd.h"
+#include "t_reply.h"
 
-struct tm_binds {
-	register_tmcb_f	register_tmcb;
+/* export not usable from scripts */
+#define NO_SCRIPT	-1
+
+#define T_RELAY_TO "t_relay_to"
+#define T_RELAY "t_relay"
+#define T_UAC "t_uac"
+#define T_REPLY "t_reply"
+#define T_REPLY_UNSAFE "t_reply_unsafe"
+#define T_FORWARD_NONACK "t_forward_nonack"
 
-/*
-	cmd_function	t_isflagset;
-	cmd_function	t_setflag;
-	cmd_function	t_resetflag;
-*/
 
+
+struct tm_binds {
+	register_tmcb_f	register_tmcb;
 	cmd_function	t_relay_to;
 	cmd_function 	t_relay;
-	cmd_function	t_fork_to_uri;
-	cmd_function	t_fork_on_no_response;
+	tuac_f			t_uac;
+	treply_f		t_reply;
+	treply_f		t_reply_unsafe;
+	tfwd_f			t_forward_nonack;
 };
 
 

+ 463 - 0
modules/tm/tm_mod.c

@@ -0,0 +1,463 @@
+/*
+ * $Id$
+ *
+ * TM module
+ *
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <netdb.h>
+
+#include "../../sr_module.h"
+#include "../../dprint.h"
+#include "../../error.h"
+#include "../../ut.h"
+#include "../../script_cb.h"
+#include "../../fifo_server.h"
+
+#include "sip_msg.h"
+#include "h_table.h"
+#include "t_funcs.h"
+#include "t_hooks.h"
+#include "tm_load.h"
+#include "ut.h"
+#include "t_reply.h"
+#include "uac.h"
+#include "t_fwd.h"
+#include "t_lookup.h"
+
+
+
+inline static int w_t_check(struct sip_msg* msg, char* str, char* str2);
+inline static int w_t_reply(struct sip_msg* msg, char* str, char* str2);
+inline static int w_t_reply_unsafe(struct sip_msg* msg, char* str, char* str2);
+inline static int w_t_release(struct sip_msg* msg, char* str, char* str2);
+inline static int fixup_t_send_reply(void** param, int param_no);
+inline static int fixup_str2int( void** param, int param_no);
+inline static int w_t_retransmit_reply(struct sip_msg* p_msg, char* foo, char* bar );
+inline static int w_t_newtran(struct sip_msg* p_msg, char* foo, char* bar );
+inline static int w_t_newdlg( struct sip_msg* p_msg, char* foo, char* bar );
+inline static int w_t_relay( struct sip_msg  *p_msg , char *_foo, char *_bar);
+inline static int w_t_relay_to( struct sip_msg  *p_msg , char *proxy, char *);
+inline static int w_t_replicate( struct sip_msg  *p_msg , 
+	char *proxy, /* struct proxy_l *proxy expected */
+	char *_foo       /* nothing expected */ );
+inline static int w_t_forward_nonack(struct sip_msg* msg, char* str, char* );
+inline static int fixup_hostport2proxy(void** param, int param_no);
+inline static int w_t_on_negative( struct sip_msg* msg, char *go_to, char *foo );
+
+
+static int mod_init(void);
+
+static int child_init(int rank);
+
+
+#ifdef STATIC_TM
+struct module_exports tm_exports = {
+#else
+struct module_exports exports= {
+#endif
+	"tm",
+	/* -------- exported functions ----------- */
+	(char*[]){			
+				"t_newtran",
+				"t_lookup_request",
+				T_REPLY,
+				T_REPLY_UNSAFE,
+				"t_retransmit_reply",
+				"t_release",
+				T_RELAY_TO,
+				"t_replicate",
+				T_RELAY,
+				T_FORWARD_NONACK,
+				"t_on_negative",
+
+				/* not applicable from script ... */
+
+				"register_tmcb",
+				T_UAC,
+				"load_tm",
+				"t_newdlg"
+			},
+	(cmd_function[]){
+					w_t_newtran,
+					w_t_check,
+					w_t_reply,
+					w_t_reply_unsafe,
+					w_t_retransmit_reply,
+					w_t_release,
+					w_t_relay_to,
+					w_t_replicate,
+					w_t_relay,
+					w_t_forward_nonack,
+					w_t_on_negative,
+
+					(cmd_function) register_tmcb,
+					(cmd_function) t_uac,
+					(cmd_function) load_tm,
+					w_t_newdlg,
+					},
+	(int[]){
+				0, /* t_newtran */
+				0, /* t_lookup_request */
+				2, /* t_reply */
+				2, /* t_reply_unsafe */
+				0, /* t_retransmit_reply */
+				0, /* t_release */
+				2, /* t_relay_to */
+				2, /* t_replicate */
+				0, /* t_relay */
+				2, /* t_forward_nonack */
+				1, /* t_on_negative */
+				NO_SCRIPT /* register_tmcb */,
+				NO_SCRIPT /* t_uac */,
+				NO_SCRIPT /* load_tm */,
+				0 /* t_newdlg */
+			},
+	(fixup_function[]){
+				0,						/* t_newtran */
+				0,						/* t_lookup_request */
+				fixup_t_send_reply,		/* t_reply */
+				fixup_t_send_reply,		/* t_reply_unsafe */
+				0,						/* t_retransmit_reply */
+				0,						/* t_release */
+				fixup_hostport2proxy,	/* t_relay_to */
+				fixup_hostport2proxy,	/* t_replicate */
+				0,						/* t_relay */
+				fixup_hostport2proxy,	/* t_forward_nonack */
+				fixup_str2int,			/* t_on_negative */
+				0,						/* register_tmcb */
+				0,						/* t_uac */
+				0,						/* load_tm */
+				0						/* t_newdlg */
+	
+		},
+	15,
+
+	/* ------------ exported variables ---------- */
+	(char *[]) { /* Module parameter names */
+		"fr_timer",
+		"fr_inv_timer",
+		"wt_timer",
+		"delete_timer",
+		"retr_timer1p1",
+		"retr_timer1p2",
+		"retr_timer1p3",
+		"retr_timer2",
+		"noisy_ctimer",
+		"uac_from"
+	},
+	(modparam_t[]) { /* variable types */
+		INT_PARAM, /* fr_timer */
+		INT_PARAM, /* fr_inv_timer */
+		INT_PARAM, /* wt_timer */
+		INT_PARAM, /* delete_timer */
+		INT_PARAM,/* retr_timer1p1 */
+		INT_PARAM, /* retr_timer1p2 */
+		INT_PARAM, /* retr_timer1p3 */
+		INT_PARAM, /* retr_timer2 */
+		INT_PARAM, /* noisy_ctimer */
+		STR_PARAM, /* uac_from */
+	},
+	(void *[]) { /* variable pointers */
+		&(timer_id2timeout[FR_TIMER_LIST]),
+		&(timer_id2timeout[FR_INV_TIMER_LIST]),
+		&(timer_id2timeout[WT_TIMER_LIST]),
+		&(timer_id2timeout[DELETE_LIST]),
+		&(timer_id2timeout[RT_T1_TO_1]),
+		&(timer_id2timeout[RT_T1_TO_2]),
+		&(timer_id2timeout[RT_T1_TO_3]),
+		&(timer_id2timeout[RT_T2]),
+		&noisy_ctimer,
+		&uac_from
+	},
+	11,      /* Number of module paramers */
+
+	mod_init, /* module initialization function */
+	(response_function) t_on_reply,
+	(destroy_function) tm_shutdown,
+	0, /* w_onbreak, */
+	child_init /* per-child init function */
+};
+
+inline static int fixup_str2int( void** param, int param_no)
+{
+	unsigned int go_to;
+	int err;
+
+	if (param_no==1) {
+		go_to=str2s(*param, strlen(*param), &err );
+		if (err==0) {
+			free(*param);
+			*param=(void *)go_to;
+			return 0;
+		} else {
+			LOG(L_ERR, "ERROR: fixup_str2int: bad number <%s>\n",
+				(char *)(*param));
+			return E_CFG;
+		}
+	}
+	return 0;
+}
+
+static int w_t_unref( struct sip_msg *foo, void *bar)
+{
+	return t_unref(foo);
+}
+
+static int script_init( struct sip_msg *foo, void *bar)
+{   
+	/* we primarily reset all private memory here to make sure
+	   private values left over from previous message will
+	   not be used again
+    */
+
+	/* make sure the new message will not inherit previous
+	   message's t_on_negative value
+	*/
+	t_on_negative( 0 );
+
+	return 1;
+}
+
+static int mod_init(void)
+{
+
+	DBG( "TM - initializing...\n");
+	/* checking if we have sufficient bitmap capacity for given
+	   maximum number of  branches */
+	if (1<<(MAX_BRANCHES+1)>UINT_MAX) {
+		LOG(L_CRIT, "Too many max UACs for UAC branch_bm_t bitmap: %d\n",
+			MAX_BRANCHES );
+		return -1;
+	}
+	if (register_fifo_cmd(fifo_uac, "t_uac", 0)<0) {
+		LOG(L_CRIT, "cannot register fifo uac\n");
+		return -1;
+	}
+
+	if (tm_startup()==-1) return -1;
+	uac_init();
+	register_tmcb( TMCB_ON_NEGATIVE, on_negative_reply, 0 /* empty param */);
+    /* register the timer function */
+    register_timer( timer_routine , hash_table , 1 );
+    /* register post-script clean-up function */
+    register_script_cb( w_t_unref, POST_SCRIPT_CB, 0 /* empty param */ );
+    register_script_cb( script_init, PRE_SCRIPT_CB , 0 /* empty param */ );
+	return 0;
+}
+
+static int child_init(int rank) {
+	uac_child_init(rank);
+	return 1;
+}
+
+
+/* (char *hostname, char *port_nr) ==> (struct proxy_l *, -)  */
+
+inline static int fixup_hostport2proxy(void** param, int param_no)
+{
+	unsigned int port;
+	char *host;
+	int err;
+	struct proxy_l *proxy;
+	
+	DBG("TM module: fixup_t_forward(%s, %d)\n", (char*)*param, param_no);
+	if (param_no==1){
+		DBG("TM module: fixup_t_forward: param 1.. do nothing, wait for #2\n");
+		return 0;
+	} else if (param_no==2) {
+
+		host=(char *) (*(param-1)); 
+		port=str2s(*param, strlen(*param), &err);
+		if (err!=0) {
+			LOG(L_ERR, "TM module:fixup_t_forward: bad port number <%s>\n",
+				(char*)(*param));
+			 return E_UNSPEC;
+		}
+		proxy=mk_proxy(host, port);
+		if (proxy==0) {
+			LOG(L_ERR, "ERROR: fixup_t_forwardv6: bad host name in URI <%s>\n",
+				host );
+			return E_BAD_ADDRESS;
+		}
+		/* success -- fix the first parameter to proxy now ! */
+		free( *(param-1));
+		*(param-1)=proxy;
+		return 0;
+	} else {
+		LOG(L_ERR, "ERROR: fixup_t_forwardv6 called with parameter #<>{1,2}\n");
+		return E_BUG;
+	}
+}
+
+
+/* (char *code, char *reason_phrase)==>(int code, r_p as is) */
+inline static int fixup_t_send_reply(void** param, int param_no)
+{
+	unsigned int code;
+	int err;
+
+	if (param_no==1){
+		code=str2s(*param, strlen(*param), &err);
+		if (err==0){
+			free(*param);
+			*param=(void*)code;
+			return 0;
+		}else{
+			LOG(L_ERR, "TM module:fixup_t_send_reply: bad  number <%s>\n",
+					(char*)(*param));
+			return E_UNSPEC;
+		}
+	}
+	/* second param => no conversion*/
+	return 0;
+}
+
+
+
+
+inline static int w_t_check(struct sip_msg* msg, char* str, char* str2)
+{
+	return t_check( msg , 0  ) ? 1 : -1;
+}
+
+
+
+inline static int w_t_forward_nonack(struct sip_msg* msg, char* proxy, char* _foo)
+{
+	struct cell *t;
+	if (t_check( msg , 0 )==-1) return -1;
+	t=get_t();
+	if ( t && t!=T_UNDEFINED ) {
+		if (msg->REQ_METHOD==METHOD_ACK) {
+			LOG(L_WARN,"WARNING: you don't really want to fwd hbh ACK\n");
+			return -1;
+		}
+		return t_forward_nonack(t, msg, ( struct proxy_l *) proxy );
+	} else {
+		DBG("DEBUG: t_forward_nonack: no transaction found\n");
+		return -1;
+	}
+}
+
+
+
+inline static int w_t_reply(struct sip_msg* msg, char* str, char* str2)
+{
+	struct cell *t;
+
+	if (msg->REQ_METHOD==METHOD_ACK) {
+		LOG(L_WARN, "WARNING: t_reply: ACKs are not replied\n");
+		return -1;
+	}
+	if (t_check( msg , 0 )==-1) return -1;
+	t=get_t();
+	if (!t) {
+		LOG(L_ERR, "ERROR: t_reply: cannot send a t_reply to a message "
+			"for which no T-state has been established\n");
+		return -1;
+	}
+	return t_reply( t, msg, (unsigned int) str, str2);
+}
+
+
+inline static int w_t_reply_unsafe(struct sip_msg* msg, char* str, char* str2)
+{
+	struct cell *t;
+
+	if (msg->REQ_METHOD==METHOD_ACK) {
+		LOG(L_WARN, "WARNING: t_reply: ACKs are not replied\n");
+		return -1;
+	}
+	if (t_check( msg , 0 )==-1) return -1;
+	t=get_t();
+	if (!t) {
+		LOG(L_ERR, "ERROR: t_reply: cannot send a t_reply to a message "
+			"for which no T-state has been established\n");
+		return -1;
+	}
+	return t_reply_unsafe(t, msg, (unsigned int) str, str2);
+}
+
+
+inline static int w_t_release(struct sip_msg* msg, char* str, char* str2)
+{
+	struct cell *t;
+	if (t_check( msg  , 0  )==-1) return -1;
+	t=get_t();
+	if ( t && t!=T_UNDEFINED ) 
+		return t_release_transaction( t );
+	return 1;
+}
+
+
+
+
+inline static int w_t_retransmit_reply( struct sip_msg* p_msg, char* foo, char* bar)
+{
+	struct cell *t;
+
+
+	if (t_check( p_msg  , 0 )==-1) 
+		return 1;
+	t=get_t();
+	if (t) {
+		if (p_msg->REQ_METHOD==METHOD_ACK) {
+			LOG(L_WARN, "WARNING: : ACKs ansmit_replies not replied\n");
+			return -1;
+		}
+		return t_retransmit_reply( t );
+	} else 
+		return -1;
+	return 1;
+}
+
+
+
+
+
+inline static int w_t_newdlg( struct sip_msg* p_msg, char* foo, char* bar ) 
+{
+	return t_newdlg( p_msg );
+}
+
+inline static int w_t_newtran( struct sip_msg* p_msg, char* foo, char* bar ) 
+{
+	/* t_newtran returns 0 on error (negative value means
+	   'transaction exists'
+	*/
+	return t_newtran( p_msg );
+}
+
+
+inline static int w_t_on_negative( struct sip_msg* msg, char *go_to, char *foo )
+{
+	return t_on_negative( (unsigned int ) go_to );
+}
+
+inline static int w_t_relay_to( struct sip_msg  *p_msg , 
+	char *proxy, /* struct proxy_l *proxy expected */
+	char *_foo       /* nothing expected */ )
+{
+	return t_relay_to( p_msg, ( struct proxy_l *) proxy,
+	0 /* no replication */ );
+}
+
+inline static int w_t_replicate( struct sip_msg  *p_msg , 
+	char *proxy, /* struct proxy_l *proxy expected */
+	char *_foo       /* nothing expected */ )
+{
+	return t_replicate(p_msg, ( struct proxy_l *) proxy );
+}
+
+inline static int w_t_relay( struct sip_msg  *p_msg , 
+						char *_foo, char *_bar)
+{
+	return t_relay_to( p_msg, 
+		(struct proxy_l *) 0 /* no proxy */,
+		0 /* no replication */ );
+}
+
+

+ 300 - 0
modules/tm/uac.c

@@ -0,0 +1,300 @@
+/*
+ * $Id$
+ *
+ * simple UAC for things such as SUBSCRIBE or SMS gateway;
+ * no authentication and other UAC features -- just send
+ * a message, retransmit and await a reply; forking is not
+ * supported during client generation, in all other places
+ * it is -- adding it should be simple
+ */
+
+#include <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <signal.h>
+#include "../../dprint.h"
+#include "../../ut.h"
+#include "../../hash_func.h"
+#include "../../md5utils.h"
+#include "../../mem/mem.h"
+#include "../../fifo_server.h"
+#include "../../error.h"
+#include "t_funcs.h"
+#include "config.h"
+#include "sip_msg.h"
+#include "ut.h"
+#include "t_msgbuilder.h"
+#include "uac.h"
+
+/* Call-ID has the following form: call_id_rand-pid-seq */
+
+char call_id[RAND_DIGITS+1+MAX_PID_LEN+1+MAX_SEQ_LEN+1];
+static unsigned long callid_seq;
+
+char *uac_from="\"UAC Account\" <sip:[email protected]:9>";
+
+char from_tag[ MD5_LEN +1];
+
+void uac_init() {
+	unsigned long init_nr;
+	char *c;
+	int len;
+
+	str src[3];
+
+	init_nr=random() % (1<<(RAND_DIGITS*4));
+	c=call_id;
+	len=RAND_DIGITS;
+	int2reverse_hex( &c, &len, init_nr );
+	while (len) { *c='z'; len--; c++; }
+	*c='-';
+
+	src[0].s="Long live SER server";
+	src[0].len=strlen(src[0].s);
+	src[1].s=sock_info[0].address_str.s;
+	src[1].len=strlen(src[1].s);
+	src[2].s=sock_info[0].port_no_str.s;
+	src[2].len=strlen(src[2].s);
+
+	MDStringArray( from_tag, src, 3 );
+	from_tag[MD5_LEN]=0;
+}
+
+
+void uac_child_init( int rank ) {
+	int pid_nr;
+	char *c;
+	int len;
+
+	pid_nr=getpid() % (1<<(MAX_PID_LEN*4));
+	c=call_id+RAND_DIGITS+1;
+	len=MAX_PID_LEN;
+	int2reverse_hex( &c, &len, pid_nr );
+	while (len) { *c='z'; len--; c++; }
+	*c='-';
+
+	callid_seq=random() % TABLE_ENTRIES;
+
+}
+
+void generate_callid() {
+	char *c;
+	int len;
+
+	/* HACK: not long enough */
+	callid_seq = (callid_seq+1) % TABLE_ENTRIES;
+	c=call_id+RAND_DIGITS+1+MAX_PID_LEN+1;
+	len=MAX_SEQ_LEN;
+	int2reverse_hex( &c, &len, callid_seq );
+	while (len) { *c='z'; len--; c++; }
+}
+
+
+
+int t_uac( str *msg_type, str *dst, 
+	str *headers, str *body, transaction_cb completion_cb,
+	void *cbp, struct dialog *dlg)
+{
+
+	struct cell *new_cell;
+	struct proxy_l *proxy;
+	int branch;
+	int ret;
+	int req_len;
+	char *buf;
+	union sockaddr_union to;
+	struct socket_info* send_sock;
+	struct retr_buf *request;
+
+	/* be optimist -- assume success for return value */
+	ret=1;
+
+	proxy=uri2proxy( dst );
+	if (proxy==0) {
+		ser_error=ret=E_BAD_ADDRESS;
+		LOG(L_ERR, "ERROR: t_uac: can't create a dst proxy\n");
+		goto done;
+	}
+	branch=0;
+	/* might go away -- we ignore it in send_pr_buffer anyway */
+	/* T->uac[branch].request.to_len=sizeof(union sockaddr_union); */
+	hostent2su(&to, &proxy->host, proxy->addr_idx, 
+		(proxy->port)?htons(proxy->port):htons(SIP_PORT));
+	send_sock=get_send_socket( &to );
+	if (send_sock==0) {
+		LOG(L_ERR, "ERROR: t_uac: no corresponding listening socket "
+			"for af %d\n", to.s.sa_family );
+		ret=E_NO_SOCKET;
+		goto error00;
+	}
+	generate_callid();
+
+	new_cell = build_cell( NULL ) ; 
+	if (!new_cell) {
+		ret=E_OUT_OF_MEM;
+		LOG(L_ERR, "ERROR: t_uac: short of cell shmem\n");
+		goto error00;
+	}
+	new_cell->completion_cb=completion_cb;
+	new_cell->cbp=cbp;
+	/* cbp is installed -- tell error handling bellow not to free it */
+	cbp=0;
+	new_cell->is_invite=msg_type->len==INVITE_LEN 
+		&& memcmp(msg_type->s, INVITE, INVITE_LEN)==0;
+	new_cell->local=1;
+	LOCK_HASH(new_cell->hash_index);
+	insert_into_hash_table_unsafe( hash_table , new_cell );
+	UNLOCK_HASH(new_cell->hash_index);
+
+	request=&new_cell->uac[branch].request;
+	request->to=to;
+	request->send_sock=send_sock;
+
+	buf=build_uac_request(  *msg_type, *dst, *headers, *body, branch,
+		new_cell /* t carries hash_index, label, md5, uac[].send_sock and
+		     other pieces of information needed to print a message*/
+		, &req_len );
+    if (!buf) {
+        ret=E_OUT_OF_MEM;
+        LOG(L_ERR, "ERROR: t_uac: short of req shmem\n");
+        goto error01;
+    }      
+	new_cell->method.s=buf;new_cell->method.len=msg_type->len;
+
+	request->buffer = buf;
+	request->buffer_len = req_len;
+	new_cell->nr_of_outgoings++;
+
+	proxy->tx++;
+	proxy->tx_bytes+=req_len;
+
+	if (SEND_BUFFER( request)==-1) {
+		LOG(L_ERR, "ERROR: t_uac: UAC sending to %.*s failed\n",
+			dst->len, dst->s );
+		proxy->errors++;
+		proxy->ok=0;
+		ser_error=ret=E_SEND;
+		goto error01;
+	}
+	start_retr( request );
+
+	/* success */
+	goto done;
+
+error01: 
+	/* this is the safest way (though not the cheapest) to
+	   make a transaction disappear; we may appreciate the
+	   safety later when we add more complexity
+	*/
+	cleanup_uac_timers(new_cell);
+	put_on_wait(new_cell);
+error00:
+	free_proxy( proxy );
+	free( proxy );
+done: 
+	/* if we did not install cbp, release it now */
+	if (cbp) shm_free(cbp);
+	return ser_error=ret;
+}
+
+static void fifo_callback( struct cell *t, struct sip_msg *msg,
+	int code, void *param)
+{
+
+	char *filename;
+	int file;
+	int r;
+	str text;
+
+	DBG("DEBUG: fifo UAC completed with status %d\n", code);
+	if (t->cbp) {
+		filename=(char *)(t->cbp);
+		file=open(filename, O_WRONLY);
+		if (file<0) {
+			LOG(L_ERR, "ERROR: fifo_callback: can't open file %s: %s\n",
+				filename, strerror(errno));
+			return;
+		}
+		get_reply_status(&text,msg,code);
+		if (text.s==0) {
+			LOG(L_ERR, "ERROR: fifo_callback: get_reply_status failed\n");
+			return;
+		}
+		r=write(file, text.s , text.len );
+		close(file);
+		pkg_free(text.s);
+		if (r<0) {
+			LOG(L_ERR, "ERROR: fifo_callback: write error: %s\n",
+				strerror(errno));
+			return;	
+		}
+	} else {
+		LOG(L_INFO, "INFO: fifo UAC completed with status %d\n", code);
+	}
+}	
+
+int fifo_uac( FILE *stream, char *response_file ) 
+{
+	char method[MAX_METHOD];
+	char header[MAX_HEADER];
+	char body[MAX_BODY];
+	char dst[MAX_DST];
+	str sm, sh, sb, sd;
+	char *shmem_file;
+	int fn_len;
+
+	sm.s=method; sh.s=header; sb.s=body; sd.s=dst;
+	while(1) {
+		if (!read_line(method, MAX_METHOD, stream,&sm.len)||sm.len==0) {
+			/* line breaking must have failed -- consume the rest
+			   and proceed to a new request
+			*/
+			LOG(L_ERR, "ERROR: fifo_uac: method expected\n");
+			return -1;
+		}
+		DBG("DEBUG: fifo_uac: method: %.*s\n", sm.len, method );
+		if (!read_line(dst, MAX_DST, stream, &sd.len)||sd.len==0) {
+			LOG(L_ERR, "ERROR: fifo_uac: destination expected\n");
+			return -1;
+		}
+		DBG("DEBUG: fifo_uac:  dst: %.*s\n", sd.len, dst );
+		/* now read header fields line by line */
+		if (!read_line_set(header, MAX_HEADER, stream, &sh.len)) {
+			LOG(L_ERR, "ERROR: fifo_uac: header fields expected\n");
+			return -1;
+		}
+		DBG("DEBUG: fifo_uac: header: %.*s\n", sh.len, header );
+		/* and eventually body */
+		if (!read_line_set(body, MAX_BODY, stream, &sb.len)) {
+			LOG(L_ERR, "ERROR: fifo_uac: body expected\n");
+			return -1;
+		}
+		DBG("DEBUG: fifo_uac: body: %.*s\n", sb.len, body );
+		DBG("DEBUG: fifo_uac: EoL -- proceeding to transaction creation\n");
+		/* we got it all, initiate transaction now! */
+		if (response_file) {
+			fn_len=strlen(response_file)+1;
+			shmem_file=shm_malloc(fn_len);
+			if (shmem_file==0) {
+				LOG(L_ERR, "ERROR: fifo_uac: no shmem\n");
+				return -1;
+			}
+			memcpy(shmem_file, response_file, fn_len );
+		} else {
+			shmem_file=0;
+		}
+		/* HACK: there is yet a shortcoming -- if t_uac fails, callback
+		   will not be triggered and no feedback will be printed
+		   to shmem_file
+		*/
+		t_uac(&sm,&sd,&sh,&sb,fifo_callback,shmem_file,0 /* no dialog */);
+		return 1;
+
+	}
+}
+

+ 53 - 0
modules/tm/uac.h

@@ -0,0 +1,53 @@
+/*
+ * $Id$
+ *
+ */
+
+#ifndef _UAC_H
+#define _UAC_H
+
+#include <stdio.h>
+#include "config.h"
+#include "t_dlg.h"
+
+/* number of random digits in beginning of a string --
+   please multiples of 2 */
+#define RAND_DIGITS	6
+/* maximum size of pid in hex characters */
+#define MAX_PID_LEN	4
+/* maximum seq size in hex chars */
+#define MAX_SEQ_LEN (T_TABLE_POWER*2)
+
+extern char *uac_from;
+extern char *fifo;
+extern int fifo_mode;
+extern char call_id[RAND_DIGITS+1+MAX_PID_LEN+1+MAX_SEQ_LEN+1];
+extern char from_tag[ MD5_LEN +1];
+
+void uac_init();
+void uac_child_init( int rank );
+void generate_callid();
+
+typedef int (*tuac_f)(str *msg_type, str *dst, str *headers,str *body,
+	transaction_cb completion_cb );
+
+int t_uac( 
+	/* MESSAGE, OPTIONS, etc. */
+	str *msg_type,  
+	/* sip:foo@bar, will be put in r-uri and To */
+	str *dst,	
+	/* all other header fields separated by CRLF, including 
+	   Content-type if body attached, excluding HFs
+	   generated by UAC: To, Content_length, CSeq, Call-ID, Via, From
+	*/
+	str *headers, 
+	/* body of the message if any */
+	str *body,
+	/* completion callback (optional) */
+	transaction_cb completion_cb,
+	/* callback parameter */
+	void *cbp,
+	struct dialog *dlg );
+
+int fifo_uac( FILE *stream, char *response_file );
+#endif

+ 221 - 201
msg_translator.c

@@ -1,4 +1,5 @@
-/* $Id$
+/* 
+ * $Id$
  *
  */
 
@@ -19,15 +20,7 @@
 #include "data_lump_rpl.h"
 #include "ip_addr.h"
 #include "resolve.h"
-
-
-
-#define MAX_VIA_LINE_SIZE      240
-#define MAX_RECEIVED_SIZE  57
-
-/* mallocs for local stuff (not needed to be shared mem?)*/
-#define local_malloc(s) pkg_malloc((s))
-#define local_free(s)   pkg_free((s))
+#include "ut.h"
 
 
 #define append_str(_dest,_src,_len,_msg) \
@@ -99,101 +92,12 @@ int check_address(struct ip_addr* ip, char *name, int resolver)
 }
 
 
-char* via_builder( struct sip_msg *msg , unsigned int *len, 
-					struct socket_info* send_sock )
-{
-	unsigned int  via_len, branch_len, extra_len;
-	char               *line_buf;
-
-	line_buf=0;
-	extra_len=0;
-
-	line_buf=pkg_malloc(sizeof(char)*MAX_VIA_LINE_SIZE);
-	if (line_buf==0){
-		ser_error=E_OUT_OF_MEM;
-		LOG(L_ERR, "ERROR: via_builder: out of memory\n");
-		goto error;
-	}
-	via_len=MY_VIA_LEN+send_sock->address_str.len; /*space included in MY_VIA*/
-#ifdef USE_IPV6
-	if (send_sock->address.af==AF_INET6) via_len+=2; /* [ ]*/
-#endif
-
-	/* jku: if we compute branches using MD5 it will take 32 bytes */
-	branch_len= (loop_checks ? MY_BRANCH_LEN : MY_BRANCH_LEN -1 + MD5_LEN)+
-					msg->add_to_branch_len;
-
-	if ((via_len+send_sock->port_no_str.len+branch_len
-								+CRLF_LEN)<MAX_VIA_LINE_SIZE){
-		memcpy(line_buf, MY_VIA, MY_VIA_LEN);
-#ifdef USE_IPV6
-	if (send_sock->address.af==AF_INET6) {
-		line_buf[MY_VIA_LEN]='[';
-		line_buf[MY_VIA_LEN+1+send_sock->address_str.len]=']';
-		extra_len=1;
-	}
-#endif
-		memcpy(line_buf+MY_VIA_LEN+extra_len, send_sock->address_str.s,
-									send_sock->address_str.len);
-		if (send_sock->port_no!=SIP_PORT){
-			memcpy(line_buf+via_len, send_sock->port_no_str.s,
-									 send_sock->port_no_str.len);
-			via_len+=send_sock->port_no_str.len;
-		}
-
-		/* jku: branch parameter */
-		memcpy(line_buf+via_len, MY_BRANCH, MY_BRANCH_LEN );
-		via_len+=MY_BRANCH_LEN;
-		/* loop checks ?
-		if (loop_checks) {
-			if (check_transaction_quadruple( msg )) {
-				str src[5];
-				int r;
-				src[0]= msg->from->body;
-				src[1]= msg->to->body;
-				src[2]= msg->callid->body;
-				src[3]= msg->first_line.u.request.uri;
-				src[4]= get_cseq( msg )->number;
-				MDStringArray ( line_buf+via_len-1, src, 5 );
-				via_len+=MD5_LEN - 1;
-			} else DBG("DEBUG: via_builder: required HFs for "
-					"loop checking missing\n");
-		}  */
-		/* someone wants me to add something to branch here ? */
-		if ( msg->add_to_branch_len ){
-			memcpy(line_buf+via_len-1, msg->add_to_branch_s,
-				msg->add_to_branch_len );
-			via_len+=msg->add_to_branch_len-1;
-		}
-
-		memcpy(line_buf+via_len, CRLF, CRLF_LEN);
-		via_len+=CRLF_LEN;
-		line_buf[via_len]=0; /* null terminate the string*/
-	}else{
-		LOG(L_ERR, " ERROR: via_builder: via too long (%d)\n",
-				via_len);
-		ser_error=E_BUG;
-		goto error;
-	}
-
-	*len = via_len;
-	return line_buf;
-
-error:
-	if (line_buf) pkg_free(line_buf);
-	return 0;
-}
-
-
-
-
-#ifdef VERY_NOISY_REPLIES
 char * warning_builder( struct sip_msg *msg, unsigned int *returned_len)
 {
 	static char buf[MAX_WARNING_LEN];
 	static unsigned int fix_len=0;
 	str *foo;
-	char *p;
+	int print_len;
 
 	if (!fix_len)
 	{
@@ -209,42 +113,28 @@ char * warning_builder( struct sip_msg *msg, unsigned int *returned_len)
 		fix_len += 24;
 	}
 
-	p = buf+fix_len;
-	/* adding pid */
-	if (p-buf+10+2>=MAX_WARNING_LEN)
-		goto done;
-	p += sprintf(p, "pid=%d", pids?pids[process_no]:0 );
-	*(p++)=' ';
-
-	/*adding src_ip*/
-	if (p-buf+26+2>=MAX_WARNING_LEN)
-		goto done;
-	p += sprintf(p,"req_src_ip=%s",ip_addr2a(&msg->src_ip));
-	*(p++)=' ';
-
-	/*adding in_uri*/
-	if(p-buf+7+msg->first_line.u.request.uri.len+2>=MAX_WARNING_LEN)
-		goto done;
-	p += sprintf( p, "in_uri=%.*s",msg->first_line.u.request.uri.len,
-		msg->first_line.u.request.uri.s);
-	*(p++) = ' ';
-
 	/*adding out_uri*/
 	if (msg->new_uri.s)
 		foo=&(msg->new_uri);
 	else
 		foo=&(msg->first_line.u.request.uri);
-	if(p-buf+8+foo->len+2>=MAX_WARNING_LEN)
-		goto done;
-	p += sprintf( p, "out_uri=%.*s", foo->len, foo->s);
-
-done:
-	*(p++) = '\"';
-	*(p) = 0;
-	*returned_len = p-buf;
-	return buf;
+	print_len=snprintf(buf+fix_len, MAX_WARNING_LEN-fix_len,
+		"pid=%d req_src_ip=%s in_uri=%.*s out_uri=%.*s via_cnt%c=%d\"",
+		pids?pids[process_no]:0,
+		ip_addr2a(&msg->src_ip),
+		msg->first_line.u.request.uri.len, msg->first_line.u.request.uri.s,
+		foo->len, foo->s, 
+		msg->parsed_flag & HDR_EOH ? '=' : '>', /* should be = */
+		via_cnt );
+
+	if (print_len==-1) {
+		*returned_len=0;
+		return 0;
+	} else {
+		*returned_len=fix_len+print_len;
+		return buf;
+	}
 }
-#endif
 
 
 
@@ -265,7 +155,7 @@ char* received_builder(struct sip_msg *msg, int *received_len)
 	buf=pkg_malloc(sizeof(char)*MAX_RECEIVED_SIZE);
 	if (buf==0){
 		ser_error=E_OUT_OF_MEM;
-		LOG(L_ERR, "ERROR: build_req_buf_from_sip_req: out of memory\n");
+		LOG(L_ERR, "ERROR: received_builder: out of memory\n");
 		return 0;
 	}
 	/*
@@ -312,7 +202,7 @@ static inline int lumps_len(struct lump* l)
 					break;
 				default:
 					/* only ADD allowed for before/after */
-					LOG(L_CRIT, "BUG:build_req_buf_from_sip_req: invalid op "
+					LOG(L_CRIT, "BUG: lumps_len: invalid op "
 							"for data lump (%x)\n", r->op);
 			}
 		}
@@ -341,7 +231,7 @@ static inline int lumps_len(struct lump* l)
 				/* do nothing */
 				break;
 			default:
-				LOG(L_CRIT,"BUG:build_req_buf_from_sip_req: invalid"
+				LOG(L_CRIT,"BUG:lumps_len: invalid"
 							" op for data lump (%x)\n", r->op);
 		}
 		for (r=t->after;r;r=r->after){
@@ -351,7 +241,7 @@ static inline int lumps_len(struct lump* l)
 					break;
 				default:
 					/* only ADD allowed for before/after */
-					LOG(L_CRIT, "BUG:build_req_buf_from_sip_req: invalid"
+					LOG(L_CRIT, "BUG:lumps_len: invalid"
 								" op for data lump (%x)\n", r->op);
 			}
 		}
@@ -391,7 +281,7 @@ static /*inline*/ void process_lumps(	struct lump* l,	char* new_buf,
 							break;
 						default:
 							/* only ADD allowed for before/after */
-							LOG(L_CRIT, "BUG:build_req_buf_from_sip_req: "
+							LOG(L_CRIT, "BUG:process_lumps: "
 									"invalid op for data lump (%x)\n", r->op);
 					}
 				}
@@ -408,7 +298,7 @@ static /*inline*/ void process_lumps(	struct lump* l,	char* new_buf,
 							break;
 						default:
 							/* only ADD allowed for before/after */
-							LOG(L_CRIT, "BUG:build_req_buf_from_sip_req: "
+							LOG(L_CRIT, "BUG:process_lumps: "
 									"invalid op for data lump (%x)\n", r->op);
 					}
 				}
@@ -439,7 +329,7 @@ static /*inline*/ void process_lumps(	struct lump* l,	char* new_buf,
 							break;
 						default:
 							/* only ADD allowed for before/after */
-							LOG(L_CRIT, "BUG:build_req_buf_from_sip_req: "
+							LOG(L_CRIT, "BUG:process_lumps: "
 									"invalid op for data lump (%x)\n",r->op);
 					}
 				}
@@ -458,13 +348,13 @@ static /*inline*/ void process_lumps(	struct lump* l,	char* new_buf,
 							break;
 						default:
 							/* only ADD allowed for before/after */
-							LOG(L_CRIT, "BUG:build_req_buf_from_sip_req: "
+							LOG(L_CRIT, "BUG:process_lumps: "
 									"invalid op for data lump (%x)\n", r->op);
 					}
 				}
 				break;
 			default:
-					LOG(L_CRIT, "BUG: build_req_buf_from_sip_req: "
+					LOG(L_CRIT, "BUG: process_lumps: "
 							"unknown op (%x)\n", t->op);
 		}
 	}
@@ -487,6 +377,7 @@ char * build_req_buf_from_sip_req( struct sip_msg* msg,
 	char  backup;
 	unsigned int offset, s_offset, size;
 	struct lump* anchor;
+	int r;
 
 	uri_len=0;
 	orig=msg->orig;
@@ -497,27 +388,29 @@ char * build_req_buf_from_sip_req( struct sip_msg* msg,
 	received_buf=0;
 
 
-	line_buf = via_builder( msg, &via_len, send_sock);
+	line_buf = via_builder( &via_len, send_sock, 
+		msg->add_to_branch_s, msg->add_to_branch_len);
 	if (!line_buf){
 		LOG(L_ERR,"ERROR: build_req_buf_from_sip_req: no via received!\n");
-		goto error1;
+		goto error00;
 	}
 	/* check if received needs to be added */
 	backup = msg->via1->host.s[msg->via1->host.len];
 	msg->via1->host.s[msg->via1->host.len] = 0;
-	if (check_address(&msg->src_ip, msg->via1->host.s, received_dns)!=0){
+	r=check_address(&msg->src_ip, msg->via1->host.s, received_dns);
+	msg->via1->host.s[msg->via1->host.len] = backup;
+	if (r!=0){
 		if ((received_buf=received_builder(msg,&received_len))==0)
-			goto error1; /* free also line_buf */
+			goto error01;  /* free also line_buf */
 	}
-	msg->via1->host.s[msg->via1->host.len] = backup;
 
 	/* add via header to the list */
 	/* try to add it before msg. 1st via */
 	/* add first via, as an anchor for second via*/
 	anchor=anchor_lump(&(msg->add_rm), msg->via1->hdr.s-buf, 0, HDR_VIA);
-	if (anchor==0) goto error1;
+	if (anchor==0) goto error01;
 	if (insert_new_lump_before(anchor, line_buf, via_len, HDR_VIA)==0)
-		goto error1; /* free also line_buf*/
+		goto error01;
 	/* if received needs to be added, add anchor after host and add it */
 	if (received_len){
 		if (msg->via1->params.s){
@@ -535,9 +428,9 @@ char * build_req_buf_from_sip_req( struct sip_msg* msg,
 		}
 		anchor=anchor_lump(&(msg->add_rm),msg->via1->hdr.s-buf+size,0,
 				HDR_VIA);
-		if (anchor==0) goto error2; /* free also received_buf */
+		if (anchor==0) goto error02; /* free also line_buf */
 		if (insert_new_lump_after(anchor, received_buf, received_len, HDR_VIA)
-				==0 ) goto error2; /* free also received_buf */
+				==0 ) goto error02; /* free also line_buf */
 	}
 
 	/* compute new msg len and fix overlapping zones*/
@@ -547,11 +440,11 @@ char * build_req_buf_from_sip_req( struct sip_msg* msg,
 		uri_len=msg->new_uri.len;
 		new_len=new_len-msg->first_line.u.request.uri.len+uri_len;
 	}
-	new_buf=(char*)local_malloc(new_len+1);
+	new_buf=(char*)pkg_malloc(new_len+1);
 	if (new_buf==0){
 		ser_error=E_OUT_OF_MEM;
 		LOG(L_ERR, "ERROR: build_req_buf_from_sip_req: out of memory\n");
-		goto error;
+		goto error00;
 	}
 
 	offset=s_offset=0;
@@ -576,19 +469,16 @@ char * build_req_buf_from_sip_req( struct sip_msg* msg,
 	*returned_len=new_len;
 	return new_buf;
 
-error1:
-	if (line_buf) pkg_free(line_buf);
-error2:
+error01:
+	pkg_free(line_buf);
+error02:
 	if (received_buf) pkg_free(received_buf);
-error:
-	if (new_buf) local_free(new_buf);
+error00:
 	*returned_len=0;
 	return 0;
 }
 
 
-
-
 char * build_res_buf_from_sip_res( struct sip_msg* msg,
 				unsigned int *returned_len)
 {
@@ -617,7 +507,7 @@ char * build_res_buf_from_sip_res( struct sip_msg* msg,
 		via_len+=msg->via1->hdr.len+1;
 	}
 	/* remove the first via*/
-	if (del_lump( &(msg->repl_add_rm), via_offset, via_len, 0)==0){
+	if (del_lump( &(msg->repl_add_rm), via_offset, via_len, HDR_VIA)==0){
 		LOG(L_ERR, "build_res_buf_from_sip_res: error trying to remove first"
 					"via\n");
 		goto error;
@@ -625,10 +515,9 @@ char * build_res_buf_from_sip_res( struct sip_msg* msg,
 	new_len=len+lumps_len(msg->repl_add_rm);
 
 	DBG(" old size: %d, new size: %d\n", len, new_len);
-	new_buf=(char*)local_malloc(new_len+1);/* +1 is for debugging
-											(\0 to print it )*/
+	new_buf=(char*)pkg_malloc(new_len+1); /* +1 is for debugging (\0 to print it )*/
 	if (new_buf==0){
-		LOG(L_ERR, "ERROR: build_res_buf_from_sip_res: out of memory\n");
+		LOG(L_ERR, "ERROR: build_res_buf_from_sip_res: out of mem\n");
 		goto error;
 	}
 	new_buf[new_len]=0; /* debug: print the message */
@@ -644,7 +533,6 @@ char * build_res_buf_from_sip_res( struct sip_msg* msg,
 	*returned_len=new_len;
 	return new_buf;
 error:
-	if (new_buf) local_free(new_buf);
 	*returned_len=0;
 	return 0;
 }
@@ -666,29 +554,38 @@ char * build_res_buf_from_sip_req( unsigned int code, char *text,
 	char              backup;
 	char              *received_buf;
 	int               received_len;
-#ifdef VERY_NOISY_REPLIES
 	char              *warning;
 	unsigned int      warning_len;
-#endif
+	int r;
 
 	received_buf=0;
 	received_len=0;
 	buf=0;
+	/* make -Wall happy */
+	warning=0;
 
 	/* force parsing all headers -- we want to return all
 	Via's in the reply and they may be scattered down to the
 	end of header (non-block Vias are a really poor property
 	of SIP :( ) */
-	parse_headers( msg, HDR_EOH, 0 );
+	if (parse_headers( msg, HDR_EOH, 0 )==-1) {
+		LOG(L_ERR, "ERROR: build_res_buf_from_sip_req: "
+			"alas, parse_headers failed\n");
+		goto error00;
+	}
 
 	/* check if received needs to be added */
 	backup = msg->via1->host.s[msg->via1->host.len];
 	msg->via1->host.s[msg->via1->host.len] = 0;
-	if (check_address(&msg->src_ip, msg->via1->host.s, received_dns)!=0){
-		if ((received_buf=received_builder(msg,&received_len))==0)
-			goto error;
-	}
+	r=check_address(&msg->src_ip, msg->via1->host.s, received_dns);
 	msg->via1->host.s[msg->via1->host.len] = backup;
+	if (r!=0) {
+		if ((received_buf=received_builder(msg,&received_len))==0) {
+			LOG(L_ERR, "ERROR: build_res_buf_from_sip_req: "
+				"alas, received_builder failed\n");
+			goto error00;
+		}
+	}
 
 	/*computes the lenght of the new response buffer*/
 	len = 0;
@@ -718,25 +615,29 @@ char * build_res_buf_from_sip_req( unsigned int code, char *text,
 	/*lumps length*/
 	for(lump=msg->reply_lump;lump;lump=lump->next)
 		len += lump->text.len;
-#ifdef NOISY_REPLIES
-	/*server header*/
-	len += SERVER_HDR_LEN + CRLF_LEN;
-	/*content length header*/
-	len +=CONTENT_LEN_LEN + CRLF_LEN;
-#endif
-#ifdef VERY_NOISY_REPLIES
-	warning = warning_builder(msg,&warning_len);
-	len += warning_len + CRLF_LEN;
-#endif
+	if (server_signature) {
+		/*server header*/
+		len += SERVER_HDR_LEN + CRLF_LEN;
+		/*content length header*/
+		len +=CONTENT_LEN_LEN + CRLF_LEN;
+	}
+	if (sip_warning) {
+		warning = warning_builder(msg,&warning_len);
+		if (warning==0) {
+			LOG(L_ERR, "ERROR: warning too big\n");
+			goto error01;
+		}
+		len += warning_len + CRLF_LEN;
+	}
 	/* end of message */
 	len += CRLF_LEN; /*new line*/
 	/*allocating mem*/
-	buf = (char*) local_malloc( len+1 );
+	buf = (char*) pkg_malloc( len+1 );
 	if (!buf)
 	{
 		LOG(L_ERR, "ERROR: build_res_buf_from_sip_req: out of memory "
 			" ; needs %d\n",len);
-		goto error;
+		goto error01;
 	}
 
 	/* filling the buffer*/
@@ -800,32 +701,151 @@ char * build_res_buf_from_sip_req( unsigned int code, char *text,
 		memcpy(p,lump->text.s,lump->text.len);
 		p += lump->text.len;
 	}
-#ifdef NOISY_REPLIES
-	/*server header*/
-	memcpy( p, SERVER_HDR , SERVER_HDR_LEN );
-	p+=SERVER_HDR_LEN;
-	memcpy( p, CRLF, CRLF_LEN );
-	p+=CRLF_LEN;
-	/* content length header*/
-	memcpy( p, CONTENT_LEN , CONTENT_LEN_LEN );
-	p+=CONTENT_LEN_LEN;
-	memcpy( p, CRLF, CRLF_LEN );
-	p+=CRLF_LEN;
-#endif
-#ifdef VERY_NOISY_REPLIES
-	memcpy( p, warning, warning_len);
-	p+=warning_len;
-	memcpy( p, CRLF, CRLF_LEN);
-	p+=CRLF_LEN;
-#endif
+	if (server_signature) {
+		/*server header*/
+		memcpy( p, SERVER_HDR , SERVER_HDR_LEN );
+		p+=SERVER_HDR_LEN;
+		memcpy( p, CRLF, CRLF_LEN );
+		p+=CRLF_LEN;
+		/* content length header*/
+		memcpy( p, CONTENT_LEN , CONTENT_LEN_LEN );
+		p+=CONTENT_LEN_LEN;
+		memcpy( p, CRLF, CRLF_LEN );
+		p+=CRLF_LEN;
+	}
+	if (sip_warning) {
+		memcpy( p, warning, warning_len);
+		p+=warning_len;
+		memcpy( p, CRLF, CRLF_LEN);
+		p+=CRLF_LEN;
+	}
 	/*end of message*/
 	memcpy( p, CRLF, CRLF_LEN );
 	p+=CRLF_LEN;
 	*(p) = 0;
 	*returned_len = len;
+	/* in req2reply, received_buf is not introduced to lumps and
+	   needs to be deleted here
+	*/
+	if (received_buf) pkg_free(received_buf);
 	return buf;
-error:
-	if (buf) local_free(buf);
+
+error01:
+	if (received_buf) pkg_free(received_buf);
+error00:
 	*returned_len=0;
 	return 0;
 }
+
+/* return number of chars printed or 0 if space exceeded;
+   assumes buffer sace of at least MAX_BRANCH_PARAM_LEN
+ */
+
+int branch_builder( unsigned int hash_index,
+	/* only either parameter useful */
+	unsigned int label, char * char_v,
+	int branch,
+	char *branch_str, int *len )
+{
+
+	char *begin;
+	int size;
+
+	/* no hash_id --- whoever called me wants to have
+	   very simple branch_id
+	*/
+	if (hash_index==0) {
+		*branch_str='0';
+		*len=1;
+		return *len;
+	}
+
+	/* hash id provided ... start with it */
+	size=MAX_BRANCH_PARAM_LEN;
+	begin=branch_str;
+	*len=0;
+	if (int2reverse_hex( &begin, &size, hash_index)==-1)
+		return 0;
+
+	if (size) {
+		*begin=BRANCH_SEPARATOR;
+		begin++; size--;
+	} else return 0;
+
+	/* label is set -- use it ... */
+	if (label) {
+		if (int2reverse_hex( &begin, &size, label )==-1)
+			return 0;
+	} else { /* ... no label -- char value is used */
+		if (memcpy(begin,char_v,MD5_LEN)) {
+			begin+=MD5_LEN; size-=MD5_LEN;
+		} else return 0;
+	}
+
+	if (size) {
+		*begin=BRANCH_SEPARATOR;
+		begin++; size--;
+	} else return 0;
+
+	if (int2reverse_hex( &begin, &size, branch)==-1)
+		return 0;
+
+	*len=MAX_BRANCH_PARAM_LEN-size;
+	return size;
+		
+}
+
+
+char* via_builder( unsigned int *len, 
+	struct socket_info* send_sock,
+	char *branch, int branch_len )
+{
+	unsigned int  via_len, extra_len;
+	char               *line_buf;
+	int max_len;
+
+
+	max_len=MY_VIA_LEN+send_sock->address_str.len /* space in MY_VIA */
+		+2 /* just in case it it a v6 address ... [ ] */
+		+send_sock->port_no_str.len
+		+MY_BRANCH_LEN+branch_len+CRLF_LEN+1;
+	line_buf=pkg_malloc( max_len );
+	if (line_buf==0){
+		ser_error=E_OUT_OF_MEM;
+		LOG(L_ERR, "ERROR: via_builder: out of memory\n");
+		return 0;
+	}
+
+	extra_len=0;
+
+	via_len=MY_VIA_LEN+send_sock->address_str.len; /*space included in MY_VIA*/
+
+	memcpy(line_buf, MY_VIA, MY_VIA_LEN);
+#	ifdef USE_IPV6
+	if (send_sock->address.af==AF_INET6) {
+		line_buf[MY_VIA_LEN]='[';
+		line_buf[MY_VIA_LEN+1+send_sock->address_str.len]=']';
+		extra_len=1;
+		via_len+=2; /* [ ]*/
+	}
+#	endif
+	memcpy(line_buf+MY_VIA_LEN+extra_len, send_sock->address_str.s,
+		send_sock->address_str.len);
+	if (send_sock->port_no!=SIP_PORT){
+		memcpy(line_buf+via_len, send_sock->port_no_str.s,
+			 send_sock->port_no_str.len);
+		via_len+=send_sock->port_no_str.len;
+	}
+
+	/* branch parameter */
+	memcpy(line_buf+via_len, MY_BRANCH, MY_BRANCH_LEN );
+	via_len+=MY_BRANCH_LEN;
+	memcpy(line_buf+via_len, branch, branch_len );
+	via_len+=branch_len;
+	memcpy(line_buf+via_len, CRLF, CRLF_LEN);
+	via_len+=CRLF_LEN;
+	line_buf[via_len]=0; /* null terminate the string*/
+
+	*len = via_len;
+	return line_buf;
+}

+ 17 - 1
msg_translator.h

@@ -8,6 +8,8 @@
 #define MY_HF_SEP ": "
 #define MY_HF_SEP_LEN 2
 
+#define BRANCH_SEPARATOR '.'
+
 #include "parser/msg_parser.h"
 #include "ip_addr.h"
 
@@ -23,8 +25,22 @@ char * build_res_buf_from_sip_req(	unsigned int code ,
 				unsigned int new_tag_len ,
 				struct sip_msg* msg,
 				unsigned int *returned_len);
-char* via_builder( 			struct sip_msg *msg ,
+
+char* via_builder( unsigned int *len,
+	struct socket_info* send_sock,
+	char *branch, int branch_len );
+
+#ifdef _OBSOLETED
+char* via_builder( struct sip_msg *msg ,
 				unsigned int *len, struct socket_info* send_sock);
+#endif
+
+int branch_builder( unsigned int hash_index, 
+	/* only either parameter useful */
+	unsigned int label, char * char_v,
+	int branch,
+	/* output value: string and actual length */
+	char *branch_str, int *len );
 
 
 #endif

+ 10 - 0
parser/hf.c

@@ -74,3 +74,13 @@ void free_hdr_field_lst(struct hdr_field* hf)
 		pkg_free(foo);
 	}
 }
+
+void dump_hdr_field( struct hdr_field* hf )
+{
+	LOG(L_ERR, "DEBUG: dump_hdr_field: type=%d, name=%.*s, "
+		"body=%.*s, parsed=%p, next=%p\n",
+		hf->type, hf->name.len, hf->name.s,
+		hf->body.len, hf->body.s,
+		hf->parsed, hf->next );
+}
+

+ 2 - 1
parser/hf.h

@@ -9,7 +9,7 @@
 
 
 /* Header types and flags */
-#define HDR_EOH                 -1   /* End of header found */
+#define HDR_EOH         		-1   /* End of header found */
 #define HDR_ERROR                0   /* Error while parsing */
 #define HDR_VIA                  1   /* Via header field */
 #define HDR_VIA1                 1   /* First Via header field */
@@ -60,5 +60,6 @@ void clean_hdr_field(struct hdr_field* hf);
  */
 void free_hdr_field_lst(struct hdr_field* hf);
 
+void dump_hdr_field( struct hdr_field* hf );
 
 #endif /* HF_H */

+ 17 - 14
parser/msg_parser.c

@@ -20,6 +20,7 @@
 #include "parse_hname2.h"
 
 
+
 #ifdef DEBUG_DMALLOC
 #include <mem/dmalloc.h>
 #endif
@@ -27,6 +28,8 @@
 
 #define parse_hname(_b,_e,_h) parse_hname2((_b),(_e),(_h))
 
+/* number of via's encounteded */
+int via_cnt;
 
 /* returns pointer to next header line, and fill hdr_f ;
  * if at end of header returns pointer to the last crlf  (always buf)*/
@@ -53,6 +56,9 @@ char* get_hdr_field(char* buf, char* end, struct hdr_field* hdr)
 	}
 	switch(hdr->type){
 		case HDR_VIA:
+			/* keep number of vias parsed -- we want to report it in
+			   replies for diagnostic purposes */
+			via_cnt++;
 			vb=pkg_malloc(sizeof(struct via_body));
 			if (vb==0){
 				LOG(L_ERR, "get_hdr_field: out of memory\n");
@@ -388,7 +394,9 @@ int parse_msg(char* buf, unsigned int len, struct sip_msg* msg)
 			DBG(" version: <%s>\n",fl->u.reply.version.s);
 			DBG(" status:  <%s>\n",fl->u.reply.status.s);
 			DBG(" reason:  <%s>\n",fl->u.reply.reason.s);
-			flags=HDR_VIA | HDR_VIA2;
+			/* flags=HDR_VIA | HDR_VIA2; */
+			/* we don't try to parse VIA2 for local messages; -Jiri */
+			flags=HDR_VIA;
 			break;
 		default:
 			DBG("unknown type %d\n",fl->type);
@@ -465,22 +473,17 @@ void free_sip_msg(struct sip_msg* msg)
 }
 
 
-#if 0
-/* it's a macro now*/
 /* make sure all HFs needed for transaction identification have been
    parsed; return 0 if those HFs can't be found
 */
+
 int check_transaction_quadruple( struct sip_msg* msg )
 {
-   return 
-	(parse_headers(msg, HDR_FROM|HDR_TO|HDR_CALLID|HDR_CSEQ, 0)!=-1 &&
-	 msg->from && msg->to && msg->callid && msg->cseq);
-  /* replaced by me ( :) andrei)
-   ( (msg->from || (parse_headers( msg, HDR_FROM, 0)!=-1 && msg->from)) &&
-   (msg->to|| (parse_headers( msg, HDR_TO, 0)!=-1 && msg->to)) &&
-   (msg->callid|| (parse_headers( msg, HDR_CALLID, 0)!=-1 && msg->callid)) &&
-   (msg->cseq|| (parse_headers( msg, HDR_CSEQ, 0)!=-1 && msg->cseq)) ) ? 1 : 0;
-  */
-
+	if ( parse_headers(msg, HDR_FROM|HDR_TO|HDR_CALLID|HDR_CSEQ,0)!=-1
+		&& msg->from && msg->to && msg->callid && msg->cseq ) {
+		return 1;
+	} else {
+		ser_error=E_BAD_TUPEL;
+		return 0;
+	}
 }
-#endif

+ 49 - 20
parser/msg_parser.h

@@ -9,6 +9,7 @@
 #include "../data_lump.h"
 #include "../flags.h"
 #include "../ip_addr.h"
+#include "../md5utils.h"
 #include "parse_def.h"
 #include "parse_cseq.h"
 #include "parse_to.h"
@@ -96,12 +97,13 @@ struct sip_msg {
 	
 	str new_uri; /* changed first line uri*/
 	
-	struct lump* add_rm;         /* used for all the forwarded messages */
-	struct lump* repl_add_rm;    /* only for localy generated replies !!!*/
-	struct lump_rpl *reply_lump;
-	
-	     /* str add_to_branch */ 
-	     /* whatever whoever want to append to branch comes here */
+	struct lump* add_rm;         /* used for all the forwarded requests */
+	struct lump* repl_add_rm;    /* used for all the forwarded replies */
+	struct lump_rpl *reply_lump; /* only for localy generated replies !!!*/
+
+	/* str add_to_branch; 
+	   whatever whoever want to append to branch comes here 
+	*/
 	char add_to_branch_s[MAX_BRANCH_PARAM_LEN];
 	int add_to_branch_len;
 	
@@ -115,6 +117,13 @@ struct sip_msg {
 	flag_t flags;	
 };
 
+/* pointer to a fakes message which was never received ;
+   (when this message is "relayed", it is generated out
+    of the original request)
+*/
+#define FAKED_REPLY     ((struct sip_msg *) -1)
+
+extern int via_cnt;
 
 int parse_msg(char* buf, unsigned int len, struct sip_msg* msg);
 
@@ -126,19 +135,39 @@ void free_sip_msg(struct sip_msg* msg);
    parsed; return 0 if those HFs can't be found
  */
 
-#define check_transaction_quadruple(msg ) \
-	(parse_headers((msg), HDR_FROM|HDR_TO|HDR_CALLID|HDR_CSEQ, 0)!=-1 && \
-	(msg)->from && (msg)->to && (msg)->callid && (msg)->cseq)
-
-/* restored to the original double-check and put macro params
-   in parenthesses  -jiri */
-/* re-reverted to the shorter version -andrei 
-#define check_transaction_quadruple(msg ) \
-   ( ((msg)->from || (parse_headers( (msg), HDR_FROM, 0)!=-1 && (msg)->from)) && 	\
-   ((msg)->to|| (parse_headers( (msg), HDR_TO, 0)!=-1 && (msg)->to)) &&		\
-   ((msg)->callid|| (parse_headers( (msg), HDR_CALLID, 0)!=-1 && (msg)->callid)) &&\
-   ((msg)->cseq|| (parse_headers( (msg), HDR_CSEQ, 0)!=-1 && (msg)->cseq)) && \
-   ((msg)->via1|| (parse_headers( (msg), HDR_VIA, 0)!=-1 && (msg)->via1)) ) 
-*/
+int check_transaction_quadruple( struct sip_msg* msg );
+
+/* calculate characteristic value of a message -- this value
+   is used to identify a transaction during the process of
+   reply matching
+ */
+inline static int char_msg_val( struct sip_msg *msg, char *cv )
+{
+	str src[8];
+
+	if (!check_transaction_quadruple(msg)) {
+		LOG(L_ERR, "ERROR: can't calculate char_value due "
+			"to a parsing error\n");
+		memset( cv, '0', MD5_LEN );
+		return 0;
+	}
+
+	src[0]= msg->from->body;
+	src[1]= msg->to->body;
+	src[2]= msg->callid->body;
+	src[3]= msg->first_line.u.request.uri;
+	src[4]= get_cseq( msg )->number;
 	
+	/* topmost Via is part of transaction key as well ! */
+	src[5]= msg->via1->host;
+	src[6]= msg->via1->port_str;
+	if (msg->via1->branch) {
+		src[7]= msg->via1->branch->value;
+		MDStringArray ( cv, src, 8 );
+	} else {
+		MDStringArray( cv, src, 7 );
+	}
+	return 1;
+}
+
 #endif

+ 1 - 1
parser/parse_to.c

@@ -216,7 +216,7 @@ static /*inline*/ char* parse_to_param(char *buffer, char *end,
 				{
 					case PARA_VALUE_QUOTED:
 						break;
-#ifdef PINGTEL_TAG_HACK
+#ifndef NO_PINGTEL_TAG_HACK
 					case TAG3:
 						param->type = TAG_PARAM;
 						param->name.len = 3;

+ 1 - 0
proxy.c

@@ -223,6 +223,7 @@ struct proxy_l* mk_proxy(char* name, unsigned short port)
 #endif
 	/* fail over to normal lookup */
 
+	DBG("DEBUG: mk_proxy: doing DNS lookup...\n");
 	he=sip_resolvehost(name, &(p->port));
 	if (he==0){
 		ser_error=E_BAD_ADDRESS;

+ 29 - 19
receive.c

@@ -16,6 +16,8 @@
 #include "mem/mem.h"
 #include "stats.h"
 #include "ip_addr.h"
+#include "script_cb.h"
+#include "dset.h"
 
 
 #ifdef DEBUG_DMALLOC
@@ -35,8 +37,13 @@ int receive_msg(char* buf, unsigned int len, union sockaddr_union* src_su)
 #endif
 
 	msg=pkg_malloc(sizeof(struct sip_msg));
-	if (msg==0) goto error1;
+	if (msg==0) {
+		LOG(L_ERR, "ERROR: receive_msg: no mem for sip_msg\n");
+		goto error00;
+	}
 	msg_no++;
+	/* number of vias parsed -- good for diagnostic info in replies */
+	via_cnt=0;
 
 	memset(msg,0, sizeof(struct sip_msg)); /* init everything to 0 */
 	/* fill in msg */
@@ -52,16 +59,23 @@ int receive_msg(char* buf, unsigned int len, union sockaddr_union* src_su)
 	msg->orig=(char*) pkg_malloc(len+1);
 	if (msg->orig==0){
 		LOG(L_ERR, "ERROR:receive_msg: memory allocation failure\n");
-		goto error1;
+		goto error01;
 	}
 	memcpy(msg->orig, buf, len);
 	msg->orig[len]=0; /* null terminate it,good for using str* functions
 						 on it*/
 	
 	if (parse_msg(buf,len, msg)!=0){
-		goto error;
+		LOG(L_ERR, "ERROR: receive_msg: parse_msg failed\n");
+		goto error02;
 	}
 	DBG("After parse_msg...\n");
+
+	/* execute pre-script callbacks, if any; -jiri */
+	exec_pre_cb(msg);
+	/* ... and clear branches from previous message */
+	clear_branches();
+
 	if (msg->first_line.type==SIP_REQUEST){
 		/* sanity checks */
 		if ((msg->via1==0) || (msg->via1->error!=PARSE_OK)){
@@ -71,22 +85,20 @@ int receive_msg(char* buf, unsigned int len, union sockaddr_union* src_su)
 		}
 		/* check if neccesarry to add receive?->moved to forward_req */
 
-		/* loop checks */
-		if (loop_checks) {
-			DBG("WARNING: receive_msg: Placeholder for loop check."
-				" NOT implemented yet.\n");
-		}
-		
 		/* exec routing script */
 		DBG("preparing to run routing scripts...\n");
 #ifdef  STATS
 		gettimeofday( & tvb, &tz );
 #endif
+
 		if (run_actions(rlist[0], msg)<0){
+
 			LOG(L_WARN, "WARNING: receive_msg: "
 					"error while trying script\n");
 			goto error;
 		}
+
+
 #ifdef STATS
 		gettimeofday( & tve, &tz );
 		diff = (tve.tv_sec-tvb.tv_sec)*1000000+(tve.tv_usec-tvb.tv_usec);
@@ -108,8 +120,8 @@ int receive_msg(char* buf, unsigned int len, union sockaddr_union* src_su)
 			LOG(L_ERR, "ERROR: receive_msg: no 2nd via found in reply\n");
 			goto error;
 		}
-#endif
 		/* check if via1 == us */
+#endif
 
 #ifdef STATS
 		gettimeofday( & tvb, &tz );
@@ -130,10 +142,8 @@ int receive_msg(char* buf, unsigned int len, union sockaddr_union* src_su)
 #ifdef STATS
 	skipped = 0;
 #endif
-/* jku: skip no more used
-skip:
-	DBG("skip:...\n");
-*/
+	/* execute post-script callbacks, if any; -jiri */
+	exec_post_cb(msg);
 	DBG("receive_msg: cleaning up\n");
 	free_sip_msg(msg);
 	pkg_free(msg);
@@ -143,13 +153,13 @@ skip:
 	return 0;
 error:
 	DBG("error:...\n");
+	/* execute post-script callbacks, if any; -jiri */
+	exec_post_cb(msg);
+error02:
 	free_sip_msg(msg);
+error01:
 	pkg_free(msg);
-	STATS_RX_DROPS;
-	return -1;
-error1:
-	if (msg) pkg_free(msg);
-	pkg_free(buf);
+error00:
 	STATS_RX_DROPS;
 	return -1;
 }

+ 20 - 0
route.c

@@ -29,6 +29,8 @@
 
 /* main routing script table  */
 struct action* rlist[RT_NO];
+/* reply routing table */
+struct action* reply_rlist[REPLY_RT_NO];
 
 
 static int fix_actions(struct action* a); /*fwd declaration*/
@@ -426,6 +428,13 @@ int fix_rls()
 			}
 		}
 	}
+	for(i=0;i<REPLY_RT_NO;i++){
+		if(reply_rlist[i]){
+			if ((ret=fix_actions(reply_rlist[i]))!=0){
+				return ret;
+			}
+		}
+	}
 	return 0;
 }
 
@@ -447,6 +456,17 @@ void print_rl()
 		}
 		DBG("\n");
 	}
+	for(j=0; j<REPLY_RT_NO; j++){
+		if (reply_rlist[j]==0){
+			if (j==0) DBG("WARNING: the main reply routing table is empty\n");
+			continue;
+		}
+		DBG("routing table %d:\n",j);
+		for (t=reply_rlist[j],i=0; t; i++, t=t->next){
+			print_action(t);
+		}
+		DBG("\n");
+	}
 }
 
 

+ 2 - 0
route.h

@@ -19,6 +19,8 @@
 
 /* main "script table" */
 extern struct action* rlist[RT_NO];
+/* main reply route table */
+extern struct action* reply_rlist[RT_NO];
 
 
 void push(struct action* a, struct action** head);

+ 24 - 0
route_struct.c

@@ -222,6 +222,30 @@ void print_action(struct action* a)
 			case EXEC_T:
 					DBG("exec(");
 					break;
+			case REVERT_URI_T:
+					DBG("revert_uri(");
+					break;
+			case STRIP_T:
+					DBG("strip(");
+					break;
+			case APPEND_BRANCH_T:
+					DBG("append_branch(");
+					break;
+			case PREFIX_T:
+					DBG("prefix(");
+					break;
+			case LEN_GT_T:
+					DBG("len_gt(");
+					break;
+			case SETFLAG_T:
+					DBG("setflag(");
+					break;
+			case RESETFLAG_T:
+					DBG("resetflag(");
+					break;
+			case ISFLAGSET_T:
+					DBG("isflagset(");
+					break;
 			case SET_HOST_T:
 					DBG("sethost(");
 					break;

+ 3 - 1
route_struct.h

@@ -28,7 +28,9 @@ enum { FORWARD_T=1, SEND_T, DROP_T, LOG_T, ERROR_T, ROUTE_T, EXEC_T,
 		SET_HOST_T, SET_HOSTPORT_T, SET_USER_T, SET_USERPASS_T, 
 		SET_PORT_T, SET_URI_T, IF_T, MODULE_T,
 		SETFLAG_T, RESETFLAG_T, ISFLAGSET_T ,
-		LEN_GT_T, PREFIX_T, STRIP_T };
+		LEN_GT_T, PREFIX_T, STRIP_T,
+		APPEND_BRANCH_T,
+		REVERT_URI_T };
 enum { NOSUBTYPE=0, STRING_ST, NET_ST, NUMBER_ST, IP_ST, RE_ST, PROXY_ST,
 		EXPR_ST, ACTIONS_ST, CMDF_ST, MODFIXUP_ST, URIHOST_ST, URIPORT_ST };
 

+ 57 - 0
script_cb.c

@@ -0,0 +1,57 @@
+/*
+ * $Id$
+ *
+ * Script callbacks -- they add the ability to register callback
+ * functions which are always called when script for request
+ * processing is entered or left
+ *
+ */
+
+#include <stdlib.h>
+#include "script_cb.h"
+#include "dprint.h"
+#include "error.h"
+
+static struct script_cb *pre_cb=0;
+static struct script_cb *post_cb=0;
+static unsigned int cb_id=0;
+
+int register_script_cb( cb_function f, callback_t t, void *param )
+{
+	struct script_cb *new_cb;
+
+	new_cb=malloc(sizeof(struct script_cb));
+	if (new_cb==0) {
+		LOG(L_ERR, "ERROR: register_script_cb: out of memory\n");
+		return E_OUT_OF_MEM;
+	}
+	new_cb->cbf=f;
+	new_cb->id=cb_id++;
+	new_cb->param=param;
+	/* insert into appropriate list */
+	if (t==PRE_SCRIPT_CB) {
+		new_cb->next=pre_cb;
+		pre_cb=new_cb;
+	} else if (t==POST_SCRIPT_CB) {
+		new_cb->next=post_cb;
+		post_cb=new_cb;
+	} else {
+		LOG(L_CRIT, "ERROR: register_script_cb: unknown CB type\n");
+		return E_BUG;
+	}
+	/* ok, callback installed */
+	return 1;
+}
+
+void exec_pre_cb( struct sip_msg *msg)
+{
+	struct script_cb *i;
+	for (i=pre_cb; i; i=i->next) i->cbf(msg, i->param);
+}
+
+void exec_post_cb( struct sip_msg *msg)
+{
+	struct script_cb *i;
+	for (i=post_cb; i; i=i->next) i->cbf(msg, i->param);
+}
+

+ 26 - 0
script_cb.h

@@ -0,0 +1,26 @@
+/*
+ * $Id$
+ */
+
+#include "parser/msg_parser.h"
+
+typedef int (cb_function)( struct sip_msg *msg, void *param );
+
+typedef enum {
+    PRE_SCRIPT_CB,
+	POST_SCRIPT_CB
+} callback_t;       /* Allowed types of callbacks */
+
+
+struct script_cb{
+	cb_function *cbf;
+	struct script_cb *next;
+	unsigned int id;
+	void *param;
+};
+
+int register_script_cb( cb_function f, callback_t t, void *param );
+void exec_pre_cb( struct sip_msg *msg);
+void exec_post_cb( struct sip_msg *msg);
+
+

+ 32 - 0
test/bad_uri.sip

@@ -0,0 +1,32 @@
+INVITE sip:[email protected] SIP/2.0
+Max-Forwards: 10
+From: "jiri" <sip:[email protected]>;tag=76ff7a07-c091-4192-84a0-d56e91fe104f
+To: <sip:[email protected]>
+Call-ID: [email protected]
+CSeq: 2 INVITE
+Contact: <sip:213.20.128.35:9315>
+User-Agent: Windows RTC/1.0
+Proxy-Authorization: Digest username="jiri", realm="iptel.org", algorithm="MD5", uri="sip:[email protected]", nonce="3cef753900000001771328f5ae1b8b7f0d742da1feb5753c", response="53fe98db10e1074b03b3e06438bda70f"
+Content-Type: application/sdp
+Content-Length: 451
+
+v=0
+o=jku2 0 0 IN IP4 213.20.128.35
+s=session
+c=IN IP4 213.20.128.35
+b=CT:1000
+t=0 0
+m=audio 54742 RTP/AVP 97 111 112 6 0 8 4 5 3 101
+a=rtpmap:97 red/8000
+a=rtpmap:111 SIREN/16000
+a=fmtp:111 bitrate=16000
+a=rtpmap:112 G7221/16000
+a=fmtp:112 bitrate=24000
+a=rtpmap:6 DVI4/16000
+a=rtpmap:0 PCMU/8000
+a=rtpmap:8 PCMA/8000
+a=rtpmap:4 G723/8000
+a=rtpmap:5 DVI4/8000
+a=rtpmap: 3 GSM/8000
+a=rtpmap:101 telephone-event/8000
+a=fmtp:101 0-16

+ 5 - 0
test/file_copyright.txt

@@ -10,6 +10,11 @@
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version.
  *
+ *  For a license to use the ser software under conditions
+ *  other than those described here, or to purchase support for this
+ *  software, please contact iptel.org by e-mail at the following addresses:
+ *     [email protected]
+ *
  * ser 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

+ 33 - 0
test/invite00.sip

@@ -0,0 +1,33 @@
+INVITE sip:[email protected] SIP/2.0
+Via: SIP/2.0/UDP 195.37.77.100:5040
+Max-Forwards: 10
+From: "jiri" <sip:[email protected]>;tag=76ff7a07-c091-4192-84a0-d56e91fe104f
+To: <sip:[email protected]>
+Call-ID: [email protected]
+CSeq: 2 INVITE
+Contact: <sip:213.20.128.35:9315>
+User-Agent: Windows RTC/1.0
+Proxy-Authorization: Digest username="jiri", realm="iptel.org", algorithm="MD5", uri="sip:[email protected]", nonce="3cef753900000001771328f5ae1b8b7f0d742da1feb5753c", response="53fe98db10e1074b03b3e06438bda70f"
+Content-Type: application/sdp
+Content-Length: 451
+
+v=0
+o=jku2 0 0 IN IP4 213.20.128.35
+s=session
+c=IN IP4 213.20.128.35
+b=CT:1000
+t=0 0
+m=audio 54742 RTP/AVP 97 111 112 6 0 8 4 5 3 101
+a=rtpmap:97 red/8000
+a=rtpmap:111 SIREN/16000
+a=fmtp:111 bitrate=16000
+a=rtpmap:112 G7221/16000
+a=fmtp:112 bitrate=24000
+a=rtpmap:6 DVI4/16000
+a=rtpmap:0 PCMU/8000
+a=rtpmap:8 PCMA/8000
+a=rtpmap:4 G723/8000
+a=rtpmap:5 DVI4/8000
+a=rtpmap: 3 GSM/8000
+a=rtpmap:101 telephone-event/8000
+a=fmtp:101 0-16

+ 33 - 0
test/invite01.sip

@@ -0,0 +1,33 @@
+INVITE sip:[email protected] SIP/2.0
+Via: SIP/2.0/UDP 195.37.77.100:5040
+Max-Forwards: 10
+From: "jiri" <sip:[email protected]>;tag=76ff7a07-c091-4192-84a0-d56e91fe104f
+To: <sip:[email protected]>
+Call-ID: [email protected]
+CSeq: 2 INVITE
+Contact: <sip:213.20.128.35:9315>
+User-Agent: Windows RTC/1.0
+Proxy-Authorization: Digest username="jiri", realm="iptel.org", algorithm="MD5", uri="sip:[email protected]", nonce="3cef753900000001771328f5ae1b8b7f0d742da1feb5753c", response="53fe98db10e1074b03b3e06438bda70f"
+Content-Type: application/sdp
+Content-Length: 451
+
+v=0
+o=jku2 0 0 IN IP4 213.20.128.35
+s=session
+c=IN IP4 213.20.128.35
+b=CT:1000
+t=0 0
+m=audio 54742 RTP/AVP 97 111 112 6 0 8 4 5 3 101
+a=rtpmap:97 red/8000
+a=rtpmap:111 SIREN/16000
+a=fmtp:111 bitrate=16000
+a=rtpmap:112 G7221/16000
+a=fmtp:112 bitrate=24000
+a=rtpmap:6 DVI4/16000
+a=rtpmap:0 PCMU/8000
+a=rtpmap:8 PCMA/8000
+a=rtpmap:4 G723/8000
+a=rtpmap:5 DVI4/8000
+a=rtpmap: 3 GSM/8000
+a=rtpmap:101 telephone-event/8000
+a=fmtp:101 0-16

+ 33 - 0
test/invite03.sip

@@ -0,0 +1,33 @@
+INVITE sip:[email protected] SIP/2.0
+Via: SIP/2.0/UDP 195.37.77.100:5040
+Max-Forwards: 3
+From: "jiri" <sip:[email protected]>;tag=76ff7a07-c091-4192-84a0-d56e91fe104f
+To: <sip:[email protected]>
+Call-ID: [email protected]
+CSeq: 2 INVITE
+Contact: <sip:213.20.128.35:9315>
+User-Agent: Windows RTC/1.0
+Proxy-Authorization: Digest username="jiri", realm="iptel.org", algorithm="MD5", uri="sip:[email protected]", nonce="3cef753900000001771328f5ae1b8b7f0d742da1feb5753c", response="53fe98db10e1074b03b3e06438bda70f"
+Content-Type: application/sdp
+Content-Length: 451
+
+v=0
+o=jku2 0 0 IN IP4 213.20.128.35
+s=session
+c=IN IP4 213.20.128.35
+b=CT:1000
+t=0 0
+m=audio 54742 RTP/AVP 97 111 112 6 0 8 4 5 3 101
+a=rtpmap:97 red/8000
+a=rtpmap:111 SIREN/16000
+a=fmtp:111 bitrate=16000
+a=rtpmap:112 G7221/16000
+a=fmtp:112 bitrate=24000
+a=rtpmap:6 DVI4/16000
+a=rtpmap:0 PCMU/8000
+a=rtpmap:8 PCMA/8000
+a=rtpmap:4 G723/8000
+a=rtpmap:5 DVI4/8000
+a=rtpmap: 3 GSM/8000
+a=rtpmap:101 telephone-event/8000
+a=fmtp:101 0-16

+ 33 - 0
test/invite04.sip

@@ -0,0 +1,33 @@
+INVITE sip:[email protected]:9 SIP/2.0
+Via: SIP/2.0/UDP 195.37.77.100:5040
+Max-Forwards: 1
+From: "jiri" <sip:[email protected]>;tag=76ff7a07-c091-4192-84a0-d56e91fe104f
+To: <sip:[email protected]>
+Call-ID: [email protected]
+CSeq: 2 INVITE
+Contact: <sip:213.20.128.35:9315>
+User-Agent: Windows RTC/1.0
+Proxy-Authorization: Digest username="jiri", realm="iptel.org", algorithm="MD5", uri="sip:[email protected]", nonce="3cef753900000001771328f5ae1b8b7f0d742da1feb5753c", response="53fe98db10e1074b03b3e06438bda70f"
+Content-Type: application/sdp
+Content-Length: 451
+
+v=0
+o=jku2 0 0 IN IP4 213.20.128.35
+s=session
+c=IN IP4 213.20.128.35
+b=CT:1000
+t=0 0
+m=audio 54742 RTP/AVP 97 111 112 6 0 8 4 5 3 101
+a=rtpmap:97 red/8000
+a=rtpmap:111 SIREN/16000
+a=fmtp:111 bitrate=16000
+a=rtpmap:112 G7221/16000
+a=fmtp:112 bitrate=24000
+a=rtpmap:6 DVI4/16000
+a=rtpmap:0 PCMU/8000
+a=rtpmap:8 PCMA/8000
+a=rtpmap:4 G723/8000
+a=rtpmap:5 DVI4/8000
+a=rtpmap: 3 GSM/8000
+a=rtpmap:101 telephone-event/8000
+a=fmtp:101 0-16

+ 33 - 0
test/invite05.sip

@@ -0,0 +1,33 @@
+INVITE sip:[email protected]:9 SIP/2.0
+Via: SIP/2.0/UDP 195.37.77.100:5040
+Max-Forwards: 1
+From: "jiri" <sip:[email protected]>;tag=76ff7a07-c091-4192-84a0-d56e91fe104f
+To: <sip:[email protected]>
+Call-ID: [email protected]
+CSeq: 2 INVITE
+Contact: <sip:213.20.128.35:9315>
+User-Agent: Windows RTC/1.0
+Proxy-Authorization: Digest username="jiri", realm="iptel.org", algorithm="MD5", uri="sip:[email protected]", nonce="3cef753900000001771328f5ae1b8b7f0d742da1feb5753c", response="53fe98db10e1074b03b3e06438bda70f"
+Content-Type: application/sdp
+Content-Length: 451
+
+v=0
+o=jku2 0 0 IN IP4 213.20.128.35
+s=session
+c=IN IP4 213.20.128.35
+b=CT:1000
+t=0 0
+m=audio 54742 RTP/AVP 97 111 112 6 0 8 4 5 3 101
+a=rtpmap:97 red/8000
+a=rtpmap:111 SIREN/16000
+a=fmtp:111 bitrate=16000
+a=rtpmap:112 G7221/16000
+a=fmtp:112 bitrate=24000
+a=rtpmap:6 DVI4/16000
+a=rtpmap:0 PCMU/8000
+a=rtpmap:8 PCMA/8000
+a=rtpmap:4 G723/8000
+a=rtpmap:5 DVI4/8000
+a=rtpmap: 3 GSM/8000
+a=rtpmap:101 telephone-event/8000
+a=fmtp:101 0-16

+ 8 - 0
test/ms-invite-00-rpl.sip

@@ -0,0 +1,8 @@
+SIP/2.0 180 Ringing
+From: "jiri" <sip:[email protected]>;tag=ae2c8aec-fd00-4f0c-98b9-6d40b72c950c
+To: <sip:[email protected]>
+Call-ID: [email protected]
+CSeq: 2 OPTIONS
+Content-Length: 0
+Via: SIP/2.0/UDP 195.37.77.100;branch=902932441872fa3682b9de564b113050.e1.0
+Via: SIP/2.0/UDP pig.iptel.org

+ 32 - 0
test/ms-invite-00.sip

@@ -0,0 +1,32 @@
+OPTIONS sip:[email protected] SIP/2.0
+Max-Forwards: 10
+Via: SIP/2.0/UDP 195.37.77.100:9;branch=0e413351aee2246f9f1edac77ccd39bc.e1.0
+From: "jiri" <sip:[email protected]>;tag=ae2c8aec-fd00-4f0c-98b9-6d40b72c950c
+To: <sip:[email protected]>
+Call-ID: [email protected]
+CSeq: 2 OPTIONS
+Contact: <sip:212.202.173.36:9310>
+User-Agent: Windows RTC/1.0
+Content-Type: application/sdp
+Content-Length: 453
+
+v=0
+o=jku2 0 0 IN IP4 212.202.173.36
+s=session
+c=IN IP4 212.202.173.36
+b=CT:1000
+t=0 0
+m=audio 16090 RTP/AVP 97 111 112 6 0 8 4 5 3 101
+a=rtpmap:97 red/8000
+a=rtpmap:111 SIREN/16000
+a=fmtp:111 bitrate=16000
+a=rtpmap:112 G7221/16000
+a=fmtp:112 bitrate=24000
+a=rtpmap:6 DVI4/16000
+a=rtpmap:0 PCMU/8000
+a=rtpmap:8 PCMA/8000
+a=rtpmap:4 G723/8000
+a=rtpmap:5 DVI4/8000
+a=rtpmap:3 GSM/8000
+a=rtpmap:101 telephone-event/8000
+a=fmtp:101 0-16

+ 10 - 0
test/ms-invite-01-rpl.sip

@@ -0,0 +1,10 @@
+SIP/2.0 180 Ringing
+From: "jiri" <sip:[email protected]>;tag=ae2c8aec-fd00-4f0c-98b9-6d40b72c950c
+To: <sip:[email protected]>
+Call-ID: [email protected]
+CSeq: 2 OPTIONS
+Content-Length: 0
+Via: SIP/2.0/UDP 195.37.77.100;branch=902932441872fa3682b9de564b113050.e1.0
+Via: SIP/2.0/UDP pig.iptel.org
+foo: bar
+

+ 3 - 0
test/no_eom_reply.sip

@@ -0,0 +1,3 @@
+SIP/2.0 183 Session Progress
+Via: SIP/2.0/UDP 195.37.77.100;branch=b7a102045e84a2d128c3e173680de665.531.0
+Via: SIP/2.0/UDP pig.iptel.org

+ 74 - 0
test/onr.cfg

@@ -0,0 +1,74 @@
+#
+# $Id$
+#
+# iptel.org real world configuration
+#
+
+# ----------- global configuration parameters ------------------------
+
+debug=3
+fork=no
+log_stderror=yes	# (cmd line: -E)
+check_via=no # (cmd. line: -v)
+dns=no # (cmd. line: -r)
+syn_branch=1
+reply_to_via=0
+
+# advertise IP address in Via (as opposed to advertising DNS name
+# which is annoying for downstream servers and some phones can
+# not handle DNS at all)
+listen=195.37.77.100
+
+# ------------------ module loading ----------------------------------
+
+loadmodule "../sip_router/modules/sl/sl.so"
+loadmodule "../sip_router/modules/print/print.so"
+#loadmodule "../sip_router/modules/tm/tm.so"
+loadmodule "../sip_router/modules/usrloc/usrloc.so"
+
+# ----------------- setting module-specific parameters ---------------
+
+# -- usrloc params --
+
+modparam("usrloc", "use_database",   0)
+modparam("usrloc", "flush_interval", 3600)
+
+# -- tm params --
+modparam("tm", "fr_timer", 10 )
+modparam("tm", "fr_inv_timer", 5 )
+
+# -------------------------  request routing logic -------------------
+
+# main routing logic
+
+route{
+	# for testing purposes, simply okay all REGISTERs
+	if (method=="REGISTER") {
+		log("REGISTER");
+		sl_send_reply("200", "ok");
+		break;
+	};
+	# print a message if a call was missed
+	seturi("sip:[email protected]");
+	/* parallel branch to sink port -- that will make it
+	   wait until timer hits
+	*/
+	append_branch("sip:[email protected]:9");
+	t_on_negative("1");
+	# start parallel forking to nobody and wer.xmla	
+	log(1,"about to relay\n");
+	t_relay();
+}
+
+reply_route[1] {
+	rewriteuri("sip:[email protected]");
+	append_branch();
+	log(1,"first redirection\n");
+	t_on_negative("2");
+}
+
+reply_route[2] {
+	rewriteuri("sip:[email protected]");
+	log(1, "second redirection\n");
+	append_branch();
+}

+ 11 - 0
test/register03.sip

@@ -0,0 +1,11 @@
+REGISTER sip:iptel.org:5060 SIP/2.0
+To: <sip:[email protected]>
+From: <sip:[email protected]>
+Call-ID: [email protected]
+CSeq: 4 REGISTER
+Via: SIP/2.0/UDP iptel.org:5060
+User-Agent:  UbiquityUserAgent/4
+Expires: 0
+Content-Length: 0
+
+

+ 11 - 0
test/short_nonce.sip

@@ -0,0 +1,11 @@
+REGISTER sip:iptel.org SIP/2.0
+Via: SIP/2.0/UDP 213.20.132.141:16449
+From: <sip:[email protected]>;tag=5df69a49-de80-436e-82eb-d0a991cbf744
+To: <sip:[email protected]>
+Call-ID: [email protected]
+CSeq: 18 REGISTER
+Contact: <sip:213.20.132.141:16449>;methods="INVITE, MESSAGE, INFO, SUBSCRIBE, OPTIONS, BYE, CANCEL, NOTIFY, ACK"
+User-Agent: Windows RTC/1.0
+Expires: 0
+Content-Length: 0
+Authorization: Digest username="jiri", realm="iptel.org", algorithm="MD5", uri="sip:iptel.org", response="8791571e2a65e8e1d5ac17cc49f8a16d", nonce="3

+ 4 - 0
test/short_reply.sip

@@ -0,0 +1,4 @@
+SIP/2.0 183 Session Progress
+Via: SIP/2.0/UDP 195.37.77.100;branch=b7a102045e84a2d128c3e173680de665.531.0
+Via: SIP/2.0/UDP pig.iptel.org
+

+ 47 - 0
test/stress.cfg

@@ -0,0 +1,47 @@
+#
+# $Id$
+#
+# iptel.org real world configuration
+#
+
+# ----------- global configuration parameters ------------------------
+
+debug=3
+fork=no
+log_stderror=yes	# (cmd line: -E)
+check_via=no # (cmd. line: -v)
+dns=no # (cmd. line: -r)
+syn_branch=1
+reply_to_via=0
+
+# advertise IP address in Via (as opposed to advertising DNS name
+# which is annoying for downstream servers and some phones can
+# not handle DNS at all)
+listen=195.37.77.100
+
+# ------------------ module loading ----------------------------------
+
+loadmodule "../sip_router/modules/sl/sl.so"
+loadmodule "../sip_router/modules/print/print.so"
+#loadmodule "../sip_router/modules/tm/tm.so"
+loadmodule "../sip_router/modules/usrloc/usrloc.so"
+
+# ----------------- setting module-specific parameters ---------------
+
+# -- usrloc params --
+
+modparam("usrloc", "use_database",   0)
+modparam("usrloc", "flush_interval", 3600)
+
+# -- tm params --
+modparam("tm", "fr_timer", 10 )
+modparam("tm", "fr_inv_timer", 5 )
+
+# -------------------------  request routing logic -------------------
+
+# main routing logic
+
+route{
+	t_relay_to("bat.iptel.org","5088");
+}
+

+ 50 - 0
test/struas.cfg

@@ -0,0 +1,50 @@
+#
+# $Id$
+#
+# iptel.org real world configuration
+#
+
+# ----------- global configuration parameters ------------------------
+
+debug=3
+fork=no
+#children=2
+log_stderror=yes	# (cmd line: -E)
+check_via=yes     # (cmd. line: -v)
+dns=0           # (cmd. line: -r)
+rev_dns=0      # (cmd. line: -R)
+port=5088
+reply_to_via=no
+
+# advertise IP address in Via (as opposed to advertising DNS name
+# which is annoying for downstream servers and some phones can
+# not handle DNS at all)
+listen=195.37.77.100
+
+# ------------------ module loading ----------------------------------
+
+loadmodule "../sip_router/modules/sl/sl.so"
+loadmodule "../sip_router/modules/print/print.so"
+#loadmodule "../sip_router/modules/tm/tm.so"
+loadmodule "../sip_router/modules/usrloc/usrloc.so"
+
+# ----------------- setting module-specific parameters ---------------
+
+# -- usrloc params --
+
+modparam("usrloc", "use_database",   0)
+modparam("usrloc", "flush_interval", 3600)
+
+# -------------------------  request routing logic -------------------
+
+# main routing logic
+
+route{
+	if ( t_newtran())
+    {
+		if (!t_reply("499", "Bizzar Error")) {
+			sl_reply_error();
+		};
+	};
+}
+

이 변경점에서 너무 많은 파일들이 변경되어 몇몇 파일들은 표시되지 않았습니다.