Selaa lähdekoodia

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 vuotta sitten
vanhempi
commit
caf80ae64e
100 muutettua tiedostoa jossa 6091 lisäystä ja 3314 poistoa
  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
 		    GNU GENERAL PUBLIC LICENSE
 		       Version 2, June 1991
 		       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  source related defs
 include Makefile.sources
 include Makefile.sources
 
 
-exclude_modules=CVS mysql pike
+exclude_modules=CVS mysql pike 
 static_modules=
 static_modules=
 static_modules_path=$(addprefix modules/, $(static_modules))
 static_modules_path=$(addprefix modules/, $(static_modules))
 extra_sources=$(wildcard $(addsuffix /*.c, $(static_modules_path)))
 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
 #		issues additional debugging information if lock/unlock is called
 # -DFAST_LOCK
 # -DFAST_LOCK
 #		uses fast arhitecture specific locking (see the arh. specific section)
 #		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
 # -DBUSY_WAIT
 #		uses busy waiting on the lock
 #		uses busy waiting on the lock
 # -DADAPTIVE_WAIT
 # -DADAPTIVE_WAIT
@@ -90,37 +87,29 @@ INSTALL-MAN = $(INSTALL) -m 644
 # -DNOSMP
 # -DNOSMP
 #		don't use smp compliant locking (faster but won't work on SMP machines)
 #		don't use smp compliant locking (faster but won't work on SMP machines)
 #		(not yet enabled)
 #		(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)"' \
 DEFS+= -DNAME='"$(NAME)"' -DVERSION='"$(RELEASE)"' -DARCH='"$(ARCH)"' \
 	 -DOS='"$(OS)"' -DCOMPILER='"$(CC_VER)"'\
 	 -DOS='"$(OS)"' -DCOMPILER='"$(CC_VER)"'\
 	 -DPKG_MALLOC \
 	 -DPKG_MALLOC \
 	 -DSHM_MEM  -DSHM_MMAP \
 	 -DSHM_MEM  -DSHM_MMAP \
 	 -DADAPTIVE_WAIT -DADAPTIVE_WAIT_LOOPS=1024 \
 	 -DADAPTIVE_WAIT -DADAPTIVE_WAIT_LOOPS=1024 \
-	 -DWAIT \
-	 -DSILENT_FR \
-	 -DNOISY_REPLIES -DVERY_NOISY_REPLIES\
-	 -DPINGTEL_TAG_HACK\
-	 -DF_MALLOC \
- 	 -DUSE_SYNONIM\
 	 -DUSE_IPV6 \
 	 -DUSE_IPV6 \
+	 -DEXTRA_DEBUG \
+	 -DVQ_MALLOC  -DDBG_QM_MALLOC \
+	 #-DCONTACT_BUG
+	 #-DF_MALLOC \
+	 #-DDBG_LOCK
 	 #-DNO_DEBUG \
 	 #-DNO_DEBUG \
 	 #-DADAPTIVE_WAIT -DADAPTIVE_WAIT_LOOPS=0 \
 	 #-DADAPTIVE_WAIT -DADAPTIVE_WAIT_LOOPS=0 \
 	 #-DNOSMP \
 	 #-DNOSMP \
 	 #-DEXTRA_DEBUG 
 	 #-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
 #PROFILE=  -pg #set this if you want profiling
 #mode = debug
 #mode = debug

+ 37 - 6
README-MODULES

@@ -3,33 +3,59 @@
 List of currently available ser modules
 List of currently available ser modules
 ----------------------------------------------------------
 ----------------------------------------------------------
 Name	Use		 	 Maturity	Purpose/Depends on
 Name	Use		 	 Maturity	Purpose/Depends on
+(owner)
 ----------------------------------------------------------
 ----------------------------------------------------------
 acc		regular		 stable		transaction accounting
 acc		regular		 stable		transaction accounting
-		/example				(the module servers also
+(jku)	/example				(the module servers also
 							    as example of how to bind
 							    as example of how to bind
 								to transaction management)
 								to transaction management)
 								-tm
 								-tm
+
 auth	regular		 stable		digest authentication
 auth	regular		 stable		digest authentication
-								-sl
+(jja)							-sl
 								-mysql
 								-mysql
+
 cpl		experimental alpha		Call Processing Language
 cpl		experimental alpha		Call Processing Language
+(bia)
+
 ext		experimental alpha		Execution of external URI
 ext		experimental alpha		Execution of external URI
-								processing logic
+(bia)							processing logic
+
 im		temporary	 alpha		Stateless instant messaging	
 im		temporary	 alpha		Stateless instant messaging	
-								client
+(bia)							client
+
 jabber	experimental beta		SIP2Jabber gateway
 jabber	experimental beta		SIP2Jabber gateway
+(dcm)
+
 maxfwd	regular		 stable		Max-Forwards check
 maxfwd	regular		 stable		Max-Forwards check
+(bia)
+
 mysql	regular		 stable		supporting MySql interface
 mysql	regular		 stable		supporting MySql interface
+(jja)
+
 pike	experimental alpha		excessive load detection
 pike	experimental alpha		excessive load detection
+(bia)
+
 print	example		 stable		Printing Message to stdout
 print	example		 stable		Printing Message to stdout
+(ape)
+
 rr		regular		 stable		Record-Routing
 rr		regular		 stable		Record-Routing
+(jja)
+
 sl		regular		 stable		Stateless Replies
 sl		regular		 stable		Stateless Replies
+(bia)
+
 sms		regular		 stable		SMS gateway
 sms		regular		 stable		SMS gateway
-								-tm
+(bia)								-tm
+
 textops regular		 stable		Message Textual Operations
 textops regular		 stable		Message Textual Operations
+(ape)
+
 tm		regular		 beta		Transaction Management
 tm		regular		 beta		Transaction Management
+(ape)
+
 usrloc	regular		 stable		User Location
 usrloc	regular		 stable		User Location
-								-sl
+(jja)								-sl
 								-mysql (optionally)
 								-mysql (optionally)
 
 
 
 
@@ -44,3 +70,8 @@ ser programmers.
 Maturity is label as stable if a module has been deployed
 Maturity is label as stable if a module has been deployed
 for longer time, alpha if it is still being developed and
 for longer time, alpha if it is still being developed and
 beta if it is under test.
 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:
 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 if () {} else {}
 x plugin interface
 x plugin interface
 x ipv6 support
 x ipv6 support
-- reply ("response line")
-- drop ACKs for our replies
+x reply ("response line")
+x drop ACKs for our replies
 - icmp error handling
 - 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:
 Low priority:
 x fix via address someday
 x fix via address someday
@@ -38,7 +38,7 @@ x forward to received= if present
 - exec improvments (add format strings to it)
 - exec improvments (add format strings to it)
 - command line switch for checking the config file syntax
 - command line switch for checking the config file syntax
 - config file version (a la sendmail)
 - config file version (a la sendmail)
-- loop detection
+0 loop detection
 - cfg. file reload
 - cfg. file reload
 - flags for using names or ip adresses in Via ?
 - flags for using names or ip adresses in Via ?
 
 
@@ -55,6 +55,6 @@ x the same for rpm
 - the same for FreeBSD and Slackware
 - 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
 - jku: try CRC as opposed to MD5

+ 21 - 0
action.c

@@ -17,6 +17,7 @@
 #include "sr_module.h"
 #include "sr_module.h"
 #include "mem/mem.h"
 #include "mem/mem.h"
 #include "globals.h"
 #include "globals.h"
+#include "dset.h"
 
 
 #include <sys/types.h>
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <sys/socket.h>
@@ -179,6 +180,18 @@ int do_action(struct action* a, struct sip_msg* msg)
 			ret=1;
 			ret=1;
 			break;
 			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 */
 		/* jku begin: is_length_greater_than */
 		case LEN_GT_T:
 		case LEN_GT_T:
 			if (a->p1_type!=NUMBER_ST) {
 			if (a->p1_type!=NUMBER_ST) {
@@ -281,6 +294,14 @@ int do_action(struct action* a, struct sip_msg* msg)
 			}
 			}
 			ret=1;
 			ret=1;
 			break;
 			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_HOST_T:
 		case SET_HOSTPORT_T:
 		case SET_HOSTPORT_T:
 		case SET_USER_T:
 		case SET_USER_T:

+ 18 - 2
cfg.lex

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

+ 41 - 3
cfg.y

@@ -56,15 +56,18 @@ void* f_tmp;
 %token LOG_TOK
 %token LOG_TOK
 %token ERROR
 %token ERROR
 %token ROUTE
 %token ROUTE
+%token REPLY_ROUTE
 %token EXEC
 %token EXEC
 %token SET_HOST
 %token SET_HOST
 %token SET_HOSTPORT
 %token SET_HOSTPORT
 %token PREFIX
 %token PREFIX
 %token STRIP
 %token STRIP
+%token APPEND_BRANCH
 %token SET_USER
 %token SET_USER
 %token SET_USERPASS
 %token SET_USERPASS
 %token SET_PORT
 %token SET_PORT
 %token SET_URI
 %token SET_URI
+%token REVERT_URI
 %token IF
 %token IF
 %token ELSE
 %token ELSE
 %token URIHOST
 %token URIHOST
@@ -90,7 +93,12 @@ void* f_tmp;
 %token STAT
 %token STAT
 %token CHILDREN
 %token CHILDREN
 %token CHECK_VIA
 %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 LOADMODULE
 %token MODPARAM
 %token MODPARAM
 %token MAXBUFFER
 %token MAXBUFFER
@@ -151,6 +159,7 @@ statements:	statements statement {}
 statement:	assign_stm 
 statement:	assign_stm 
 		| module_stm
 		| module_stm
 		| route_stm 
 		| route_stm 
+		| reply_route_stm
 		| CR	/* null statement*/
 		| CR	/* null statement*/
 	;
 	;
 
 
@@ -180,8 +189,18 @@ assign_stm:	DEBUG EQUAL NUMBER { debug=$3; }
 		| CHILDREN EQUAL error { yyerror("number expected"); } 
 		| CHILDREN EQUAL error { yyerror("number expected"); } 
 		| CHECK_VIA EQUAL NUMBER { check_via=$3; }
 		| CHECK_VIA EQUAL NUMBER { check_via=$3; }
 		| CHECK_VIA EQUAL error { yyerror("boolean value expected"); }
 		| 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  {
 		| LISTEN EQUAL ip  {
 								if (sock_no< MAX_LISTEN){
 								if (sock_no< MAX_LISTEN){
 									tmp=ip_addr2a($3);
 									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"); }
 		| 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; }
 rules:	rules rule { push($2, &$1); $$=$1; }
 	| rule {$$=$1; }
 	| rule {$$=$1; }
@@ -685,6 +715,12 @@ cmd:		FORWARD LPAREN host RPAREN	{ $$=mk_action(	FORWARD_T,
 		| STRIP LPAREN error RPAREN { $$=0; yyerror("bad argument, "
 		| STRIP LPAREN error RPAREN { $$=0; yyerror("bad argument, "
 														"number expected"); }
 														"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, 
 		| SET_HOSTPORT LPAREN STRING RPAREN { $$=mk_action( SET_HOSTPORT_T, 
 														STRING_ST, 0, $3, 0); }
 														STRING_ST, 0, $3, 0); }
 		| SET_HOSTPORT error { $$=0; yyerror("missing '(' or ')' ?"); }
 		| 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 error { $$=0; yyerror("missing '(' or ')' ?"); }
 		| SET_URI LPAREN error RPAREN { $$=0; yyerror("bad argument, "
 		| SET_URI LPAREN error RPAREN { $$=0; yyerror("bad argument, "
 										"string expected"); }
 										"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);
 		| ID LPAREN RPAREN			{ f_tmp=(void*)find_export($1, 0);
 									   if (f_tmp==0){
 									   if (f_tmp==0){
 										yyerror("unknown command, missing"
 										yyerror("unknown command, missing"

+ 14 - 5
config.h

@@ -22,6 +22,7 @@
 #define CHILD_NO    8
 #define CHILD_NO    8
 
 
 #define RT_NO 10 /* routing tables number */
 #define RT_NO 10 /* routing tables number */
+#define REPLY_RT_NO 10 /* reply routing tables number */
 #define DEFAULT_RT 0 /* default routing table */
 #define DEFAULT_RT 0 /* default routing table */
 
 
 #define MAX_REC_LEV 100 /* maximum number of recursive calls */
 #define MAX_REC_LEV 100 /* maximum number of recursive calls */
@@ -46,8 +47,8 @@
 
 
 #define MAX_WARNING_LEN  256
 #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 */
 #define MAX_PORT_LEN 7 /* ':' + max 5 letters + \0 */
@@ -88,8 +89,16 @@
 #define MAX_VIA_LINE_SIZE	240
 #define MAX_VIA_LINE_SIZE	240
 #define MAX_RECEIVED_SIZE	57
 #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
 #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 <stdio.h>
 #include "error.h"
 #include "error.h"
+#include "str.h"
+#include "parser/msg_parser.h"
+#include "mem/mem.h"
 
 
 /* current function's error; */
 /* current function's error; */
 int ser_error=-1;
 int ser_error=-1;
@@ -43,6 +46,10 @@ int err2reason_phrase(
 			error_txt="Regretfuly, we were not able to process the URI";
 			error_txt="Regretfuly, we were not able to process the URI";
 			*sip_error=-ser_error;
 			*sip_error=-ser_error;
 			break;
 			break;
+		case E_BAD_TUPEL:
+			error_txt="Transaction tupel incomplete";
+			*sip_error=-E_BAD_REQ;
+			break;
 		default:
 		default:
 			error_txt="I'm terribly sorry, server error occured";
 			error_txt="I'm terribly sorry, server error occured";
 			*sip_error=500;
 			*sip_error=500;
@@ -51,3 +58,104 @@ int err2reason_phrase(
 	return snprintf( phrase, etl, "%s (%d/%s)", error_txt, 
 	return snprintf( phrase, etl, "%s (%d/%s)", error_txt, 
 		-ser_error, signature );
 		-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_BUG         -5
 #define E_CFG         -6
 #define E_CFG         -6
 #define E_NO_SOCKET		-7
 #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
 #define E_SEND		  -477
 /* unresolveable next-hop address */
 /* unresolveable next-hop address */
@@ -23,12 +29,22 @@
 
 
 #define MAX_REASON_LEN	128
 #define MAX_REASON_LEN	128
 
 
+#include "str.h"
+
 /* processing status of the last command */
 /* processing status of the last command */
 extern int ser_error;
 extern int ser_error;
 extern int prev_ser_error;
 extern int prev_ser_error;
 
 
+struct sip_msg;
+
+/* ser error -> SIP error */
 int err2reason_phrase( int ser_error, int *sip_error, 
 int err2reason_phrase( int ser_error, int *sip_error, 
                 char *phrase, int etl, char *signature );
                 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
 #endif

+ 1 - 0
etc/iptel.cfg

@@ -614,5 +614,6 @@ route[3] {
 		sl_reply_error(); 
 		sl_reply_error(); 
 		break; 
 		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 ) {
 int resetflag( struct sip_msg* msg, flag_t flag ) {
-	msg->flags &= ~ flag;
+	msg->flags &= ~ (1 << flag);
 	return 1;
 	return 1;
 }
 }
 
 
 int isflagset( struct sip_msg* msg, flag_t flag ) {
 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 ) {
 int flag_in_range( flag_t flag ) {

+ 50 - 8
forward.c

@@ -13,6 +13,7 @@
 #include <arpa/inet.h>
 #include <arpa/inet.h>
 
 
 #include "forward.h"
 #include "forward.h"
+#include "hash_func.h"
 #include "config.h"
 #include "config.h"
 #include "parser/msg_parser.h"
 #include "parser/msg_parser.h"
 #include "route.h"
 #include "route.h"
@@ -67,6 +68,7 @@ int forward_request( struct sip_msg* msg, struct proxy_l * p)
 	char* buf;
 	char* buf;
 	union sockaddr_union* to;
 	union sockaddr_union* to;
 	struct socket_info* send_sock;
 	struct socket_info* send_sock;
+	char md5[MD5_LEN];
 	
 	
 	to=0;
 	to=0;
 	buf=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 "
 		LOG(L_ERR, "forward_req: ERROR: cannot forward to af %d "
 				"no coresponding listening socket\n", to->s.sa_family);
 				"no coresponding listening socket\n", to->s.sa_family);
 		ser_error=E_NO_SOCKET;
 		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);
 	buf = build_req_buf_from_sip_req( msg, &len, send_sock);
 	if (!buf){
 	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! */
 	 /* send it! */
 	DBG("Sending:\n%s.\n", buf);
 	DBG("Sending:\n%s.\n", buf);
@@ -116,7 +144,7 @@ int forward_request( struct sip_msg* msg, struct proxy_l * p)
 			p->errors++;
 			p->errors++;
 			p->ok=0;
 			p->ok=0;
 			STATS_TX_DROPS;
 			STATS_TX_DROPS;
-			goto error;
+			goto error1;
 	}
 	}
 	/* sent requests stats */
 	/* sent requests stats */
 	else STATS_TX_REQUEST(  msg->first_line.u.request.method_value );
 	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);
 	free(to);
 	/* received_buf & line_buf will be freed in receiv_msg by free_lump_list*/
 	/* received_buf & line_buf will be freed in receiv_msg by free_lump_list*/
 	return 0;
 	return 0;
+
+error1:
+	free(to);
 error:
 error:
 	if (buf) pkg_free(buf);
 	if (buf) pkg_free(buf);
-	if (to) free(to);
 	return -1;
 	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,
 int update_sock_struct_from_via( union sockaddr_union* to,
 								 struct via_body* via )
 								 struct via_body* via )
 {
 {
@@ -252,9 +293,10 @@ int forward_reply(struct sip_msg* msg)
 			if (mod->exports->response_f(msg)==0) goto skip;
 			if (mod->exports->response_f(msg)==0) goto skip;
 		}
 		}
 	}
 	}
-	
+
 	/* we have to forward the reply stateless, so we need second via -bogdan*/
 	/* 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 */
 		/* no second via => error */
 		LOG(L_ERR, "ERROR: forward_msg: no 2nd via found in reply\n");
 		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 forward_request( struct sip_msg* msg,  struct proxy_l* p);
 int update_sock_struct_from_via( union sockaddr_union* to,
 int update_sock_struct_from_via( union sockaddr_union* to,
 								struct via_body* via );
 								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);
 int forward_reply( struct sip_msg* msg);
 
 
 #endif
 #endif

+ 12 - 1
globals.h

@@ -44,8 +44,10 @@ extern int children_no;
 extern int dont_fork;
 extern int dont_fork;
 extern int check_via;
 extern int check_via;
 extern int received_dns;
 extern int received_dns;
-extern int loop_checks;
+extern int syn_branch;
 extern int process_no;
 extern int process_no;
+extern int sip_warning;
+extern int server_signature;
 /*
 /*
  * debug & log_stderr moved to dprint.h*/
  * debug & log_stderr moved to dprint.h*/
 
 
@@ -57,4 +59,13 @@ extern unsigned int msg_no;
 
 
 extern unsigned int shm_mem_size;
 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
 #endif

+ 48 - 27
main.c

@@ -36,6 +36,7 @@
 #include "resolve.h"
 #include "resolve.h"
 #include "parser/parse_hname2.h"
 #include "parser/parse_hname2.h"
 #include "parser/digest/digest_parser.h"
 #include "parser/digest/digest_parser.h"
+#include "fifo_server.h"
 
 
 
 
 #include "stats.h"
 #include "stats.h"
@@ -93,21 +94,6 @@ static char flags[]=
 #ifdef DEBUG_DMALLOC
 #ifdef DEBUG_DMALLOC
 ", DEBUG_DMALLOC"
 ", DEBUG_DMALLOC"
 #endif
 #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
 #ifdef FAST_LOCK
 ", FAST_LOCK"
 ", FAST_LOCK"
 #ifdef BUSY_WAIT
 #ifdef BUSY_WAIT
@@ -125,7 +111,6 @@ static char flags[]=
 static char help_msg[]= "\
 static char help_msg[]= "\
 Usage: " NAME " -l address [-p port] [-l address [-p port]...] [options]\n\
 Usage: " NAME " -l address [-p port] [-l address [-p port]...] [options]\n\
 Options:\n\
 Options:\n\
-    -c           Perform loop checks and compute branches\n\
     -f file      Configuration file (default " CFG_FILE ")\n\
     -f file      Configuration file (default " CFG_FILE ")\n\
     -p port      Listen on the specified port (default: 5060)\n\
     -p port      Listen on the specified port (default: 5060)\n\
                  applies to the last address in -l and to all \n\
                  applies to the last address in -l and to all \n\
@@ -173,8 +158,8 @@ void print_ct_constants()
 #endif
 #endif
 */
 */
 	printf("MAX_RECV_BUFFER_SIZE %d, MAX_LISTEN %d,"
 	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 );
 		BUF_SIZE );
 }
 }
 
 
@@ -209,14 +194,29 @@ int sig_flag = 0;              /* last signal received */
 int debug = 0;
 int debug = 0;
 int dont_fork = 0;
 int dont_fork = 0;
 int log_stderr = 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* working_dir = 0;
 char* chroot_dir = 0;
 char* chroot_dir = 0;
 int uid = 0;
 int uid = 0;
 int gid = 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
 #if 0
 char* names[MAX_LISTEN];              /* our names */
 char* names[MAX_LISTEN];              /* our names */
@@ -497,6 +497,12 @@ int main_loop()
 			LOG(L_ERR, "init_child failed\n");
 			LOG(L_ERR, "init_child failed\n");
 			goto error;
 			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
 		is_main=1; /* hack 42: call init_child with is_main=0 in case
 					 some modules wants to fork a child */
 					 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?*/
 			/*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*/
 	/*this is the main process*/
 	pids[process_no]=getpid();
 	pids[process_no]=getpid();
 	process_bit = 0;
 	process_bit = 0;
@@ -590,6 +601,7 @@ int main_loop()
 static void sig_usr(int signo)
 static void sig_usr(int signo)
 {
 {
 
 
+
 	if (is_main){
 	if (is_main){
 		if (sig_flag==0) sig_flag=signo;
 		if (sig_flag==0) sig_flag=signo;
 		else /*  previous sig. not processed yet, ignoring? */
 		else /*  previous sig. not processed yet, ignoring? */
@@ -604,6 +616,11 @@ static void sig_usr(int signo)
 			case SIGINT:
 			case SIGINT:
 			case SIGPIPE:
 			case SIGPIPE:
 			case SIGTERM:
 			case SIGTERM:
+					/* print memory stats for non-main too */
+					#ifdef PKG_MALLOC
+					LOG(L_INFO, "Memory status (pkg):\n");
+					pkg_status();
+					#endif
 					exit(0);
 					exit(0);
 					break;
 					break;
 			case SIGUSR1:
 			case SIGUSR1:
@@ -675,7 +692,7 @@ int main(int argc, char** argv)
 #ifdef STATS
 #ifdef STATS
 	"s:"
 	"s:"
 #endif
 #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){
 	while((c=getopt(argc,argv,options))!=-1){
 		switch(c){
 		switch(c){
@@ -748,9 +765,6 @@ int main(int argc, char** argv)
 			case 'v':
 			case 'v':
 					check_via=1;
 					check_via=1;
 					break;
 					break;
-			case 'c':
-					loop_checks=1;
-					break;
 			case 'r':
 			case 'r':
 					received_dns|=DO_DNS;
 					received_dns|=DO_DNS;
 					break;
 					break;
@@ -847,6 +861,11 @@ int main(int argc, char** argv)
 		goto error;
 		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*/
 	register_builtin_modules();
 	register_builtin_modules();
@@ -869,17 +888,19 @@ int main(int argc, char** argv)
 
 
 	
 	
 	if (children_no<=0) children_no=CHILD_NO;
 	if (children_no<=0) children_no=CHILD_NO;
+#ifdef _OBSOLETED
 	else if (children_no >= MAX_PROCESSES ) {
 	else if (children_no >= MAX_PROCESSES ) {
 		fprintf(stderr, "ERROR: too many children processes configured;"
 		fprintf(stderr, "ERROR: too many children processes configured;"
 				" maximum is %d\n",
 				" maximum is %d\n",
 			MAX_PROCESSES-1 );
 			MAX_PROCESSES-1 );
 		goto error;
 		goto error;
 	}
 	}
+#endif
 	
 	
 	if (working_dir==0) working_dir="/";
 	if (working_dir==0) working_dir="/";
 	/*alloc pids*/
 	/*alloc pids*/
 #ifdef SHM_MEM
 #ifdef SHM_MEM
-	pids=shm_malloc(sizeof(int)*(children_no+1));
+	pids=shm_malloc(sizeof(int)*(children_no+1/*timer */+1/*fifo*/));
 #else
 #else
 	pids=malloc(sizeof(int)*(children_no+1));
 	pids=malloc(sizeof(int)*(children_no+1));
 #endif
 #endif

+ 2 - 3
md5utils.c

@@ -80,8 +80,7 @@ void MDStringArray (char *dst, str src[], int size)
   }
   }
   MDFinal (digest, &context);
   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 *
  * $Id *
  */
  */
 
 
+#include <stdio.h>
 #include "../config.h"
 #include "../config.h"
 #include "../dprint.h"
 #include "../dprint.h"
+#include "../globals.h"
 #include "mem.h"
 #include "mem.h"
 
 
 #ifdef PKG_MALLOC
 #ifdef PKG_MALLOC
@@ -42,6 +44,8 @@ int init_mallocs()
 	#endif
 	#endif
 	if (mem_block==0){
 	if (mem_block==0){
 		LOG(L_CRIT, "could not initialize memory pool\n");
 		LOG(L_CRIT, "could not initialize memory pool\n");
+		fprintf(stderr, "Too much pkg memory demanded: %d\n",
+			PKG_MEM_POOL_SIZE );
 		return -1;
 		return -1;
 	}
 	}
 #endif
 #endif
@@ -49,6 +53,8 @@ int init_mallocs()
 #ifdef SHM_MEM
 #ifdef SHM_MEM
 	if (shm_mem_init()<0) {
 	if (shm_mem_init()<0) {
 		LOG(L_CRIT, "could not initialize shared memory pool, exiting...\n");
 		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;
 		return -1;
 	}
 	}
 #endif
 #endif

+ 6 - 1
mem/memtest.c

@@ -1,5 +1,10 @@
 #ifdef DBG_QM_MALLOC
 #ifdef DBG_QM_MALLOC
 
 
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+
 #include "../globals.h"
 #include "../globals.h"
 #include "../config.h"
 #include "../config.h"
 
 
@@ -33,7 +38,7 @@ void memtest()
                                                                 __LINE__);
                                                                 __LINE__);
 	char tst_mem[TEST_SIZE];
 	char tst_mem[TEST_SIZE];
 	struct MY_BLOCK* mem_block;
 	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;
 	int i, j, f;
 	char *p[TEST_RUN];
 	char *p[TEST_RUN];
 	int t;
 	int t;

+ 37 - 2
mem/shm_mem.c

@@ -5,6 +5,8 @@
 
 
 #ifdef SHM_MEM
 #ifdef SHM_MEM
 
 
+#include <stdlib.h>
+
 #include "shm_mem.h"
 #include "shm_mem.h"
 #include "../config.h"
 #include "../config.h"
 #include "../globals.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
     guarantee for buffer content; if allocation fails, we return
     NULL
     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
 #ifdef DBG_QM_MALLOC
 void* _shm_resize( void* p, unsigned int s, char* file, char* func, unsigned int line)
 void* _shm_resize( void* p, unsigned int s, char* file, char* func, unsigned int line)
 #else
 #else
@@ -108,10 +141,11 @@ void* _shm_resize( void* p , unsigned int s)
 #	ifdef VQ_MALLOC
 #	ifdef VQ_MALLOC
 	f=(struct  vqm_frag*) ((char*)p-sizeof(struct vqm_frag));
 	f=(struct  vqm_frag*) ((char*)p-sizeof(struct vqm_frag));
 #	ifdef DBG_QM_MALLOC
 #	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);
 	VQM_DEBUG_FRAG(shm_block, f);
 	if (p>(void *)shm_block->core_end || p<(void*)shm_block->init_core){
 	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);
 				"aborting\n", p);
 		abort();
 		abort();
 	}
 	}
@@ -126,6 +160,7 @@ void* _shm_resize( void* p , unsigned int s)
 	/* we can't make the request happy with current size */
 	/* we can't make the request happy with current size */
 	return sh_realloc( p, s ); 
 	return sh_realloc( p, s ); 
 }
 }
+#endif
 
 
 
 
 
 

+ 21 - 16
mem/vq_malloc.c

@@ -49,6 +49,8 @@
 
 
 #ifdef VQ_MALLOC
 #ifdef VQ_MALLOC
 
 
+#include <stdlib.h>
+
 #include "../config.h"
 #include "../config.h"
 #include "../globals.h"
 #include "../globals.h"
 #include "vq_malloc.h"
 #include "vq_malloc.h"
@@ -79,7 +81,7 @@ void my_assert( int assertation, int line, char *file, char *function )
 {
 {
 	if (assertation) return;
 	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);
 		function, file, line);
 	abort();
 	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)
 void vqm_debug_frag(struct vqm_block* qm, struct vqm_frag* f)
 {
 {
 
 
-	int r;
 
 
 	if (f->check!=ST_CHECK_PATTERN){
 	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);
 				f, f->check);
 		vqm_status(qm);
 		vqm_status(qm);
 		abort();
 		abort();
 	};
 	};
 	if (memcmp(f->end_check, END_CHECK_PATTERN, END_CHECK_PATTERN_LEN)!=0) {
 	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 );
 				f, END_CHECK_PATTERN_LEN, f->end_check );
 		vqm_status(qm);
 		vqm_status(qm);
 		abort();
 		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 *prev, *next;
-	struct vqm_frag_end *end;
 
 
 	prev=FRAG_END(frag)->prv_free; 
 	prev=FRAG_END(frag)->prv_free; 
 	next=frag->u.nxt_free;
 	next=frag->u.nxt_free;
@@ -260,8 +260,8 @@ void* vqm_malloc(struct vqm_block* qm, unsigned int size)
 	
 	
 #ifdef DBG_QM_MALLOC
 #ifdef DBG_QM_MALLOC
 	unsigned int demanded_size;
 	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;
 	demanded_size = size;
 #endif
 #endif
 	new_chunk=0;
 	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 */
 	if (!new_chunk) { /* no chunk can be reused; slice one from the core */
 		new_chunk=MORE_CORE( qm, bucket, size );
 		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.magic = FR_USED;
 	new_chunk->u.inuse.bucket=bucket;
 	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->line=line;
 	new_chunk->demanded_size=demanded_size;
 	new_chunk->demanded_size=demanded_size;
 	qm->usage[ bucket ]++;
 	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), 
 		qm, demanded_size, (char*)new_chunk+sizeof(struct vqm_frag), 
 		bucket, size );
 		bucket, size );
 
 
@@ -317,9 +321,10 @@ void vqm_free(struct vqm_block* qm, void* p)
 	unsigned char b;
 	unsigned char b;
 
 
 #ifdef DBG_QM_MALLOC
 #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){
 	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);
 				"aborting\n", p);
 		abort();
 		abort();
 	}
 	}
@@ -389,13 +394,13 @@ void vqm_free(struct vqm_block* qm, void* p)
 
 
 void dump_frag( struct vqm_frag* f, int i )
 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);
 		(char*)f+sizeof(struct vqm_frag), f->size, f->u.inuse.bucket);
 #ifdef DBG_QM_MALLOC
 #ifdef DBG_QM_MALLOC
 	LOG(L_INFO, "            demanded size=%d\n", f->demanded_size );
 	LOG(L_INFO, "            demanded size=%d\n", f->demanded_size );
 	LOG(L_INFO, "            alloc'd from %s: %s(%d)\n",
 	LOG(L_INFO, "            alloc'd from %s: %s(%d)\n",
 		f->file, f->func, f->line);
 		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 );
 			f->check, END_CHECK_PATTERN_LEN, f->end_check );
 #endif
 #endif
 }
 }
@@ -403,9 +408,9 @@ void dump_frag( struct vqm_frag* f, int i )
 void vqm_status(struct vqm_block* qm)
 void vqm_status(struct vqm_block* qm)
 {
 {
 	struct vqm_frag* f;
 	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;
 	if (!qm) return;
 	LOG(L_INFO, " heap size= %d, available: %d\n", 
 	LOG(L_INFO, " heap size= %d, available: %d\n", 
 		qm->core_end-qm->init_core, qm->free_core );
 		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");
 	DBG("dumping bucket statistics:\n");
 	for (i=0; i<=BIG_BUCKET(qm); i++) {
 	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++;
 		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 );
 			i, qm->usage[i], on_list );
 	}
 	}
 #endif
 #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);
 struct vqm_block* vqm_malloc_init(char* address, unsigned int size);
 
 
 #ifdef DBG_QM_MALLOC
 #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, 
 void* vqm_malloc(struct vqm_block*, unsigned int size, char* file, char* func, 
 					unsigned int line);
 					unsigned int line);
 void  vqm_free(struct vqm_block*, void* p, char* file, char* func, 
 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 T_TABLE_POWER    12
 #define TABLE_ENTRIES    (1 << (T_TABLE_POWER))
 #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 */
 /* maximumum length of localy generated acknowledgement */
 #define MAX_ACK_LEN   1024
 #define MAX_ACK_LEN   1024
@@ -41,11 +46,9 @@ enum fork_type { DEFAULT, NO_RESPONSE };
 #define REPLY_OVERBUFFER_LEN 160
 #define REPLY_OVERBUFFER_LEN 160
 #define TAG_OVERBUFFER_LEN 32
 #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
 #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$
  * $Id$
  */
  */
 
 
-#include "hash_func.h"
+#include "../../mem/shm_mem.h"
+#include "../../hash_func.h"
 #include "h_table.h"
 #include "h_table.h"
 #include "../../dprint.h"
 #include "../../dprint.h"
-#include "sh_malloc.h"
 #include "../../md5utils.h"
 #include "../../md5utils.h"
 /* bogdan test */
 /* bogdan test */
 #include "../../ut.h"
 #include "../../ut.h"
 #include "../../globals.h"
 #include "../../globals.h"
 #include "../../error.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;
 	char *b;
 	int i;
 	int i;
+	struct sip_msg *rpl;
 
 
 	release_cell_lock( dead_cell );
 	release_cell_lock( dead_cell );
 	shm_lock();
 	shm_lock();
@@ -27,31 +41,35 @@ void free_cell( struct cell* dead_cell )
 		sip_msg_free_unsafe( dead_cell->uas.request );
 		sip_msg_free_unsafe( dead_cell->uas.request );
 	if ( dead_cell->uas.response.buffer )
 	if ( dead_cell->uas.response.buffer )
 		shm_free_unsafe( 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 */
 	/* UA Clients */
 	for ( i =0 ; i<dead_cell->nr_of_outgoings;  i++ )
 	for ( i =0 ; i<dead_cell->nr_of_outgoings;  i++ )
 	{
 	{
 		/* retransmission buffer */
 		/* retransmission buffer */
 		if ( (b=dead_cell->uac[i].request.buffer) )
 		if ( (b=dead_cell->uac[i].request.buffer) )
-		{
 			shm_free_unsafe( b );
 			shm_free_unsafe( b );
-			b = 0;
-		}
+#ifdef OLD_CANCEL
 		if ( (b=dead_cell->uac[i].request.ack) )
 		if ( (b=dead_cell->uac[i].request.ack) )
-		{
 			shm_free_unsafe( b );
 			shm_free_unsafe( b );
-			b = 0;
-		}
 		if ( (b=dead_cell->uac[i].request.cancel) )
 		if ( (b=dead_cell->uac[i].request.cancel) )
-		{
 			shm_free_unsafe( b );
 			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) )
 		if ( (b=dead_cell->uac[i].rpl_buffer.s) )
-		{
 			shm_free_unsafe( b );
 			shm_free_unsafe( b );
-			b = 0;
-		}
+#endif
 	}
 	}
 
 
 	/* the cell's body */
 	/* the cell's body */
@@ -67,16 +85,16 @@ struct cell*  build_cell( struct sip_msg* p_msg )
 {
 {
 	struct cell* new_cell;
 	struct cell* new_cell;
 	unsigned int i;
 	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 */
 	/* 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 ) {
 	if  ( !new_cell ) {
 		ser_error=E_OUT_OF_MEM;
 		ser_error=E_OUT_OF_MEM;
 		return NULL;
 		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.tg=TG_FR;
 	new_cell->uas.response.fr_timer.payload =
 	new_cell->uas.response.fr_timer.payload =
 		new_cell->uas.response.retr_timer.payload = &(new_cell->uas.response);
 		new_cell->uas.response.retr_timer.payload = &(new_cell->uas.response);
+	new_cell->uas.response.my_T=new_cell;
 
 
 	/* bogdan - debug */
 	/* bogdan - debug */
 	/*fprintf(stderr,"before clone VIA |%.*s|\n",via_len(p_msg->via1),
 	/*fprintf(stderr,"before clone VIA |%.*s|\n",via_len(p_msg->via1),
 		via_s(p_msg->via1,p_msg));*/
 		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;
 	new_cell->uas.response.my_T = new_cell;
 
 
 	/* UAC */
 	/* 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 */
 	/* 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->wait_tl.payload = new_cell;
 	new_cell->dele_tl.payload = new_cell;
 	new_cell->dele_tl.payload = new_cell;
 	new_cell->relaied_reply_branch   = -1;
 	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->wait_tl.tg=TG_WT;
 	new_cell->dele_tl.tg=TG_DEL;
 	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 );
 	init_cell_lock(  new_cell );
 	return new_cell;
 	return new_cell;
 
 
 error:
 error:
-	sh_free(new_cell);
+	shm_free(new_cell);
 	return NULL;
 	return NULL;
 }
 }
 
 
@@ -183,7 +210,7 @@ void free_hash_table( struct s_table *hash_table )
 		for ( i=0 ; i<NR_OF_TIMER_LISTS ; i++ )
 		for ( i=0 ; i<NR_OF_TIMER_LISTS ; i++ )
 			release_timerlist_lock( &(hash_table->timers[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;
 	int              i;
 
 
 	/*allocs the table*/
 	/*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 )
 	if ( !hash_table )
 		goto error;
 		goto error;
 
 
@@ -248,6 +275,9 @@ void insert_into_hash_table_unsafe( struct s_table *hash_table,
 	} else p_entry->first_cell = p_cell;
 	} else p_entry->first_cell = p_cell;
 
 
 	p_entry->last_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)
 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 );
 	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 */
 /*  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]);
 	struct entry*  p_entry  = &(hash_table->entrys[p_cell->hash_index]);
 
 
 	/* unlink the cell from entry list */
 	/* unlink the cell from entry list */
-	lock( &(p_entry->mutex) );
+	/* lock( &(p_entry->mutex) ); */
 
 
 	if ( p_cell->prev_cell )
 	if ( p_cell->prev_cell )
 		p_cell->prev_cell->next_cell = p_cell->next_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;
 		p_cell->next_cell->prev_cell = p_cell->prev_cell;
 	else
 	else
 		p_entry->last_cell = p_cell->prev_cell;
 		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 "../../types.h"
 #include "../../md5utils.h"
 #include "../../md5utils.h"
 #include "config.h"
 #include "config.h"
-/*#include "t_flags.h"*/
 
 
 struct s_table;
 struct s_table;
 struct entry;
 struct entry;
 struct cell;
 struct cell;
 struct timer;
 struct timer;
+struct retr_buf;
 
 
-#include "sh_malloc.h"
-
-#include "timer.h"
+#include "../../mem/shm_mem.h"
+#include "timer.h" 
 #include "lock.h"
 #include "lock.h"
 #include "sip_msg.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 )
 #define NO_CANCEL       ( (char*) 0 )
@@ -39,7 +36,14 @@ struct timer;
 #define TYPE_LOCAL_CANCEL -1
 #define TYPE_LOCAL_CANCEL -1
 #define TYPE_REQUEST       0
 #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
 typedef struct retr_buf
 {
 {
@@ -49,18 +53,10 @@ typedef struct retr_buf
 
 
 	char *buffer;
 	char *buffer;
 	int   buffer_len;
 	int   buffer_len;
-	char *ack;
-	int   ack_len;
-	char *cancel;
-	int   cancel_len;
 
 
-	/* v6 changes; -jiri
-	struct sockaddr_in to; */
 	union sockaddr_union to;
 	union sockaddr_union to;
 	struct socket_info* send_sock;
 	struct socket_info* send_sock;
 
 
-	size_t tolen;
-
 	/* a message can be linked just to retransmission and FR list */
 	/* a message can be linked just to retransmission and FR list */
 	struct timer_link retr_timer;
 	struct timer_link retr_timer;
 	struct timer_link fr_timer;
 	struct timer_link fr_timer;
@@ -81,7 +77,7 @@ typedef struct ua_server
 	struct sip_msg   *request;
 	struct sip_msg   *request;
 	struct retr_buf  response;
 	struct retr_buf  response;
 	unsigned int     status;
 	unsigned int     status;
-	str              *tag;
+	str              to_tag;
 	unsigned int     isACKed;
 	unsigned int     isACKed;
 }ua_server_type;
 }ua_server_type;
 
 
@@ -92,11 +88,21 @@ typedef struct ua_server
 typedef struct ua_client
 typedef struct ua_client
 {
 {
 	struct retr_buf  request;
 	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              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;
 }ua_client_type;
 
 
 
 
@@ -109,8 +115,40 @@ typedef struct cell
 	struct cell*     next_cell;
 	struct cell*     next_cell;
 	struct cell*     prev_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 */
 	/* tells in which hash table entry the cell lives */
 	unsigned int  hash_index;
 	unsigned int  hash_index;
 	/* sequence number within hash collision slot */
 	/* sequence number within hash collision slot */
@@ -120,47 +158,43 @@ typedef struct cell
 	struct timer_link wait_tl;
 	struct timer_link wait_tl;
 	struct timer_link dele_tl;
 	struct timer_link dele_tl;
 
 
-	/* useful data */
 	/* number of forks */
 	/* number of forks */
 	int nr_of_outgoings;
 	int nr_of_outgoings;
 	/* nr of replied branch */
 	/* nr of replied branch */
 	int relaied_reply_branch;
 	int relaied_reply_branch;
-	/* transaction that is canceled (usefull only for CANCEL req) */
-	struct cell *T_canceled;
 	/* UA Server */
 	/* UA Server */
 	struct ua_server  uas;
 	struct ua_server  uas;
 	/* UA Clients */
 	/* UA Clients */
-	struct ua_client  uac[ NR_OF_CLIENTS ];
+	struct ua_client  uac[ MAX_BRANCHES ];
 
 
 	/* protection against concurrent reply processing */
 	/* protection against concurrent reply processing */
 	ser_lock_t   reply_mutex;
 	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 */
 	/* protection against reentering WAIT state */
 	ser_lock_t	wait_mutex;
 	ser_lock_t	wait_mutex;
 	/* has the transaction been put on wait status ? */
 	/* has the transaction been put on wait status ? */
 	int on_wait;
 	int on_wait;
 #endif
 #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];
 	char md5[MD5_LEN];
-#endif
 
 
 #ifdef	EXTRA_DEBUG
 #ifdef	EXTRA_DEBUG
 	/* scheduled for deletion ? */
 	/* scheduled for deletion ? */
 	short damocles;
 	short damocles;
 #endif
 #endif
-
+	/* has the transaction been scheduled to die? */
+	enum kill_reason kr;
 }cell_type;
 }cell_type;
 
 
 
 
@@ -174,6 +208,7 @@ typedef struct entry
 	unsigned int    next_label;
 	unsigned int    next_label;
 	/* sync mutex */
 	/* sync mutex */
 	ser_lock_t      mutex;
 	ser_lock_t      mutex;
+	unsigned int	entries;
 }entry_type;
 }entry_type;
 
 
 
 
@@ -193,11 +228,15 @@ struct s_table* init_hash_table();
 void   free_hash_table( struct s_table* hash_table );
 void   free_hash_table( struct s_table* hash_table );
 void   free_cell( struct cell* dead_cell );
 void   free_cell( struct cell* dead_cell );
 struct cell*  build_cell( struct sip_msg* p_msg );
 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,
 void   insert_into_hash_table_unsafe( struct s_table *hash_table,
 		struct cell * p_cell );
 		struct cell * p_cell );
 
 
+unsigned int transaction_count( void );
+
 #endif
 #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
 static int
 	entry_semaphore=0, 
 	entry_semaphore=0, 
 	timer_semaphore=0, 
 	timer_semaphore=0, 
-	reply_semaphore=0,
+	reply_semaphore=0;
+#ifdef _OBSOLETED
 	ack_semaphore=0;
 	ack_semaphore=0;
-#ifdef WAIT
+#endif
+#ifdef _XWAIT
 static int  wait_semaphore=0;
 static int  wait_semaphore=0;
 #endif
 #endif
 /* and the maximum number of semaphores in the entry_semaphore set */
 /* and the maximum number of semaphores in the entry_semaphore set */
@@ -171,9 +173,11 @@ again:
 			semctl( entry_semaphore, 0 , IPC_RMID , 0 );
 			semctl( entry_semaphore, 0 , IPC_RMID , 0 );
 		if (reply_semaphore>0)
 		if (reply_semaphore>0)
 			semctl(reply_semaphore, 0 , IPC_RMID , 0 );
 			semctl(reply_semaphore, 0 , IPC_RMID , 0 );
+#ifdef _OBSOLETED
 		if (ack_semaphore>0)
 		if (ack_semaphore>0)
 			semctl(reply_semaphore, 0 , IPC_RMID , 0 );
 			semctl(reply_semaphore, 0 , IPC_RMID , 0 );
-#ifdef WAIT
+#endif
+#ifdef _XWAIT
 		if (wait_semaphore>0)
 		if (wait_semaphore>0)
 			semctl(wait_semaphore, 0 , IPC_RMID , 0 );
 			semctl(wait_semaphore, 0 , IPC_RMID , 0 );
 #endif
 #endif
@@ -233,7 +237,7 @@ again:
 			goto error;
 			goto error;
 		}
 		}
 	}
 	}
-	
+#ifdef _OBSOLETED	
 	if ((ack_semaphore=init_semaphore_set(sem_nr))<0){
 	if ((ack_semaphore=init_semaphore_set(sem_nr))<0){
 		if (errno==EINVAL || errno==ENOSPC ) {
 		if (errno==EINVAL || errno==ENOSPC ) {
 			DBG( "DEBUG:lock_initialize: ack semaphore initialization"
 			DBG( "DEBUG:lock_initialize: ack semaphore initialization"
@@ -247,8 +251,9 @@ again:
 			goto error;
 			goto error;
 		}
 		}
 	}
 	}
+#endif
 
 
-#ifdef WAIT
+#ifdef _XWAIT
 	if ((wait_semaphore=init_semaphore_set(sem_nr))<0){
 	if ((wait_semaphore=init_semaphore_set(sem_nr))<0){
 		if (errno==EINVAL || errno==ENOSPC ) {
 		if (errno==EINVAL || errno==ENOSPC ) {
 			DBG( "DEBUG:lock_initialize: wait semaphore initialization"
 			DBG( "DEBUG:lock_initialize: wait semaphore initialization"
@@ -281,7 +286,6 @@ error:
 void lock_cleanup()
 void lock_cleanup()
 {
 {
 	/* must check if someone uses them, for now just leave them allocated*/
 	/* 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
 #else
@@ -294,7 +298,6 @@ void lock_cleanup()
 	   no other process lives 
 	   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 */
 	/* sibling double-check missing here; install a signal handler */
 
 
 	if (entry_semaphore > 0 && 
 	if (entry_semaphore > 0 && 
@@ -306,18 +309,24 @@ void lock_cleanup()
 	if (reply_semaphore > 0 &&
 	if (reply_semaphore > 0 &&
 	    semctl( reply_semaphore, 0 , IPC_RMID , 0 )==-1)
 	    semctl( reply_semaphore, 0 , IPC_RMID , 0 )==-1)
 		LOG(L_ERR, "ERROR: lock_cleanup, reply_semaphore cleanup failed\n");
 		LOG(L_ERR, "ERROR: lock_cleanup, reply_semaphore cleanup failed\n");
+#ifdef _OBSOLETED
 	if (ack_semaphore > 0 &&
 	if (ack_semaphore > 0 &&
 	    semctl( ack_semaphore, 0 , IPC_RMID , 0 )==-1)
 	    semctl( ack_semaphore, 0 , IPC_RMID , 0 )==-1)
 		LOG(L_ERR, "ERROR: lock_cleanup, ack_semaphore cleanup failed\n");
 		LOG(L_ERR, "ERROR: lock_cleanup, ack_semaphore cleanup failed\n");
-#ifdef WAIT
+#endif
+#ifdef _XWAIT
 	if (wait_semaphore > 0 &&
 	if (wait_semaphore > 0 &&
 		semctl( wait_semaphore, 0 , IPC_RMID , 0 )==-1)
 		semctl( wait_semaphore, 0 , IPC_RMID , 0 )==-1)
 		LOG(L_ERR, "ERROR: lock_cleanup, wait_semaphore cleanup failed\n");
 		LOG(L_ERR, "ERROR: lock_cleanup, wait_semaphore cleanup failed\n");
 #endif
 #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;
 	wait_semaphore = 0;
 #endif
 #endif
 
 
@@ -333,17 +342,21 @@ int init_cell_lock( struct cell *cell )
 {
 {
 #ifdef FAST_LOCK
 #ifdef FAST_LOCK
 	init_lock(cell->reply_mutex);
 	init_lock(cell->reply_mutex);
+#ifdef _OBSOLETED
 	init_lock(cell->ack_mutex);
 	init_lock(cell->ack_mutex);
-#ifdef WAIT
+#endif
+#ifdef _XWAIT
 	init_lock(cell->wait_mutex);
 	init_lock(cell->wait_mutex);
 #endif
 #endif
 	return 0;
 	return 0;
 #else
 #else
 	cell->reply_mutex.semaphore_set=reply_semaphore;
 	cell->reply_mutex.semaphore_set=reply_semaphore;
 	cell->reply_mutex.semaphore_index = cell->hash_index % sem_nr;
 	cell->reply_mutex.semaphore_index = cell->hash_index % sem_nr;
+#ifdef _OBSOLETED
 	cell->ack_mutex.semaphore_set=ack_semaphore;
 	cell->ack_mutex.semaphore_set=ack_semaphore;
 	cell->ack_mutex.semaphore_index = cell->hash_index % sem_nr;
 	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_set=wait_semaphore;
 	cell->wait_mutex.semaphore_index = cell->hash_index % sem_nr;
 	cell->wait_mutex.semaphore_index = cell->hash_index % sem_nr;
 #endif /* WAIT */
 #endif /* WAIT */

+ 1 - 1
modules/tm/lock.h

@@ -42,7 +42,7 @@ enum timer_groups {
 
 
 
 
 #include "h_table.h"
 #include "h_table.h"
-#include "timer.h"
+#include "timer.h" 
 
 
 /* Uni*x permissions for IPC */
 /* Uni*x permissions for IPC */
 #define IPC_PERMISSIONS 0666
 #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$
  * $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>
 #include <stdio.h>
@@ -154,6 +165,12 @@ struct sip_msg*  sip_msg_cloner( struct sip_msg *org_msg )
 						len+=ROUND4(sizeof(struct via_param ));
 						len+=ROUND4(sizeof(struct via_param ));
 				}
 				}
 				break;
 				break;
+			default:
+				if (hdr->parsed) {
+					LOG(L_WARN, "WARNING: sip_msg_cloner: "
+						"header body ignored: %d\n", hdr->type );
+				}
+				break;
 		}/*switch*/
 		}/*switch*/
 	}/*for all headers*/
 	}/*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)
 	for(rpl_lump=org_msg->reply_lump;rpl_lump;rpl_lump=rpl_lump->next)
 		len+=rpl_lump->text.len;
 		len+=rpl_lump->text.len;
 
 
-	p=(char *)sh_malloc(len);foo=p;
+	p=(char *)shm_malloc(len);foo=p;
 	if (!p)
 	if (!p)
 	{
 	{
 		LOG(L_ERR , "ERROR: sip_msg_cloner: cannot allocate memory\n" );
 		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);
 			hdr->name.s);
 		new_hdr->body.s = translate_pointer(new_msg->buf, org_msg->buf,
 		new_hdr->body.s = translate_pointer(new_msg->buf, org_msg->buf,
 			hdr->body.s);
 			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)
 		switch (hdr->type)
 		{
 		{
@@ -360,6 +382,42 @@ struct sip_msg*  sip_msg_cloner( struct sip_msg *org_msg )
 			case HDR_ROUTE :
 			case HDR_ROUTE :
 				new_msg->route = new_hdr;
 				new_msg->route = new_hdr;
 				break;
 				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*/
 		}/*switch*/
 
 
 		if ( last_hdr )
 		if ( last_hdr )

+ 1 - 2
modules/tm/sip_msg.h

@@ -7,8 +7,7 @@
 #define _SIP_MSG_H
 #define _SIP_MSG_H
 
 
 #include "../../parser/msg_parser.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(_p_msg) shm_free( (_p_msg ))
 #define  sip_msg_free_unsafe(_p_msg) shm_free_unsafe( (_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
  * transaction maintenance functions
  */
  */
 
 
+#include <limits.h>
 #include "../../dprint.h"
 #include "../../dprint.h"
 #include "../../config.h"
 #include "../../config.h"
 #include "../../parser/parser_f.h"
 #include "../../parser/parser_f.h"
 #include "../../ut.h"
 #include "../../ut.h"
-#include "hash_func.h"
+#include "../../hash_func.h"
+#include "../../dset.h"
 #include "t_funcs.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;
 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()
 int tm_startup()
 {
 {
@@ -35,15 +59,12 @@ int tm_startup()
 	hash_table->timers[WT_TIMER_LIST].id     = WT_TIMER_LIST;
 	hash_table->timers[WT_TIMER_LIST].id     = WT_TIMER_LIST;
 	hash_table->timers[DELETE_LIST].id       = DELETE_LIST;
 	hash_table->timers[DELETE_LIST].id       = DELETE_LIST;
 
 
-	/* register the timer function */
-	register_timer( timer_routine , hash_table , 1 );
 
 
 	/* fork table */
 	/* 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;
 	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
 /*   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;
 	return 1;
 }
 }
 
 
 
 
-
-
-
 /* ----------------------------HELPER FUNCTIONS-------------------------------- */
 /* ----------------------------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);
 	LOCK_WAIT(Trans);
 	if (Trans->on_wait)
 	if (Trans->on_wait)
 	{
 	{
 		DBG("DEBUG: t_put_on_wait: already on wait\n");
 		DBG("DEBUG: t_put_on_wait: already on wait\n");
 		UNLOCK_WAIT(Trans);
 		UNLOCK_WAIT(Trans);
-		return 1;
 	} else {
 	} else {
 		Trans->on_wait=1;
 		Trans->on_wait=1;
 		UNLOCK_WAIT(Trans);
 		UNLOCK_WAIT(Trans);
 	}
 	}
 #endif
 #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
 #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 {
 	} 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 <netinet/in.h>
 #include <netdb.h>
 #include <netdb.h>
 
 
+#include "../../mem/shm_mem.h"
 #include "../../parser/msg_parser.h"
 #include "../../parser/msg_parser.h"
 #include "../../globals.h"
 #include "../../globals.h"
 #include "../../udp_server.h"
 #include "../../udp_server.h"
@@ -23,8 +24,9 @@
 #include "config.h"
 #include "config.h"
 #include "lock.h"
 #include "lock.h"
 #include "timer.h"
 #include "timer.h"
-#include "sh_malloc.h"
 #include "sip_msg.h"
 #include "sip_msg.h"
+#include "h_table.h"
+#include "ut.h"
 
 
 
 
 struct s_table;
 struct s_table;
@@ -32,115 +34,56 @@ struct timer;
 struct entry;
 struct entry;
 struct cell;
 struct cell;
 
 
-extern struct cell      *T;
-extern unsigned int     global_msg_id;
 extern struct s_table*  hash_table;
 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 LOCK_ACK(_t) lock(&(_t)->ack_mutex )
 #define UNLOCK_ACK(_t) unlock(&(_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
 /* send a private buffer: utilize a retransmission structure
    but take a separate buffer not refered by it; healthy
    but take a separate buffer not refered by it; healthy
    for reducing time spend in REPLIES locks
    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 ) \
 #define SEND_PR_BUFFER(_rb,_bf,_le ) \
 	send_pr_buffer( (_rb), (_bf), (_le),  __FUNCTION__, __LINE__ )
 	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 ) \
 #define SEND_BUFFER( _rb ) \
 	SEND_PR_BUFFER( (_rb) , (_rb)->buffer , (_rb)->buffer_len )
 	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();
 int   tm_startup();
@@ -154,225 +97,30 @@ void tm_shutdown();
 int  t_add_transaction( struct sip_msg* p_msg  );
 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
 /* 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 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,
 int get_ip_and_port_from_uri( str* uri , unsigned int *param_ip,
 	unsigned int *param_port);
 	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*);
 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
 #endif
 
 

+ 336 - 329
modules/tm/t_fwd.c

@@ -8,13 +8,20 @@
 #include "../../parser/parser_f.h"
 #include "../../parser/parser_f.h"
 #include "../../ut.h"
 #include "../../ut.h"
 #include "../../timer.h"
 #include "../../timer.h"
-#include "hash_func.h"
+#include "../../hash_func.h"
+#include "../../globals.h"
+#include "../../dset.h"
 #include "t_funcs.h"
 #include "t_funcs.h"
-#include "t_fork.h"
-
 #include "t_hooks.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) \
 #define shm_free_lump( _lmp) \
 	do{\
 	do{\
 		if ((_lmp)) {\
 		if ((_lmp)) {\
@@ -23,367 +30,367 @@
 			shm_free((_lmp));\
 			shm_free((_lmp));\
 		}\
 		}\
 	}while(0);
 	}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;
 		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;
 			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:
 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;
 	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;
 		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;
 		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:
 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$
  * $Id$
  */
  */
 
 
-
+#include "stdlib.h"
+#include "../../dprint.h"
+#include "../../error.h"
 #include "t_hooks.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;
 static int callback_id=0;
 
 
 /* register a callback function 'f' of type 'cbt'; will be called
 /* register a callback function 'f' of type 'cbt'; will be called
    back whenever the event 'cbt' occurs in transaction module
    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;
 	struct tm_callback_s *cbs;
 
 
@@ -30,19 +32,19 @@ int register_tmcb( tmcb_type cbt, transaction_cb f )
 	cbs->id=callback_id;
 	cbs->id=callback_id;
 	cbs->callback=f;
 	cbs->callback=f;
 	cbs->next=callback_array[ cbt ];
 	cbs->next=callback_array[ cbt ];
+	cbs->param=param;
 	callback_array[ cbt ]=cbs;
 	callback_array[ cbt ]=cbs;
 
 
 	return callback_id;
 	return callback_id;
 }
 }
 
 
 void callback_event( tmcb_type cbt , struct cell *trans,
 void callback_event( tmcb_type cbt , struct cell *trans,
-	struct sip_msg *msg )
+	struct sip_msg *msg, int code )
 {
 {
 	struct tm_callback_s *cbs;
 	struct tm_callback_s *cbs;
 
 
-	DBG("DBG: callback type %d entered\n", cbt );
 	for (cbs=callback_array[ cbt ]; cbs; cbs=cbs->next)  {
 	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
 #ifndef _HOOKS_H
 #define _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, 
 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
 	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_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
 	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 {
 struct tm_callback_s {
 	int id;
 	int id;
 	transaction_cb* callback;
 	transaction_cb* callback;
 	struct tm_callback_s* next;
 	struct tm_callback_s* next;
+	void *param;
 };
 };
 
 
 
 
 extern struct tm_callback_s* callback_array[ TMCB_END ];
 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,
 void callback_event( tmcb_type cbt, struct cell *trans,
-	struct sip_msg *msg );
+	struct sip_msg *msg, int code );
 
 
 #endif
 #endif

+ 305 - 386
modules/tm/t_lookup.c

@@ -21,7 +21,7 @@
  *
  *
  * The branch parameter is formed as follows:
  * The branch parameter is formed as follows:
  * SYNONYMS  on: hash.synonym.branch
  * SYNONYMS  on: hash.synonym.branch
- * SYNONYMS off: md5.hash.branch
+ * SYNONYMS off: hash.md5.branch
  *
  *
  * -jiri
  * -jiri
  *
  *
@@ -33,11 +33,14 @@
 #include "../../parser/parser_f.h"
 #include "../../parser/parser_f.h"
 #include "../../ut.h"
 #include "../../ut.h"
 #include "../../timer.h"
 #include "../../timer.h"
-#include "hash_func.h"
+#include "../../hash_func.h"
+#include "../../globals.h"
+#include "../../forward.h"
 #include "t_funcs.h"
 #include "t_funcs.h"
 #include "config.h"
 #include "config.h"
 #include "sip_msg.h"
 #include "sip_msg.h"
 #include "t_hooks.h"
 #include "t_hooks.h"
+#include "t_lookup.h"
 
 
 
 
 #define EQ_LEN(_hf) (t_msg->_hf->body.len==p_msg->_hf->body.len)
 #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)))\
 	 (t_msg->via1->bsize-(t_msg->_via->name.s-(t_msg->_via->hdr.s+t_msg->_via->hdr.len)))\
 	)==0 )
 	)==0 )
 
 
+#define HF_LEN(_hf) ((_hf)->body.s+(_hf)->body.len-(_hf)->name.s)
+
 /* presumably matching transaction for an e2e ACK */
 /* presumably matching transaction for an e2e ACK */
 static struct cell *t_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:
 /* function returns:
  *      negative - transaction wasn't found
  *      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 */
 	/* 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;
 	isACK = p_msg->REQ_METHOD==METHOD_ACK;
 	DBG("t_lookup_request: start searching: hash=%d, isACK=%d\n",
 	DBG("t_lookup_request: start searching: hash=%d, isACK=%d\n",
 		p_msg->hash_index,isACK);
 		p_msg->hash_index,isACK);
@@ -96,7 +126,7 @@ int t_lookup_request( struct sip_msg* p_msg , int leave_new_locked )
 	ret=-1;
 	ret=-1;
 
 
 	/* lock the hole entry*/
 	/* 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 */
 	/* all the transactions from the entry are compared */
 	for ( p_cell = hash_table->entrys[p_msg->hash_index].first_cell;
 	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)
 			if (get_to(t_msg)->uri.len!=get_to(p_msg)->uri.len)
 				continue;
 				continue;
 			/* ... its to-tag compared to reply's tag */
 			/* ... 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;
 				continue;
 
 
 			/* we first skip r-uri and Via and proceed with
 			/* 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 (!EQ_STR(from)) continue;
 			if (memcmp(get_to(t_msg)->uri.s, get_to(p_msg)->uri.s,
 			if (memcmp(get_to(t_msg)->uri.s, get_to(p_msg)->uri.s,
 				get_to(t_msg)->uri.len)!=0) continue;
 				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
 			/* 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
 			   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 */
 	/* no transaction found */
 	T = 0;
 	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");
 	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:
 found:
 	T=p_cell;
 	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;
 	return 1;
 }
 }
 
 
-#endif
-
 
 
 
 
 /* function returns:
 /* function returns:
@@ -365,99 +240,67 @@ found:
  *       T - transaction found
  *       T - transaction found
  */
  */
 struct cell* t_lookupOriginalT(  struct s_table* hash_table ,
 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     *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;
 	hash_index = p_msg->hash_index;
+	LOCK_HASH(hash_index);
 	DBG("DEBUG: t_lookupOriginalT: searching on hash entry %d\n",hash_index );
 	DBG("DEBUG: t_lookupOriginalT: searching on hash entry %d\n",hash_index );
 
 
 	/* all the transactions from the entry are compared */
 	/* 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;
 		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 */
 	/* no transaction found */
+	UNLOCK_HASH(hash_index);
 	DBG("DEBUG: t_lookupOriginalT: no CANCEL maching found! \n" );
 	DBG("DEBUG: t_lookupOriginalT: no CANCEL maching found! \n" );
 	return 0;
 	return 0;
 }
 }
@@ -468,8 +311,7 @@ struct cell* t_lookupOriginalT(  struct s_table* hash_table ,
 /* Returns 0 - nothing found
 /* Returns 0 - nothing found
  *         1  - T 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;
 	struct cell*  p_cell;
 	int hash_index   = 0;
 	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;
 	char  *hashi, *branchi, *p, *n;
 	int hashl, branchl;
 	int hashl, branchl;
 	int scan_space;
 	int scan_space;
-#ifndef USE_SYNONIM
+	str cseq_method;
+	str req_method;
+
 	char *loopi;
 	char *loopi;
 	int loopl;
 	int loopl;
-#else
 	char *syni;
 	char *syni;
 	int synl;
 	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),
 	/* split the branch into pieces: loop_detection_check(ignored),
 	 hash_table_id, synonym_id, branch_id */
 	 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;
 	p=p_msg->via1->branch->value.s;
 	scan_space=p_msg->via1->branch->value.len;
 	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 */
 	/* hash_id */
 	n=eat_token2_end( p, p+scan_space, BRANCH_SEPARATOR);
 	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;
 	hashi=p;
 	p=n+1;scan_space--;
 	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 */
 	/* branch id  -  should exceed the scan_space */
 	n=eat_token_end( p, p+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;
 	branchi=p;
 
 
 	/* sanity check */
 	/* 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 "
 		DBG("DEBUG: t_reply_matching: poor reply lables %d label %d "
 			"branch %d\n",hash_index, entry_label, branch_id );
 			"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
 	/* search the hash table list at entry 'hash_index'; lock the
 	   entry first 
 	   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; 
 	for (p_cell = hash_table->entrys[hash_index].first_cell; p_cell; 
 		p_cell=p_cell->next_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;
 				continue;
-		#endif
+		} else {
+			if ( memcmp(p_cell->md5, loopi,MD5_LEN)!=0)
+					continue;
+		}
+
 		/* sanity check ... too high branch ? */
 		/* sanity check ... too high branch ? */
 		if ( branch_id>=p_cell->nr_of_outgoings )
 		if ( branch_id>=p_cell->nr_of_outgoings )
 			continue;
 			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
 		/* we passed all disqualifying factors .... the transaction has been
 		   matched !
 		   matched !
 		*/
 		*/
 		T=p_cell;
 		T=p_cell;
 		*p_branch = branch_id;
 		*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;
 		return 1;
 	} /* for cycle */
 	} /* for cycle */
 
 
 	/* nothing found */
 	/* nothing found */
-	unlock(&(hash_table->entrys[hash_index].mutex));
+	UNLOCK_HASH(hash_index);
 	DBG("DEBUG: t_reply_matching: no matching transaction exists\n");
 	DBG("DEBUG: t_reply_matching: no matching transaction exists\n");
 
 
 nomatch2:
 nomatch2:
@@ -612,10 +463,9 @@ nomatch2:
   * for current message exists;
   * for current message exists;
   * it returns 1 if found, 0 if not found, -1 on error
   * 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_branch;
-	int local_cancel;
 
 
 	/* is T still up-to-date ? */
 	/* is T still up-to-date ? */
 	DBG("DEBUG: t_check: msg id=%d global id=%d T start=%p\n", 
 	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;
 				return -1;
 			t_lookup_request( p_msg , 0 /* unlock before returning */ );
 			t_lookup_request( p_msg , 0 /* unlock before returning */ );
 		} else {
 		} 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;
 				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 ,
 			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
 #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) ;
 	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:
 /* 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
 	+1	if a request did not match a transaction
 		- it that was an ack, the calling function
 		- 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
 		  introduced and the calling function
 		  shall reply/relay/whatever_appropriate
 		  shall reply/relay/whatever_appropriate
 
 
-	0 on error
+	0 on retransmission
 */
 */
 int t_newtran( struct sip_msg* p_msg )
 int t_newtran( struct sip_msg* p_msg )
 {
 {
 
 
 	int ret, lret;
 	int ret, lret;
 	struct cell *new_cell;
 	struct cell *new_cell;
+	struct sip_msg *shm_msg;
+	int a,b,c;
+
+	ret=1;
 
 
 	/* is T still up-to-date ? */
 	/* is T still up-to-date ? */
 	DBG("DEBUG: t_addifnew: msg id=%d , global msg id=%d ,"
 	DBG("DEBUG: t_addifnew: msg id=%d , global msg id=%d ,"
 		" T on entrance=%p\n",p_msg->id,global_msg_id,T);
 		" 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;
 	global_msg_id = p_msg->id;
 	T = T_UNDEFINED;
 	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 ... */
 	/* on error, pass the error in the stack ... */
-	if (lret==0) return 0;
+	if (lret==0) return E_BAD_TUPEL;
 	if (lret<0) {
 	if (lret<0) {
+		new_cell=0;
 		/* transaction not found, it's a new request;
 		/* transaction not found, it's a new request;
 		   establish a new transaction (unless it is an ACK) */
 		   establish a new transaction (unless it is an ACK) */
-		ret=1;
 		if ( p_msg->REQ_METHOD!=METHOD_ACK ) {
 		if ( p_msg->REQ_METHOD!=METHOD_ACK ) {
 			/* add new transaction */
 			/* add new transaction */
 			new_cell = build_cell( p_msg ) ;
 			new_cell = build_cell( p_msg ) ;
 			if  ( !new_cell ){
 			if  ( !new_cell ){
 				LOG(L_ERR, "ERROR: t_addifnew: out of mem:\n");
 				LOG(L_ERR, "ERROR: t_addifnew: out of mem:\n");
-				ret = 0;
+				ret = E_OUT_OF_MEM;
 			} else {
 			} else {
 				insert_into_hash_table_unsafe( hash_table , new_cell );
 				insert_into_hash_table_unsafe( hash_table , new_cell );
 				T=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 */
 		/* was it an e2e ACK ? if so, trigger a callback */
 		if (lret==-2) {
 		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 */
 		} 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;
 		return ret;
 	} else {
 	} 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
  * message printing
  */
  */
 
 
-#include "hash_func.h"
+#include "../../hash_func.h"
+#include "../../globals.h"
 #include "t_funcs.h"
 #include "t_funcs.h"
 #include "../../dprint.h"
 #include "../../dprint.h"
 #include "../../config.h"
 #include "../../config.h"
 #include "../../parser/parser_f.h"
 #include "../../parser/parser_f.h"
 #include "../../ut.h"
 #include "../../ut.h"
 #include "../../parser/msg_parser.h"
 #include "../../parser/msg_parser.h"
+#include "t_msgbuilder.h"
+#include "uac.h"
 
 
 
 
 
 
@@ -19,270 +22,256 @@
 			memcpy((_d),(_s),(_len));\
 			memcpy((_d),(_s),(_len));\
 			(_d) += (_len);\
 			(_d) += (_len);\
 		}while(0);
 		}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;
 	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*/
 	/*via*/
-	if ( add_branch_label(Trans,p_msg,branch)==-1 )
+	if (!t_calc_branch(Trans,  branch, 
+		branch_buf, &branch_len ))
 		goto error;
 		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)
 	if (!via)
 	{
 	{
 		LOG(L_ERR, "ERROR: t_build_and_send_CANCEL: "
 		LOG(L_ERR, "ERROR: t_build_and_send_CANCEL: "
 			"no via header got from builder\n");
 			"no via header got from builder\n");
 		goto error;
 		goto error;
 	}
 	}
-	len+= via_len;
+	*len+= via_len;
 	/*headers*/
 	/*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)
 	if (!cancel_buf)
 	{
 	{
 		LOG(L_ERR, "ERROR: t_build_and_send_CANCEL: cannot allocate memory\n");
 		LOG(L_ERR, "ERROR: t_build_and_send_CANCEL: cannot allocate memory\n");
-		goto error;
+		goto error01;
 	}
 	}
 	p = cancel_buf;
 	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 );
 	append_mem_block( p, " " SIP_VERSION CRLF, 1+SIP_VERSION_LEN+CRLF_LEN );
 
 
 	/* insert our via */
 	/* insert our via */
 	append_mem_block(p,via,via_len);
 	append_mem_block(p,via,via_len);
 
 
 	/*other headers*/
 	/*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);
 	pkg_free(via);
-	return 1;
+	return cancel_buf;
+error01:
+	pkg_free(via);
 error:
 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;
 		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;
 		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;
 		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:
 error1:
-	pkg_free(via );
+	pkg_free(via);	
 error:
 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 "t_funcs.h"
 #include "../../dprint.h"
 #include "../../dprint.h"
 #include "../../config.h"
 #include "../../config.h"
 #include "../../parser/parser_f.h"
 #include "../../parser/parser_f.h"
 #include "../../ut.h"
 #include "../../ut.h"
 #include "../../timer.h"
 #include "../../timer.h"
+#include "../../error.h"
+#include "../../action.h"
+#include "../../dset.h"
 
 
 #include "t_hooks.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.
 /* Retransmits the last sent inbound reply.
  * input: p_msg==request for which I want to retransmit an associated reply
  * input: p_msg==request for which I want to retransmit an associated reply
  * Returns  -1 - error
  * Returns  -1 - error
  *           1 - OK
  *           1 - OK
  */
  */
-int t_retransmit_reply( /* struct sip_msg* p_msg    */ )
+int t_retransmit_reply( struct cell *t )
 {
 {
 	static char b[BUF_SIZE];
 	static char b[BUF_SIZE];
 	int len;
 	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;
 		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;
 	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;
 	unsigned int len, buf_len=0;
 	char * buf;
 	char * buf;
 	struct retr_buf *rb;
 	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)
 	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;
 		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;
 			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 ) ;
 	pkg_free( buf ) ;
-	DBG("DEBUG: t_send_reply: finished\n");
+	DBG("DEBUG: t_reply: finished\n");
 	return 1;
 	return 1;
 
 
 error2:
 error2:
+	if (lock) UNLOCK_REPLIES( trans );
 	pkg_free ( buf );
 	pkg_free ( buf );
 error:
 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;
 	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:
 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; 
 /*  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 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 timers *** */
 	/* stop retransmission */
 	/* 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 */
 	/* 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;
 	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
  *  based on the current
  * transactin status.
  * transactin status.
  * Returns 	- branch number (0,1,...) which should be relayed
  * Returns 	- branch number (0,1,...) which should be relayed
  *         -1 if nothing to 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;
 	/* note: this code never lets replies to CANCEL go through;
 	   we generate always a local 200 for CANCEL; 200s are
 	   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  && 
 		if (new_code>=200 && new_code < 300  && 
 			Trans->uas.request->REQ_METHOD==METHOD_INVITE) {
 			Trans->uas.request->REQ_METHOD==METHOD_INVITE) {
 			DBG("DBG: t_should_relay: 200 INV after final sent\n");
 			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 {
 		} else {
+			/* except the exception above, too late  messages will
+			   be discarded */
 			*should_store=0;
 			*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;
 			*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
  * Timer handlers
  */
  */
 
 
-#include "hash_func.h"
-#include "t_funcs.h"
+#include "../../hash_func.h"
 #include "../../dprint.h"
 #include "../../dprint.h"
 #include "../../config.h"
 #include "../../config.h"
 #include "../../parser/parser_f.h"
 #include "../../parser/parser_f.h"
 #include "../../ut.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*/
 	/*the transaction is already removed from RETRANSMISSION_LIST by timer*/
 	/* retransmision */
 	/* 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 );
 			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;
 	id = r_buf->retr_list;
@@ -59,66 +202,126 @@ inline void retransmission_handler( void *attr)
 
 
 inline void final_response_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"
 		LOG( L_ERR, "ERROR: transaction %p scheduled for deletion and"
 			" called from FR timer\n",r_buf->my_T);
 			" called from FR timer\n",r_buf->my_T);
 		abort();
 		abort();
 	}
 	}
-#endif
+#	endif
+
+	reset_timer( hash_table , &(r_buf->retr_timer) );
 
 
 	/* the transaction is already removed from FR_LIST by the timer */
 	/* the transaction is already removed from FR_LIST by the timer */
+
+	/* FR for local cancels.... */
 	if (r_buf->activ_type==TYPE_LOCAL_CANCEL)
 	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;
 		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");
 	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)
 inline void wait_handler( void *attr)
@@ -131,20 +334,23 @@ inline void wait_handler( void *attr)
 			" called from WAIT timer\n",p_cell);
 			" called from WAIT timer\n",p_cell);
 		abort();
 		abort();
 	}	
 	}	
+	DBG("DEBUG: ---------- WAIT timer hit ------- \n");
 #endif
 #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 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 );
 	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 */
 	/* 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
 #ifdef EXTRA_DEBUG
 	p_cell->damocles = 1;
 	p_cell->damocles = 1;
 #endif
 #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");
 	DBG("DEBUG: wait_handler : done\n");
 }
 }
 
 
@@ -163,7 +369,17 @@ inline void delete_handler( void *attr)
 		abort();
 		abort();
 	}	
 	}	
 #endif
 #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");
     DBG("DEBUG: delete_handler : done\n");
 }
 }
 
 
@@ -178,7 +394,8 @@ inline void delete_handler( void *attr)
 		(_tl)->next_tl = (_tl)->prev_tl = 0;\
 		(_tl)->next_tl = (_tl)->prev_tl = 0;\
 		DBG("DEBUG: timer routine:%d,tl=%p next=%p\n",\
 		DBG("DEBUG: timer routine:%d,tl=%p next=%p\n",\
 			id,(_tl),tmp_tl);\
 			id,(_tl),tmp_tl);\
-		(_handler)( (_tl)->payload );\
+		if ((_tl)->time_out>TIMER_DELETED) \
+			(_handler)( (_tl)->payload );\
 		(_tl) = tmp_tl;\
 		(_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 "t_funcs.h"
 #include "../../dprint.h"
 #include "../../dprint.h"
 #include "../../config.h"
 #include "../../config.h"

+ 153 - 2
modules/tm/timer.c

@@ -1,7 +1,74 @@
 /*
 /*
  * $Id$
  * $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 "config.h"
 #include "h_table.h"
 #include "h_table.h"
@@ -84,6 +151,10 @@ void remove_timer_unsafe(  struct timer_link* tl )
 	};
 	};
 #endif
 #endif
 	if (is_in_timer_list2( tl )) {
 	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->prev_tl->next_tl = tl->next_tl;
 		tl->next_tl->prev_tl = tl->prev_tl;
 		tl->next_tl->prev_tl = tl->prev_tl;
 		tl->next_tl = 0;
 		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;
 /* put a new cell into a list nr. list_id within a hash_table;
    set initial timeout */
    set initial timeout */
 void add_timer_unsafe( struct timer *timer_list, struct timer_link *tl,
 void add_timer_unsafe( struct timer *timer_list, struct timer_link *tl,
-													unsigned int time_out )
+	unsigned int time_out )
 {
 {
 #ifdef EXTRA_DEBUG
 #ifdef EXTRA_DEBUG
 	if (timer_list->last_tl.prev_tl==0) {
 	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 */
 /* detach items passed by the time from timer list */
 struct timer_link  *check_and_split_time_list( struct timer *timer_list,
 struct timer_link  *check_and_split_time_list( struct timer *timer_list,
-																int time )
+	int time )
 {
 {
 	struct timer_link *tl , *end, *ret;
 	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
 #ifndef _TIMER_H
 #define _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;
 struct timer;
 
 
 #include "lock.h"
 #include "lock.h"
+#include "t_funcs.h"
 
 
 
 
 /* all you need to put a cell in a timer list
 /* 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 *next_tl;
 	struct timer_link *prev_tl;
 	struct timer_link *prev_tl;
-	unsigned int       time_out;
+	volatile unsigned int       time_out;
 	void              *payload;
 	void              *payload;
 	struct timer      *timer_list;
 	struct timer      *timer_list;
 	enum timer_groups  tg;
 	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);
 void add_timer_unsafe( struct timer*, struct timer_link*, unsigned int);
 struct timer_link  *check_and_split_time_list( struct timer*, 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
 #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 "tm_load.h"
+#include "uac.h"
+
+#define LOAD_ERROR "ERROR: tm_bind: TM module function "
 
 
 int load_tm( struct tm_binds *tmb)
 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;
 		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;
 		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;
 		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;
 		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;
 		return -1;
 	}
 	}
 
 

+ 19 - 9
modules/tm/tm_load.h

@@ -7,20 +7,30 @@
 
 
 #include "../../sr_module.h"
 #include "../../sr_module.h"
 #include "t_hooks.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_to;
 	cmd_function 	t_relay;
 	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 "data_lump_rpl.h"
 #include "ip_addr.h"
 #include "ip_addr.h"
 #include "resolve.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) \
 #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)
 char * warning_builder( struct sip_msg *msg, unsigned int *returned_len)
 {
 {
 	static char buf[MAX_WARNING_LEN];
 	static char buf[MAX_WARNING_LEN];
 	static unsigned int fix_len=0;
 	static unsigned int fix_len=0;
 	str *foo;
 	str *foo;
-	char *p;
+	int print_len;
 
 
 	if (!fix_len)
 	if (!fix_len)
 	{
 	{
@@ -209,42 +113,28 @@ char * warning_builder( struct sip_msg *msg, unsigned int *returned_len)
 		fix_len += 24;
 		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*/
 	/*adding out_uri*/
 	if (msg->new_uri.s)
 	if (msg->new_uri.s)
 		foo=&(msg->new_uri);
 		foo=&(msg->new_uri);
 	else
 	else
 		foo=&(msg->first_line.u.request.uri);
 		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);
 	buf=pkg_malloc(sizeof(char)*MAX_RECEIVED_SIZE);
 	if (buf==0){
 	if (buf==0){
 		ser_error=E_OUT_OF_MEM;
 		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;
 		return 0;
 	}
 	}
 	/*
 	/*
@@ -312,7 +202,7 @@ static inline int lumps_len(struct lump* l)
 					break;
 					break;
 				default:
 				default:
 					/* only ADD allowed for before/after */
 					/* 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);
 							"for data lump (%x)\n", r->op);
 			}
 			}
 		}
 		}
@@ -341,7 +231,7 @@ static inline int lumps_len(struct lump* l)
 				/* do nothing */
 				/* do nothing */
 				break;
 				break;
 			default:
 			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);
 							" op for data lump (%x)\n", r->op);
 		}
 		}
 		for (r=t->after;r;r=r->after){
 		for (r=t->after;r;r=r->after){
@@ -351,7 +241,7 @@ static inline int lumps_len(struct lump* l)
 					break;
 					break;
 				default:
 				default:
 					/* only ADD allowed for before/after */
 					/* 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);
 								" op for data lump (%x)\n", r->op);
 			}
 			}
 		}
 		}
@@ -391,7 +281,7 @@ static /*inline*/ void process_lumps(	struct lump* l,	char* new_buf,
 							break;
 							break;
 						default:
 						default:
 							/* only ADD allowed for before/after */
 							/* 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);
 									"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;
 							break;
 						default:
 						default:
 							/* only ADD allowed for before/after */
 							/* 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);
 									"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;
 							break;
 						default:
 						default:
 							/* only ADD allowed for before/after */
 							/* 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);
 									"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;
 							break;
 						default:
 						default:
 							/* only ADD allowed for before/after */
 							/* 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);
 									"invalid op for data lump (%x)\n", r->op);
 					}
 					}
 				}
 				}
 				break;
 				break;
 			default:
 			default:
-					LOG(L_CRIT, "BUG: build_req_buf_from_sip_req: "
+					LOG(L_CRIT, "BUG: process_lumps: "
 							"unknown op (%x)\n", t->op);
 							"unknown op (%x)\n", t->op);
 		}
 		}
 	}
 	}
@@ -487,6 +377,7 @@ char * build_req_buf_from_sip_req( struct sip_msg* msg,
 	char  backup;
 	char  backup;
 	unsigned int offset, s_offset, size;
 	unsigned int offset, s_offset, size;
 	struct lump* anchor;
 	struct lump* anchor;
+	int r;
 
 
 	uri_len=0;
 	uri_len=0;
 	orig=msg->orig;
 	orig=msg->orig;
@@ -497,27 +388,29 @@ char * build_req_buf_from_sip_req( struct sip_msg* msg,
 	received_buf=0;
 	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){
 	if (!line_buf){
 		LOG(L_ERR,"ERROR: build_req_buf_from_sip_req: no via received!\n");
 		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 */
 	/* check if received needs to be added */
 	backup = msg->via1->host.s[msg->via1->host.len];
 	backup = msg->via1->host.s[msg->via1->host.len];
 	msg->via1->host.s[msg->via1->host.len] = 0;
 	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)
 		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 */
 	/* add via header to the list */
 	/* try to add it before msg. 1st via */
 	/* try to add it before msg. 1st via */
 	/* add first via, as an anchor for second via*/
 	/* add first via, as an anchor for second via*/
 	anchor=anchor_lump(&(msg->add_rm), msg->via1->hdr.s-buf, 0, HDR_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)
 	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 needs to be added, add anchor after host and add it */
 	if (received_len){
 	if (received_len){
 		if (msg->via1->params.s){
 		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,
 		anchor=anchor_lump(&(msg->add_rm),msg->via1->hdr.s-buf+size,0,
 				HDR_VIA);
 				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)
 		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*/
 	/* 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;
 		uri_len=msg->new_uri.len;
 		new_len=new_len-msg->first_line.u.request.uri.len+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){
 	if (new_buf==0){
 		ser_error=E_OUT_OF_MEM;
 		ser_error=E_OUT_OF_MEM;
 		LOG(L_ERR, "ERROR: build_req_buf_from_sip_req: out of memory\n");
 		LOG(L_ERR, "ERROR: build_req_buf_from_sip_req: out of memory\n");
-		goto error;
+		goto error00;
 	}
 	}
 
 
 	offset=s_offset=0;
 	offset=s_offset=0;
@@ -576,19 +469,16 @@ char * build_req_buf_from_sip_req( struct sip_msg* msg,
 	*returned_len=new_len;
 	*returned_len=new_len;
 	return new_buf;
 	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);
 	if (received_buf) pkg_free(received_buf);
-error:
-	if (new_buf) local_free(new_buf);
+error00:
 	*returned_len=0;
 	*returned_len=0;
 	return 0;
 	return 0;
 }
 }
 
 
 
 
-
-
 char * build_res_buf_from_sip_res( struct sip_msg* msg,
 char * build_res_buf_from_sip_res( struct sip_msg* msg,
 				unsigned int *returned_len)
 				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;
 		via_len+=msg->via1->hdr.len+1;
 	}
 	}
 	/* remove the first via*/
 	/* 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"
 		LOG(L_ERR, "build_res_buf_from_sip_res: error trying to remove first"
 					"via\n");
 					"via\n");
 		goto error;
 		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);
 	new_len=len+lumps_len(msg->repl_add_rm);
 
 
 	DBG(" old size: %d, new size: %d\n", len, new_len);
 	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){
 	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;
 		goto error;
 	}
 	}
 	new_buf[new_len]=0; /* debug: print the message */
 	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;
 	*returned_len=new_len;
 	return new_buf;
 	return new_buf;
 error:
 error:
-	if (new_buf) local_free(new_buf);
 	*returned_len=0;
 	*returned_len=0;
 	return 0;
 	return 0;
 }
 }
@@ -666,29 +554,38 @@ char * build_res_buf_from_sip_req( unsigned int code, char *text,
 	char              backup;
 	char              backup;
 	char              *received_buf;
 	char              *received_buf;
 	int               received_len;
 	int               received_len;
-#ifdef VERY_NOISY_REPLIES
 	char              *warning;
 	char              *warning;
 	unsigned int      warning_len;
 	unsigned int      warning_len;
-#endif
+	int r;
 
 
 	received_buf=0;
 	received_buf=0;
 	received_len=0;
 	received_len=0;
 	buf=0;
 	buf=0;
+	/* make -Wall happy */
+	warning=0;
 
 
 	/* force parsing all headers -- we want to return all
 	/* force parsing all headers -- we want to return all
 	Via's in the reply and they may be scattered down to the
 	Via's in the reply and they may be scattered down to the
 	end of header (non-block Vias are a really poor property
 	end of header (non-block Vias are a really poor property
 	of SIP :( ) */
 	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 */
 	/* check if received needs to be added */
 	backup = msg->via1->host.s[msg->via1->host.len];
 	backup = msg->via1->host.s[msg->via1->host.len];
 	msg->via1->host.s[msg->via1->host.len] = 0;
 	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;
 	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*/
 	/*computes the lenght of the new response buffer*/
 	len = 0;
 	len = 0;
@@ -718,25 +615,29 @@ char * build_res_buf_from_sip_req( unsigned int code, char *text,
 	/*lumps length*/
 	/*lumps length*/
 	for(lump=msg->reply_lump;lump;lump=lump->next)
 	for(lump=msg->reply_lump;lump;lump=lump->next)
 		len += lump->text.len;
 		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 */
 	/* end of message */
 	len += CRLF_LEN; /*new line*/
 	len += CRLF_LEN; /*new line*/
 	/*allocating mem*/
 	/*allocating mem*/
-	buf = (char*) local_malloc( len+1 );
+	buf = (char*) pkg_malloc( len+1 );
 	if (!buf)
 	if (!buf)
 	{
 	{
 		LOG(L_ERR, "ERROR: build_res_buf_from_sip_req: out of memory "
 		LOG(L_ERR, "ERROR: build_res_buf_from_sip_req: out of memory "
 			" ; needs %d\n",len);
 			" ; needs %d\n",len);
-		goto error;
+		goto error01;
 	}
 	}
 
 
 	/* filling the buffer*/
 	/* 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);
 		memcpy(p,lump->text.s,lump->text.len);
 		p += 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*/
 	/*end of message*/
 	memcpy( p, CRLF, CRLF_LEN );
 	memcpy( p, CRLF, CRLF_LEN );
 	p+=CRLF_LEN;
 	p+=CRLF_LEN;
 	*(p) = 0;
 	*(p) = 0;
 	*returned_len = len;
 	*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;
 	return buf;
-error:
-	if (buf) local_free(buf);
+
+error01:
+	if (received_buf) pkg_free(received_buf);
+error00:
 	*returned_len=0;
 	*returned_len=0;
 	return 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 ": "
 #define MY_HF_SEP_LEN 2
 #define MY_HF_SEP_LEN 2
 
 
+#define BRANCH_SEPARATOR '.'
+
 #include "parser/msg_parser.h"
 #include "parser/msg_parser.h"
 #include "ip_addr.h"
 #include "ip_addr.h"
 
 
@@ -23,8 +25,22 @@ char * build_res_buf_from_sip_req(	unsigned int code ,
 				unsigned int new_tag_len ,
 				unsigned int new_tag_len ,
 				struct sip_msg* msg,
 				struct sip_msg* msg,
 				unsigned int *returned_len);
 				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);
 				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
 #endif

+ 10 - 0
parser/hf.c

@@ -74,3 +74,13 @@ void free_hdr_field_lst(struct hdr_field* hf)
 		pkg_free(foo);
 		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 */
 /* 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_ERROR                0   /* Error while parsing */
 #define HDR_VIA                  1   /* Via header field */
 #define HDR_VIA                  1   /* Via header field */
 #define HDR_VIA1                 1   /* First 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 free_hdr_field_lst(struct hdr_field* hf);
 
 
+void dump_hdr_field( struct hdr_field* hf );
 
 
 #endif /* HF_H */
 #endif /* HF_H */

+ 17 - 14
parser/msg_parser.c

@@ -20,6 +20,7 @@
 #include "parse_hname2.h"
 #include "parse_hname2.h"
 
 
 
 
+
 #ifdef DEBUG_DMALLOC
 #ifdef DEBUG_DMALLOC
 #include <mem/dmalloc.h>
 #include <mem/dmalloc.h>
 #endif
 #endif
@@ -27,6 +28,8 @@
 
 
 #define parse_hname(_b,_e,_h) parse_hname2((_b),(_e),(_h))
 #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 ;
 /* returns pointer to next header line, and fill hdr_f ;
  * if at end of header returns pointer to the last crlf  (always buf)*/
  * 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){
 	switch(hdr->type){
 		case HDR_VIA:
 		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));
 			vb=pkg_malloc(sizeof(struct via_body));
 			if (vb==0){
 			if (vb==0){
 				LOG(L_ERR, "get_hdr_field: out of memory\n");
 				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(" version: <%s>\n",fl->u.reply.version.s);
 			DBG(" status:  <%s>\n",fl->u.reply.status.s);
 			DBG(" status:  <%s>\n",fl->u.reply.status.s);
 			DBG(" reason:  <%s>\n",fl->u.reply.reason.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;
 			break;
 		default:
 		default:
 			DBG("unknown type %d\n",fl->type);
 			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
 /* make sure all HFs needed for transaction identification have been
    parsed; return 0 if those HFs can't be found
    parsed; return 0 if those HFs can't be found
 */
 */
+
 int check_transaction_quadruple( struct sip_msg* msg )
 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 "../data_lump.h"
 #include "../flags.h"
 #include "../flags.h"
 #include "../ip_addr.h"
 #include "../ip_addr.h"
+#include "../md5utils.h"
 #include "parse_def.h"
 #include "parse_def.h"
 #include "parse_cseq.h"
 #include "parse_cseq.h"
 #include "parse_to.h"
 #include "parse_to.h"
@@ -96,12 +97,13 @@ struct sip_msg {
 	
 	
 	str new_uri; /* changed first line uri*/
 	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];
 	char add_to_branch_s[MAX_BRANCH_PARAM_LEN];
 	int add_to_branch_len;
 	int add_to_branch_len;
 	
 	
@@ -115,6 +117,13 @@ struct sip_msg {
 	flag_t flags;	
 	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);
 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
    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
 #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:
 					case PARA_VALUE_QUOTED:
 						break;
 						break;
-#ifdef PINGTEL_TAG_HACK
+#ifndef NO_PINGTEL_TAG_HACK
 					case TAG3:
 					case TAG3:
 						param->type = TAG_PARAM;
 						param->type = TAG_PARAM;
 						param->name.len = 3;
 						param->name.len = 3;

+ 1 - 0
proxy.c

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

+ 29 - 19
receive.c

@@ -16,6 +16,8 @@
 #include "mem/mem.h"
 #include "mem/mem.h"
 #include "stats.h"
 #include "stats.h"
 #include "ip_addr.h"
 #include "ip_addr.h"
+#include "script_cb.h"
+#include "dset.h"
 
 
 
 
 #ifdef DEBUG_DMALLOC
 #ifdef DEBUG_DMALLOC
@@ -35,8 +37,13 @@ int receive_msg(char* buf, unsigned int len, union sockaddr_union* src_su)
 #endif
 #endif
 
 
 	msg=pkg_malloc(sizeof(struct sip_msg));
 	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++;
 	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 */
 	memset(msg,0, sizeof(struct sip_msg)); /* init everything to 0 */
 	/* fill in msg */
 	/* 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);
 	msg->orig=(char*) pkg_malloc(len+1);
 	if (msg->orig==0){
 	if (msg->orig==0){
 		LOG(L_ERR, "ERROR:receive_msg: memory allocation failure\n");
 		LOG(L_ERR, "ERROR:receive_msg: memory allocation failure\n");
-		goto error1;
+		goto error01;
 	}
 	}
 	memcpy(msg->orig, buf, len);
 	memcpy(msg->orig, buf, len);
 	msg->orig[len]=0; /* null terminate it,good for using str* functions
 	msg->orig[len]=0; /* null terminate it,good for using str* functions
 						 on it*/
 						 on it*/
 	
 	
 	if (parse_msg(buf,len, msg)!=0){
 	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");
 	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){
 	if (msg->first_line.type==SIP_REQUEST){
 		/* sanity checks */
 		/* sanity checks */
 		if ((msg->via1==0) || (msg->via1->error!=PARSE_OK)){
 		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 */
 		/* 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 */
 		/* exec routing script */
 		DBG("preparing to run routing scripts...\n");
 		DBG("preparing to run routing scripts...\n");
 #ifdef  STATS
 #ifdef  STATS
 		gettimeofday( & tvb, &tz );
 		gettimeofday( & tvb, &tz );
 #endif
 #endif
+
 		if (run_actions(rlist[0], msg)<0){
 		if (run_actions(rlist[0], msg)<0){
+
 			LOG(L_WARN, "WARNING: receive_msg: "
 			LOG(L_WARN, "WARNING: receive_msg: "
 					"error while trying script\n");
 					"error while trying script\n");
 			goto error;
 			goto error;
 		}
 		}
+
+
 #ifdef STATS
 #ifdef STATS
 		gettimeofday( & tve, &tz );
 		gettimeofday( & tve, &tz );
 		diff = (tve.tv_sec-tvb.tv_sec)*1000000+(tve.tv_usec-tvb.tv_usec);
 		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");
 			LOG(L_ERR, "ERROR: receive_msg: no 2nd via found in reply\n");
 			goto error;
 			goto error;
 		}
 		}
-#endif
 		/* check if via1 == us */
 		/* check if via1 == us */
+#endif
 
 
 #ifdef STATS
 #ifdef STATS
 		gettimeofday( & tvb, &tz );
 		gettimeofday( & tvb, &tz );
@@ -130,10 +142,8 @@ int receive_msg(char* buf, unsigned int len, union sockaddr_union* src_su)
 #ifdef STATS
 #ifdef STATS
 	skipped = 0;
 	skipped = 0;
 #endif
 #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");
 	DBG("receive_msg: cleaning up\n");
 	free_sip_msg(msg);
 	free_sip_msg(msg);
 	pkg_free(msg);
 	pkg_free(msg);
@@ -143,13 +153,13 @@ skip:
 	return 0;
 	return 0;
 error:
 error:
 	DBG("error:...\n");
 	DBG("error:...\n");
+	/* execute post-script callbacks, if any; -jiri */
+	exec_post_cb(msg);
+error02:
 	free_sip_msg(msg);
 	free_sip_msg(msg);
+error01:
 	pkg_free(msg);
 	pkg_free(msg);
-	STATS_RX_DROPS;
-	return -1;
-error1:
-	if (msg) pkg_free(msg);
-	pkg_free(buf);
+error00:
 	STATS_RX_DROPS;
 	STATS_RX_DROPS;
 	return -1;
 	return -1;
 }
 }

+ 20 - 0
route.c

@@ -29,6 +29,8 @@
 
 
 /* main routing script table  */
 /* main routing script table  */
 struct action* rlist[RT_NO];
 struct action* rlist[RT_NO];
+/* reply routing table */
+struct action* reply_rlist[REPLY_RT_NO];
 
 
 
 
 static int fix_actions(struct action* a); /*fwd declaration*/
 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;
 	return 0;
 }
 }
 
 
@@ -447,6 +456,17 @@ void print_rl()
 		}
 		}
 		DBG("\n");
 		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" */
 /* main "script table" */
 extern struct action* rlist[RT_NO];
 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);
 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:
 			case EXEC_T:
 					DBG("exec(");
 					DBG("exec(");
 					break;
 					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:
 			case SET_HOST_T:
 					DBG("sethost(");
 					DBG("sethost(");
 					break;
 					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_HOST_T, SET_HOSTPORT_T, SET_USER_T, SET_USERPASS_T, 
 		SET_PORT_T, SET_URI_T, IF_T, MODULE_T,
 		SET_PORT_T, SET_URI_T, IF_T, MODULE_T,
 		SETFLAG_T, RESETFLAG_T, ISFLAGSET_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,
 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 };
 		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
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version.
  * (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,
  * ser is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * 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();
+		};
+	};
+}
+

Kaikkia tiedostoja ei voida näyttää, sillä liian monta tiedostoa muuttui tässä diffissä