bignag change -- lot of things primarily added in relationship with
authorJiri Kuthan <jiri@iptel.org>
Thu, 15 Aug 2002 08:13:29 +0000 (08:13 +0000)
committerJiri Kuthan <jiri@iptel.org>
Thu, 15 Aug 2002 08:13:29 +0000 (08:13 +0000)
refurbushing TM; see [sr] archive (2002-08-14) -- "ser update" and
"TM update" for a long list of details

103 files changed:
COPYING
Makefile
Makefile.defs
README-MODULES
TODO
action.c
cfg.lex
cfg.y
config.h
dset.c [new file with mode: 0644]
dset.h [new file with mode: 0644]
error.c
error.h
etc/iptel.cfg
fifo_server.c [new file with mode: 0644]
fifo_server.h [new file with mode: 0644]
flags.c
forward.c
forward.h
globals.h
main.c
md5utils.c
mem/mem.c
mem/memtest.c
mem/shm_mem.c
mem/vq_malloc.c
mem/vq_malloc.h
modules/tm/README
modules/tm/TODO [deleted file]
modules/tm/config.h
modules/tm/fix_lumps.h [new file with mode: 0644]
modules/tm/h_table.c
modules/tm/h_table.h
modules/tm/hash_func.c [deleted file]
modules/tm/hash_func.h [deleted file]
modules/tm/lock.c
modules/tm/lock.h
modules/tm/sh_malloc.h [deleted file]
modules/tm/sip_msg.c
modules/tm/sip_msg.h
modules/tm/t_cancel.c [new file with mode: 0644]
modules/tm/t_cancel.h [new file with mode: 0644]
modules/tm/t_dlg.c [new file with mode: 0644]
modules/tm/t_dlg.h [new file with mode: 0644]
modules/tm/t_fork.c [deleted file]
modules/tm/t_fork.h [deleted file]
modules/tm/t_funcs.c
modules/tm/t_funcs.h
modules/tm/t_fwd.c
modules/tm/t_fwd.h [new file with mode: 0644]
modules/tm/t_hooks.c
modules/tm/t_hooks.h
modules/tm/t_lookup.c
modules/tm/t_lookup.h [new file with mode: 0644]
modules/tm/t_msgbuilder.c
modules/tm/t_msgbuilder.h [new file with mode: 0644]
modules/tm/t_reply.c
modules/tm/t_reply.h [new file with mode: 0644]
modules/tm/t_thandlers.c
modules/tm/test.c
modules/tm/timer.c
modules/tm/timer.h
modules/tm/tm.c [deleted file]
modules/tm/tm_load.c
modules/tm/tm_load.h
modules/tm/tm_mod.c [new file with mode: 0644]
modules/tm/uac.c [new file with mode: 0644]
modules/tm/uac.h [new file with mode: 0644]
msg_translator.c
msg_translator.h
parser/hf.c
parser/hf.h
parser/msg_parser.c
parser/msg_parser.h
parser/parse_to.c
proxy.c
receive.c
route.c
route.h
route_struct.c
route_struct.h
script_cb.c [new file with mode: 0644]
script_cb.h [new file with mode: 0644]
test/bad_uri.sip [new file with mode: 0644]
test/file_copyright.txt
test/invite00.sip [new file with mode: 0644]
test/invite01.sip [new file with mode: 0644]
test/invite03.sip [new file with mode: 0644]
test/invite04.sip [new file with mode: 0644]
test/invite05.sip [new file with mode: 0644]
test/ms-invite-00-rpl.sip [new file with mode: 0644]
test/ms-invite-00.sip [new file with mode: 0644]
test/ms-invite-01-rpl.sip [new file with mode: 0644]
test/no_eom_reply.sip [new file with mode: 0644]
test/onr.cfg [new file with mode: 0644]
test/register03.sip [new file with mode: 0644]
test/short_nonce.sip [new file with mode: 0644]
test/short_reply.sip [new file with mode: 0644]
test/stress.cfg [new file with mode: 0644]
test/struas.cfg [new file with mode: 0644]
test/transaction.fifo [new file with mode: 0644]
test/uas.cfg [new file with mode: 0644]
ut.h

diff --git a/COPYING b/COPYING
index d60c31a..469656f 100644 (file)
--- a/COPYING
+++ b/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:
+
+    info@iptel.org
+
+   (see http://www.gnu.org/copyleft/gpl-faq.html#TOCHeardOtherLicense
+    for an explanation how parallel licenses comply with GPL)
+
+2) ser software allows programmers to plug-in external modules to the
+   core part. Note that GPL mandates all plug-ins developed for the
+   ser software released under GPL license to be GPL-ed as well.
+
+   (see http://www.gnu.org/copyleft/gpl-faq.html#GPLAndPlugins
+    for a detailed explanation)
+
+3) Note that the GPL bellow is copyrighted by the Free Software Foundation,
+   but the ser software is copyrighted by iptel.org.
+
+
+-------------------------------------------------------------------------
+
                    GNU GENERAL PUBLIC LICENSE
                       Version 2, June 1991
 
index c19ba3d..9890239 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -10,7 +10,7 @@ auto_gen=lex.yy.c cfg.tab.c   #lexx, yacc etc
 #include  source related defs
 include Makefile.sources
 
-exclude_modules=CVS mysql pike
+exclude_modules=CVS mysql pike 
 static_modules=
 static_modules_path=$(addprefix modules/, $(static_modules))
 extra_sources=$(wildcard $(addsuffix /*.c, $(static_modules_path)))
index 2b2b248..1be6db0 100644 (file)
@@ -76,9 +76,6 @@ INSTALL-MAN = $(INSTALL) -m 644
 #              issues additional debugging information if lock/unlock is called
 # -DFAST_LOCK
 #              uses fast arhitecture specific locking (see the arh. specific section)
-# -DNOISY_REPLIES
-#              turns on appending User-agent and Content-length:0 to ser-generated
-#              replies; 
 # -DBUSY_WAIT
 #              uses busy waiting on the lock
 # -DADAPTIVE_WAIT
@@ -90,37 +87,29 @@ INSTALL-MAN = $(INSTALL) -m 644
 # -DNOSMP
 #              don't use smp compliant locking (faster but won't work on SMP machines)
 #              (not yet enabled)
-# -DWAIT
-#              protection against race condiditions; turn off only for debugging;
-#       to become non-optional if stable
-#
-# -SILENT_FR
-#              if defined, when FR timer hits (in tm) cancel is sent only if forking
-#              if used; otherwise, just delete the transaction without doing anything
+# -DNO_PINGTEL_TAG_HACK
+#              if enabled, To-header-field will be less liberal and will not accept
+#              'tag=' (tag parameter with equal sign and without value); it is called
+#              this way because such message was sighted from a Pingtel phone
 
 DEFS+= -DNAME='"$(NAME)"' -DVERSION='"$(RELEASE)"' -DARCH='"$(ARCH)"' \
         -DOS='"$(OS)"' -DCOMPILER='"$(CC_VER)"'\
         -DPKG_MALLOC \
         -DSHM_MEM  -DSHM_MMAP \
         -DADAPTIVE_WAIT -DADAPTIVE_WAIT_LOOPS=1024 \
-        -DWAIT \
-        -DSILENT_FR \
-        -DNOISY_REPLIES -DVERY_NOISY_REPLIES\
-        -DPINGTEL_TAG_HACK\
-        -DF_MALLOC \
-        -DUSE_SYNONIM\
         -DUSE_IPV6 \
+        -DEXTRA_DEBUG \
+        -DVQ_MALLOC  -DDBG_QM_MALLOC \
+        #-DCONTACT_BUG
+        #-DF_MALLOC \
+        #-DDBG_LOCK
         #-DNO_DEBUG \
         #-DADAPTIVE_WAIT -DADAPTIVE_WAIT_LOOPS=0 \
         #-DNOSMP \
         #-DEXTRA_DEBUG 
-        #-DVQ_MALLOC  -DDBG_LOCK  #-DSTATS
-        #-DDBG_QM_MALLOC 
-# -DUSE_SHM_MEM
-#-DSTATS 
-#-DNO_LOG
-
-
+        #-DUSE_SHM_MEM
+        #-DSTATS 
+        #-DNO_LOG
 
 #PROFILE=  -pg #set this if you want profiling
 #mode = debug
index 8bc552b..591c6bd 100644 (file)
@@ -3,33 +3,59 @@
 List of currently available ser modules
 ----------------------------------------------------------
 Name   Use                      Maturity       Purpose/Depends on
+(owner)
 ----------------------------------------------------------
 acc            regular          stable         transaction accounting
-               /example                                (the module servers also
+(jku)  /example                                (the module servers also
                                                            as example of how to bind
                                                                to transaction management)
                                                                -tm
+
 auth   regular          stable         digest authentication
-                                                               -sl
+(jja)                                                  -sl
                                                                -mysql
+
 cpl            experimental alpha              Call Processing Language
+(bia)
+
 ext            experimental alpha              Execution of external URI
-                                                               processing logic
+(bia)                                                  processing logic
+
 im             temporary        alpha          Stateless instant messaging     
-                                                               client
+(bia)                                                  client
+
 jabber experimental beta               SIP2Jabber gateway
+(dcm)
+
 maxfwd regular          stable         Max-Forwards check
+(bia)
+
 mysql  regular          stable         supporting MySql interface
+(jja)
+
 pike   experimental alpha              excessive load detection
+(bia)
+
 print  example          stable         Printing Message to stdout
+(ape)
+
 rr             regular          stable         Record-Routing
+(jja)
+
 sl             regular          stable         Stateless Replies
+(bia)
+
 sms            regular          stable         SMS gateway
-                                                               -tm
+(bia)                                                          -tm
+
 textops regular                 stable         Message Textual Operations
+(ape)
+
 tm             regular          beta           Transaction Management
+(ape)
+
 usrloc regular          stable         User Location
-                                                               -sl
+(jja)                                                          -sl
                                                                -mysql (optionally)
 
 
@@ -44,3 +70,8 @@ ser programmers.
 Maturity is label as stable if a module has been deployed
 for longer time, alpha if it is still being developed and
 beta if it is under test.
+
+Modules underway include presence server functionality,
+firewall control, message store and more. If you are
+interested in any of these or other modules, write us
+to info@iptel.org.
diff --git a/TODO b/TODO
index 77123b7..77700dd 100644 (file)
--- a/TODO
+++ b/TODO
@@ -16,16 +16,16 @@ x (different way) add request header bitmap field for the modules
 
 
 High priority:
-- fix/replace T_REF/T_UNREF
-- review all the tm locking
+x fix/replace T_REF/T_UNREF
+x review all the tm locking
 x if () {} else {}
 x plugin interface
 x ipv6 support
-- reply ("response line")
-- drop ACKs for our replies
+x reply ("response line")
+x drop ACKs for our replies
 - icmp error handling
-- add To-tag (for the replies)
-- add User-Agent (for the replies)
+x add To-tag (for the replies)
+x add User-Agent (for the replies)
 
 Low priority:
 x fix via address someday
@@ -38,7 +38,7 @@ x forward to received= if present
 - exec improvments (add format strings to it)
 - command line switch for checking the config file syntax
 - config file version (a la sendmail)
-- loop detection
+0 loop detection
 - cfg. file reload
 - flags for using names or ip adresses in Via ?
 
@@ -55,6 +55,6 @@ x the same for rpm
 - the same for FreeBSD and Slackware
 
 
-- jku: branch hash computation over canonical values
-- jku: loop checking
+x jku: branch hash computation over canonical values
+0 jku: loop checking
 - jku: try CRC as opposed to MD5
index 7a5644e..368e478 100644 (file)
--- a/action.c
+++ b/action.c
@@ -17,6 +17,7 @@
 #include "sr_module.h"
 #include "mem/mem.h"
 #include "globals.h"
+#include "dset.h"
 
 #include <sys/types.h>
 #include <sys/socket.h>
@@ -179,6 +180,18 @@ int do_action(struct action* a, struct sip_msg* msg)
                        ret=1;
                        break;
 
+               /* jku -- introduce a new branch */
+               case APPEND_BRANCH_T:
+                       if ((a->p1_type!=STRING_ST)) {
+                               LOG(L_CRIT, "BUG: do_action: bad append_branch_t %d\n",
+                                       a->p1_type );
+                               ret=E_BUG;
+                               break;
+                       }
+                       ret=append_branch( msg, a->p1.string, 
+                               a->p1.string ? strlen(a->p1.string):0 );
+                       break;
+
                /* jku begin: is_length_greater_than */
                case LEN_GT_T:
                        if (a->p1_type!=NUMBER_ST) {
@@ -281,6 +294,14 @@ int do_action(struct action* a, struct sip_msg* msg)
                        }
                        ret=1;
                        break;
+               case REVERT_URI_T:
+                       if (msg->new_uri.s) {
+                               pkg_free(msg->new_uri.s);
+                               msg->new_uri.len=0;
+                               msg->new_uri.s=0;
+                       };
+                       ret=1;
+                       break;
                case SET_HOST_T:
                case SET_HOSTPORT_T:
                case SET_USER_T:
diff --git a/cfg.lex b/cfg.lex
index 3172ebf..147817c 100644 (file)
--- a/cfg.lex
+++ b/cfg.lex
@@ -46,6 +46,7 @@ SEND  send
 LOG            log
 ERROR  error
 ROUTE  route
+REPLY_ROUTE reply_route
 EXEC   exec
 SETFLAG                setflag
 RESETFLAG      resetflag
@@ -57,8 +58,10 @@ SET_USER             "rewriteuser"|"setuser"|"setu"
 SET_USERPASS   "rewriteuserpass"|"setuserpass"|"setup"
 SET_PORT               "rewriteport"|"setport"|"setp"
 SET_URI                        "rewriteuri"|"seturi"
+REVERT_URI             "revert_uri"
 PREFIX                 "prefix"
 STRIP                  "strip"
+APPEND_BRANCH  "append_branch"
 IF                             "if"
 ELSE                   "else"
 
@@ -94,7 +97,12 @@ STAT statistics
 MAXBUFFER maxbuffer
 CHILDREN children
 CHECK_VIA      check_via
-LOOP_CHECKS    loop_checks
+SYN_BRANCH syn_branch
+SIP_WARNING sip_warning
+FIFO fifo
+FIFO_MODE fifo_mode
+SERVER_SIGNATURE server_signature
+REPLY_TO_VIA reply_to_via
 
 LOADMODULE     loadmodule
 MODPARAM        modparam
@@ -148,6 +156,7 @@ EAT_ABLE    [\ \t\b\r]
 <INITIAL>{ISFLAGSET}   { count(); yylval.strval=yytext; return ISFLAGSET; }
 <INITIAL>{LEN_GT}      { count(); yylval.strval=yytext; return LEN_GT; }
 <INITIAL>{ROUTE}       { count(); yylval.strval=yytext; return ROUTE; }
+<INITIAL>{REPLY_ROUTE} { count(); yylval.strval=yytext; return REPLY_ROUTE; }
 <INITIAL>{EXEC}        { count(); yylval.strval=yytext; return EXEC; }
 <INITIAL>{SET_HOST}    { count(); yylval.strval=yytext; return SET_HOST; }
 <INITIAL>{SET_HOSTPORT}        { count(); yylval.strval=yytext; return SET_HOSTPORT; }
@@ -155,8 +164,10 @@ EAT_ABLE   [\ \t\b\r]
 <INITIAL>{SET_USERPASS}        { count(); yylval.strval=yytext; return SET_USERPASS; }
 <INITIAL>{SET_PORT}    { count(); yylval.strval=yytext; return SET_PORT; }
 <INITIAL>{SET_URI}     { count(); yylval.strval=yytext; return SET_URI; }
+<INITIAL>{REVERT_URI}  { count(); yylval.strval=yytext; return REVERT_URI; }
 <INITIAL>{PREFIX}      { count(); yylval.strval=yytext; return PREFIX; }
 <INITIAL>{STRIP}       { count(); yylval.strval=yytext; return STRIP; }
+<INITIAL>{APPEND_BRANCH}       { count(); yylval.strval=yytext; return APPEND_BRANCH; }
 <INITIAL>{IF}  { count(); yylval.strval=yytext; return IF; }
 <INITIAL>{ELSE}        { count(); yylval.strval=yytext; return ELSE; }
 
@@ -181,7 +192,12 @@ EAT_ABLE   [\ \t\b\r]
 <INITIAL>{MAXBUFFER}   { count(); yylval.strval=yytext; return MAXBUFFER; }
 <INITIAL>{CHILDREN}    { count(); yylval.strval=yytext; return CHILDREN; }
 <INITIAL>{CHECK_VIA}   { count(); yylval.strval=yytext; return CHECK_VIA; }
-<INITIAL>{LOOP_CHECKS} { count(); yylval.strval=yytext; return LOOP_CHECKS; }
+<INITIAL>{SYN_BRANCH}  { count(); yylval.strval=yytext; return SYN_BRANCH; }
+<INITIAL>{SIP_WARNING} { count(); yylval.strval=yytext; return SIP_WARNING; }
+<INITIAL>{FIFO}        { count(); yylval.strval=yytext; return FIFO; }
+<INITIAL>{FIFO_MODE}   { count(); yylval.strval=yytext; return FIFO_MODE; }
+<INITIAL>{SERVER_SIGNATURE}    { count(); yylval.strval=yytext; return SERVER_SIGNATURE; }
+<INITIAL>{REPLY_TO_VIA}        { count(); yylval.strval=yytext; return REPLY_TO_VIA; }
 <INITIAL>{LOADMODULE}  { count(); yylval.strval=yytext; return LOADMODULE; }
 <INITIAL>{MODPARAM}     { count(); yylval.strval=yytext; return MODPARAM; }
 
diff --git a/cfg.y b/cfg.y
index 5b3f7e8..0061b25 100644 (file)
--- a/cfg.y
+++ b/cfg.y
@@ -56,15 +56,18 @@ void* f_tmp;
 %token LOG_TOK
 %token ERROR
 %token ROUTE
+%token REPLY_ROUTE
 %token EXEC
 %token SET_HOST
 %token SET_HOSTPORT
 %token PREFIX
 %token STRIP
+%token APPEND_BRANCH
 %token SET_USER
 %token SET_USERPASS
 %token SET_PORT
 %token SET_URI
+%token REVERT_URI
 %token IF
 %token ELSE
 %token URIHOST
@@ -90,7 +93,12 @@ void* f_tmp;
 %token STAT
 %token CHILDREN
 %token CHECK_VIA
-%token LOOP_CHECKS
+%token SYN_BRANCH
+%token SIP_WARNING
+%token FIFO
+%token FIFO_MODE
+%token SERVER_SIGNATURE
+%token REPLY_TO_VIA
 %token LOADMODULE
 %token MODPARAM
 %token MAXBUFFER
@@ -151,6 +159,7 @@ statements: statements statement {}
 statement:     assign_stm 
                | module_stm
                | route_stm 
+               | reply_route_stm
                | CR    /* null statement*/
        ;
 
@@ -180,8 +189,18 @@ assign_stm:        DEBUG EQUAL NUMBER { debug=$3; }
                | CHILDREN EQUAL error { yyerror("number expected"); } 
                | CHECK_VIA EQUAL NUMBER { check_via=$3; }
                | CHECK_VIA EQUAL error { yyerror("boolean value expected"); }
-               | LOOP_CHECKS EQUAL NUMBER { loop_checks=$3; }
-               | LOOP_CHECKS EQUAL error { yyerror("boolean value expected"); }
+               | SYN_BRANCH EQUAL NUMBER { syn_branch=$3; }
+               | SYN_BRANCH EQUAL error { yyerror("boolean value expected"); }
+               | SIP_WARNING EQUAL NUMBER { sip_warning=$3; }
+               | SIP_WARNING EQUAL error { yyerror("boolean value expected"); }
+               | FIFO EQUAL STRING { fifo=$3; }
+               | FIFO EQUAL error { yyerror("string value expected"); }
+               | FIFO_MODE EQUAL NUMBER { fifo_mode=$3; }
+               | FIFO_MODE EQUAL NUMBER { yyerror("int value expected"); }
+               | SERVER_SIGNATURE EQUAL NUMBER { server_signature=$3; }
+               | SERVER_SIGNATURE EQUAL error { yyerror("boolean value expected"); }
+               | REPLY_TO_VIA EQUAL NUMBER { reply_to_via=$3; }
+               | REPLY_TO_VIA EQUAL error { yyerror("boolean value expected"); }
                | LISTEN EQUAL ip  {
                                                                if (sock_no< MAX_LISTEN){
                                                                        tmp=ip_addr2a($3);
@@ -347,6 +366,17 @@ route_stm: ROUTE LBRACE actions RBRACE { push($3, &rlist[DEFAULT_RT]); }
                                                                                }
                | ROUTE error { yyerror("invalid  route  statement"); }
        ;
+
+reply_route_stm: REPLY_ROUTE LBRACK NUMBER RBRACK LBRACE actions RBRACE {
+                                                                               if (($3<REPLY_RT_NO)&&($3>=1)){
+                                                                                       push($6, &reply_rlist[$3]);
+                                                                               } else {
+                                                                                       yyerror("invalid reply routing"
+                                                                                               "table number");
+                                                                                       YYABORT; }
+                                                                               }
+               | REPLY_ROUTE error { yyerror("invalid reply_route statement"); }
+       ;
 /*
 rules: rules rule { push($2, &$1); $$=$1; }
        | rule {$$=$1; }
@@ -685,6 +715,12 @@ cmd:               FORWARD LPAREN host RPAREN      { $$=mk_action( FORWARD_T,
                | STRIP LPAREN error RPAREN { $$=0; yyerror("bad argument, "
                                                                                                                "number expected"); }
 
+               | APPEND_BRANCH LPAREN STRING RPAREN { $$=mk_action( APPEND_BRANCH_T,
+                                                                                                       STRING_ST, 0, $3, 0) ; }
+               | APPEND_BRANCH LPAREN RPAREN { $$=mk_action( APPEND_BRANCH_T,
+                                                                                                       STRING_ST, 0, 0, 0 ) ; }
+               | APPEND_BRANCH {  $$=mk_action( APPEND_BRANCH_T, STRING_ST, 0, 0, 0 ) ; }
+
                | SET_HOSTPORT LPAREN STRING RPAREN { $$=mk_action( SET_HOSTPORT_T, 
                                                                                                                STRING_ST, 0, $3, 0); }
                | SET_HOSTPORT error { $$=0; yyerror("missing '(' or ')' ?"); }
@@ -710,6 +746,8 @@ cmd:                FORWARD LPAREN host RPAREN      { $$=mk_action( FORWARD_T,
                | SET_URI error { $$=0; yyerror("missing '(' or ')' ?"); }
                | SET_URI LPAREN error RPAREN { $$=0; yyerror("bad argument, "
                                                                                "string expected"); }
+               | REVERT_URI LPAREN RPAREN { $$=mk_action( REVERT_URI_T, 0,0,0,0); }
+               | REVERT_URI { $$=mk_action( REVERT_URI_T, 0,0,0,0); }
                | ID LPAREN RPAREN                      { f_tmp=(void*)find_export($1, 0);
                                                                           if (f_tmp==0){
                                                                                yyerror("unknown command, missing"
index 84e6d42..80c9b2e 100644 (file)
--- a/config.h
+++ b/config.h
@@ -22,6 +22,7 @@
 #define CHILD_NO    8
 
 #define RT_NO 10 /* routing tables number */
+#define REPLY_RT_NO 10 /* reply routing tables number */
 #define DEFAULT_RT 0 /* default routing table */
 
 #define MAX_REC_LEV 100 /* maximum number of recursive calls */
@@ -46,8 +47,8 @@
 
 #define MAX_WARNING_LEN  256
                
-#define MY_BRANCH ";branch=0"
-#define MY_BRANCH_LEN 9
+#define MY_BRANCH ";branch="
+#define MY_BRANCH_LEN 8
 
 
 #define MAX_PORT_LEN 7 /* ':' + max 5 letters + \0 */
 #define MAX_VIA_LINE_SIZE      240
 #define MAX_RECEIVED_SIZE      57
 
-/* maximum number of processes is constrained by capacity of
-   process bitmaps */
-#define MAX_PROCESSES (sizeof( process_bm_t) * 8 )
+/* maximum number of branches per transaction */
+#define MAX_BRANCHES    4
 
+/* maximum length of a FIFO server command */
+#define MAX_FIFO_COMMAND 512
+
+/* buffer dimensions for FIFO server */
+#define MAX_CONSUME_BUFFER 1024
+/* where reply pipes may be opened */
+#define FIFO_DIR "/tmp/"
+/* max length of the text of fifo 'print' command */
+#define MAX_PRINT_TEXT 256
 #endif
diff --git a/dset.c b/dset.c
new file mode 100644 (file)
index 0000000..3881062
--- /dev/null
+++ b/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;
+}
diff --git a/dset.h b/dset.h
new file mode 100644 (file)
index 0000000..1ae9d8f
--- /dev/null
+++ b/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
diff --git a/error.c b/error.c
index a71c370..8d1328d 100644 (file)
--- a/error.c
+++ b/error.c
@@ -5,6 +5,9 @@
 
 #include <stdio.h>
 #include "error.h"
+#include "str.h"
+#include "parser/msg_parser.h"
+#include "mem/mem.h"
 
 /* current function's error; */
 int ser_error=-1;
@@ -43,6 +46,10 @@ int err2reason_phrase(
                        error_txt="Regretfuly, we were not able to process the URI";
                        *sip_error=-ser_error;
                        break;
+               case E_BAD_TUPEL:
+                       error_txt="Transaction tupel incomplete";
+                       *sip_error=-E_BAD_REQ;
+                       break;
                default:
                        error_txt="I'm terribly sorry, server error occured";
                        *sip_error=500;
@@ -51,3 +58,104 @@ int err2reason_phrase(
        return snprintf( phrase, etl, "%s (%d/%s)", error_txt, 
                -ser_error, signature );
 }
+
+char *error_text( int code )
+{
+       switch(code) {
+
+               case 100: return "Trying";
+               case 180: return "Ringing";
+               case 181: return "Call is Being Forwarded";
+               case 182: return "Queued";
+               case 183: return "Session Progress";
+
+               case 200: return "OK";
+
+               case 300: return "Multiple Choices";
+               case 301: return "Moved Permanently";
+               case 302: return "Moved Temporarily";
+               case 305: return "Use Proxy";
+               case 380: return "Alternative Service";
+
+               case 400: return "Bad Request";
+               case 401: return "Unauthorized";
+               case 402: return "Payement Required";
+               case 403: return "Forbidden";
+               case 404: return "Not Found";
+               case 405: return "Method not Allowed";
+               case 406: return "Not Acceptable";
+               case 407: return "Proxy authentication Required";
+               case 408: return "Request Timeout";
+               case 410: return "Gone";
+               case 413: return "Request Entity Too Large";
+               case 414: return "Request-URI Too Long";
+               case 415: return "Unsupported Media Type";
+               case 416: return "Unsupported URI Scheme";
+               case 417: return "Bad Extension";
+               case 421: return "Extension Required";
+               case 423: return "Interval Too Brief";
+               case 480: return "Temporarily Unavailable";
+               case 481: return "Call/Transaction Does not Exist";
+               case 482: return "Loop Detected";
+               case 483: return "Too Many Hops";
+               case 484: return "Address Incomplete";
+               case 485: return "Ambigous";
+               case 486: return "Busy Here";
+               case 487: return "Request Terminated";
+               case 488: return "Not Acceptable Here";
+               case 491: return "Request Pending";
+       
+               case 500: return "Server Internal Error";
+               case 501: return "Not Implemented";
+               case 502: return "Bad Gateway";
+               case 503: return "Service Unavailable";
+               case 504: return "Server Time-out";
+               case 505: return "Version not Supported";
+               case 513: return "Message Too Large";
+
+               case 600: return "Busy Everywhere";
+               case 603: return "Decline";
+               case 604: return "Does not Exist Anywhere";
+               case 606: return "Not Acceptable";
+
+       }
+
+       if (code>=600) return "Global Failure";
+       else if (code>=500) return "Server Failure";
+       else if (code>=400) return "Request Failure";
+       else if (code>=300) return "Redirection";
+       else if (code>=200) return "Successful";
+       else if (code>=100) return "Provisional";
+       else return "Unspecified";
+}
+
+void get_reply_status( str *status, struct sip_msg *reply, int code )
+{
+       str phrase;
+
+       status->s=0;
+
+       if (reply==0) {
+               LOG(L_CRIT, "BUG: get_reply_status called with 0 msg\n");
+               return;
+       }
+
+       if (reply==FAKED_REPLY) {
+               phrase.s=error_text(code);
+               phrase.len=strlen(phrase.s);
+       } else {
+               phrase=reply->first_line.u.reply.reason;
+       }
+       status->len=phrase.len+3/*code*/+1/*space*/+1/*ZT*/;
+       status->s=pkg_malloc(status->len);
+       if (!status->s) {
+               LOG(L_ERR, "ERROR: get_reply_status: no mem\n");
+               return;
+       }
+       status->s[3]=' ';
+       status->s[2]='0'+code % 10; code=code/10;
+       status->s[1]='0'+code% 10; code=code/10;
+       status->s[0]='0'+code % 10;
+       memcpy(&status->s[4], phrase.s, phrase.len);
+       status->s[status->len-1]=0;
+}
diff --git a/error.h b/error.h
index d71fd55..3544606 100644 (file)
--- a/error.h
+++ b/error.h
 #define E_BUG         -5
 #define E_CFG         -6
 #define E_NO_SOCKET            -7
+/* unresolveable topmost Via */
+#define E_BAD_VIA              -8
+/* incomplete transaction tupel */
+#define E_BAD_TUPEL            -9
+/* script programming error */
+#define E_SCRIPT               -10
 
 #define E_SEND           -477
 /* unresolveable next-hop address */
 
 #define MAX_REASON_LEN 128
 
+#include "str.h"
+
 /* processing status of the last command */
 extern int ser_error;
 extern int prev_ser_error;
 
+struct sip_msg;
+
+/* ser error -> SIP error */
 int err2reason_phrase( int ser_error, int *sip_error, 
                 char *phrase, int etl, char *signature );
 
+/* SIP error core -> SIP text */
+char *error_text( int code );
+
+/* return pkg_malloc-ed reply status in status->s */
+void get_reply_status( str *status, struct sip_msg *reply, int code );
 
 #endif
index 8302299..b23524e 100644 (file)
@@ -614,5 +614,6 @@ route[3] {
                sl_reply_error(); 
                break; 
        };
+
 }
 
diff --git a/fifo_server.c b/fifo_server.c
new file mode 100644 (file)
index 0000000..3454395
--- /dev/null
@@ -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;
+}
diff --git a/fifo_server.h b/fifo_server.h
new file mode 100644 (file)
index 0000000..d304aa3
--- /dev/null
@@ -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
diff --git a/flags.c b/flags.c
index 96bbc12..511b056 100644 (file)
--- a/flags.c
+++ b/flags.c
@@ -16,12 +16,12 @@ int setflag( struct sip_msg* msg, flag_t flag ) {
 }
 
 int resetflag( struct sip_msg* msg, flag_t flag ) {
-       msg->flags &= ~ flag;
+       msg->flags &= ~ (1 << flag);
        return 1;
 }
 
 int isflagset( struct sip_msg* msg, flag_t flag ) {
-       return msg->flags & (1<<flag) ? 1 : -1;
+       return (msg->flags & (1<<flag)) ? 1 : -1;
 }
 
 int flag_in_range( flag_t flag ) {
index 6197599..53588bd 100644 (file)
--- a/forward.c
+++ b/forward.c
@@ -13,6 +13,7 @@
 #include <arpa/inet.h>
 
 #include "forward.h"
+#include "hash_func.h"
 #include "config.h"
 #include "parser/msg_parser.h"
 #include "route.h"
@@ -67,6 +68,7 @@ int forward_request( struct sip_msg* msg, struct proxy_l * p)
        char* buf;
        union sockaddr_union* to;
        struct socket_info* send_sock;
+       char md5[MD5_LEN];
        
        to=0;
        buf=0;
@@ -98,13 +100,39 @@ int forward_request( struct sip_msg* msg, struct proxy_l * p)
                LOG(L_ERR, "forward_req: ERROR: cannot forward to af %d "
                                "no coresponding listening socket\n", to->s.sa_family);
                ser_error=E_NO_SOCKET;
-               goto error;
+               goto error1;
        }
-       
+
+       /* calculate branch for outbound request;  if syn_branch is turned off,
+          calculate is from transaction key, i.e., as an md5 of From/To/CallID/
+          CSeq exactly the same way as TM does; good for reboot -- than messages
+          belonging to transaction lost due to reboot will still be forwarded
+          with the same branch parameter and will be match-able downstream
+
+       if it is turned on, we don't care about reboot; we simply put a simple
+          value in there; better for performance
+       */
+
+       if (syn_branch ) {
+               *msg->add_to_branch_s='0';
+               msg->add_to_branch_len=1;
+       } else {
+               if (!char_msg_val( msg, md5 ))  { /* parses transaction key */
+                       LOG(L_ERR, "ERROR: forward_request: char_msg_val failed\n");
+                       goto error1;
+               }
+               msg->hash_index=hash( msg->callid->body, get_cseq(msg)->number);
+               if (!branch_builder( msg->hash_index, 0, md5, 0 /* 0-th branch */,
+                                       msg->add_to_branch_s, &msg->add_to_branch_len )) {
+                       LOG(L_ERR, "ERROR: forward_request: branch_builder failed\n");
+                       goto error1;
+               }
+       }
+
        buf = build_req_buf_from_sip_req( msg, &len, send_sock);
        if (!buf){
-               LOG(L_ERR, "ERROR: forward_reply: building failed\n");
-               goto error;
+               LOG(L_ERR, "ERROR: forward_request: building failed\n");
+               goto error1;
        }
         /* send it! */
        DBG("Sending:\n%s.\n", buf);
@@ -116,7 +144,7 @@ int forward_request( struct sip_msg* msg, struct proxy_l * p)
                        p->errors++;
                        p->ok=0;
                        STATS_TX_DROPS;
-                       goto error;
+                       goto error1;
        }
        /* sent requests stats */
        else STATS_TX_REQUEST(  msg->first_line.u.request.method_value );
@@ -124,13 +152,26 @@ int forward_request( struct sip_msg* msg, struct proxy_l * p)
        free(to);
        /* received_buf & line_buf will be freed in receiv_msg by free_lump_list*/
        return 0;
+
+error1:
+       free(to);
 error:
        if (buf) pkg_free(buf);
-       if (to) free(to);
        return -1;
 }
 
 
+int update_sock_struct_from_ip( union sockaddr_union* to,
+       struct sip_msg *msg )
+{
+       to->sin.sin_port=(msg->via1->port)
+               ?htons(msg->via1->port): htons(SIP_PORT);
+       to->sin.sin_family=msg->src_ip.af;
+       memcpy(&to->sin.sin_addr, &msg->src_ip.u, msg->src_ip.len);
+
+       return 1;
+}
+
 int update_sock_struct_from_via( union sockaddr_union* to,
                                                                 struct via_body* via )
 {
@@ -252,9 +293,10 @@ int forward_reply(struct sip_msg* msg)
                        if (mod->exports->response_f(msg)==0) goto skip;
                }
        }
-       
+
        /* we have to forward the reply stateless, so we need second via -bogdan*/
-       if ((msg->via2==0) || (msg->via2->error!=PARSE_OK))
+       if (parse_headers( msg, HDR_VIA2, 0 )==-1 
+               || (msg->via2==0) || (msg->via2->error!=PARSE_OK))
        {
                /* no second via => error */
                LOG(L_ERR, "ERROR: forward_msg: no 2nd via found in reply\n");
index 6bc3f85..a6ffd8c 100644 (file)
--- a/forward.h
+++ b/forward.h
@@ -16,6 +16,8 @@ struct socket_info* get_send_socket(union sockaddr_union* su);
 int forward_request( struct sip_msg* msg,  struct proxy_l* p);
 int update_sock_struct_from_via( union sockaddr_union* to,
                                                                struct via_body* via );
+int update_sock_struct_from_ip( union sockaddr_union* to,
+    struct sip_msg *msg );
 int forward_reply( struct sip_msg* msg);
 
 #endif
index 5d3b8ba..d587fa0 100644 (file)
--- a/globals.h
+++ b/globals.h
@@ -44,8 +44,10 @@ extern int children_no;
 extern int dont_fork;
 extern int check_via;
 extern int received_dns;
-extern int loop_checks;
+extern int syn_branch;
 extern int process_no;
+extern int sip_warning;
+extern int server_signature;
 /*
  * debug & log_stderr moved to dprint.h*/
 
@@ -57,4 +59,13 @@ extern unsigned int msg_no;
 
 extern unsigned int shm_mem_size;
 
+/* FIFO server config */
+char extern *fifo; /* FIFO name */
+extern int fifo_mode;
+
+extern int *pids;
+extern int process_no;
+
+extern int reply_to_via;
+
 #endif
diff --git a/main.c b/main.c
index 9be5224..53e8b00 100644 (file)
--- a/main.c
+++ b/main.c
@@ -36,6 +36,7 @@
 #include "resolve.h"
 #include "parser/parse_hname2.h"
 #include "parser/digest/digest_parser.h"
+#include "fifo_server.h"
 
 
 #include "stats.h"
@@ -93,21 +94,6 @@ static char flags[]=
 #ifdef DEBUG_DMALLOC
 ", DEBUG_DMALLOC"
 #endif
-#ifdef SILENT_FR
-", SILENT_FR"
-#endif
-#ifdef USE_SYNONIM
-", USE_SYNONIM"
-#endif
-#ifdef NOISY_REPLIES
-", NOISY_REPLIES"
-#endif
-#ifdef VERY_NOISY_REPLIES
-", VERY_NOISY_REPLIES"
-#endif
-#ifdef NEW_HNAME
-", NEW_HNAME"
-#endif
 #ifdef FAST_LOCK
 ", FAST_LOCK"
 #ifdef BUSY_WAIT
@@ -125,7 +111,6 @@ static char flags[]=
 static char help_msg[]= "\
 Usage: " NAME " -l address [-p port] [-l address [-p port]...] [options]\n\
 Options:\n\
-    -c           Perform loop checks and compute branches\n\
     -f file      Configuration file (default " CFG_FILE ")\n\
     -p port      Listen on the specified port (default: 5060)\n\
                  applies to the last address in -l and to all \n\
@@ -173,8 +158,8 @@ void print_ct_constants()
 #endif
 */
        printf("MAX_RECV_BUFFER_SIZE %d, MAX_LISTEN %d,"
-                       " MAX_URI_SIZE %d, MAX_PROCESSES %d, BUF_SIZE %d\n",
-               MAX_RECV_BUFFER_SIZE, MAX_LISTEN, MAX_URI_SIZE, MAX_PROCESSES,
+                       " MAX_URI_SIZE %d, BUF_SIZE %d\n",
+               MAX_RECV_BUFFER_SIZE, MAX_LISTEN, MAX_URI_SIZE, 
                BUF_SIZE );
 }
 
@@ -209,14 +194,29 @@ int sig_flag = 0;              /* last signal received */
 int debug = 0;
 int dont_fork = 0;
 int log_stderr = 0;
-int check_via =  0;        /* check if reply first via host==us */
-int loop_checks = 0;   /* calculate branches and check for loops/spirals */
-int received_dns = 0;      /* use dns and/or rdns or to see if we need to 
-                              add a ;received=x.x.x.x to via: */
+/* check if reply first via host==us */
+int check_via =  0;        
+/* shall use stateful synonym branches? faster but not reboot-safe */
+int syn_branch = 0;
+/* should replies include extensive warnings? by default yes,
+   good for trouble-shooting
+*/
+int sip_warning = 1;
+/* should localy-generated messages include server's signature?
+   be default yes, good for trouble-shooting
+*/
+int server_signature=1;
+/* use dns and/or rdns or to see if we need to add 
+   a ;received=x.x.x.x to via: */
+int received_dns = 0;      
 char* working_dir = 0;
 char* chroot_dir = 0;
 int uid = 0;
 int gid = 0;
+/* a hint to reply modules whether they should send reply
+   to IP advertised in Via or IP from which a request came
+*/
+int reply_to_via=0;
 
 #if 0
 char* names[MAX_LISTEN];              /* our names */
@@ -497,6 +497,12 @@ int main_loop()
                        LOG(L_ERR, "init_child failed\n");
                        goto error;
                }
+
+               /* if configured to do so, start a server for accepting FIFO commands */
+               if (open_fifo_server()<0) {
+                       LOG(L_ERR, "opening fifo server failed\n");
+                       goto error;
+               }
                is_main=1; /* hack 42: call init_child with is_main=0 in case
                                         some modules wants to fork a child */
                
@@ -543,6 +549,11 @@ int main_loop()
                        /*close(udp_sock)*/; /*if it's closed=>sendto invalid fd errors?*/
                }
        }
+       /* if configured to do so, start a server for accepting FIFO commands */
+       if (open_fifo_server()<0) {
+               LOG(L_ERR, "opening fifo server failed\n");
+               goto error;
+       }
        /*this is the main process*/
        pids[process_no]=getpid();
        process_bit = 0;
@@ -590,6 +601,7 @@ int main_loop()
 static void sig_usr(int signo)
 {
 
+
        if (is_main){
                if (sig_flag==0) sig_flag=signo;
                else /*  previous sig. not processed yet, ignoring? */
@@ -604,6 +616,11 @@ static void sig_usr(int signo)
                        case SIGINT:
                        case SIGPIPE:
                        case SIGTERM:
+                                       /* print memory stats for non-main too */
+                                       #ifdef PKG_MALLOC
+                                       LOG(L_INFO, "Memory status (pkg):\n");
+                                       pkg_status();
+                                       #endif
                                        exit(0);
                                        break;
                        case SIGUSR1:
@@ -675,7 +692,7 @@ int main(int argc, char** argv)
 #ifdef STATS
        "s:"
 #endif
-       "f:p:m:b:l:n:rRvcdDEVhw:t:u:g:P:";
+       "f:p:m:b:l:n:rRvdDEVhw:t:u:g:P:";
        
        while((c=getopt(argc,argv,options))!=-1){
                switch(c){
@@ -748,9 +765,6 @@ int main(int argc, char** argv)
                        case 'v':
                                        check_via=1;
                                        break;
-                       case 'c':
-                                       loop_checks=1;
-                                       break;
                        case 'r':
                                        received_dns|=DO_DNS;
                                        break;
@@ -847,6 +861,11 @@ int main(int argc, char** argv)
                goto error;
        }
 
+       /* register a diagnostic FIFO command */
+       if (register_fifo_cmd(print_fifo_cmd, "print", 0)<0) {
+               LOG(L_CRIT, "unable to register 'print' FIFO cmd\n");
+               goto error;
+       }
 
        /*register builtin  modules*/
        register_builtin_modules();
@@ -869,17 +888,19 @@ int main(int argc, char** argv)
 
        
        if (children_no<=0) children_no=CHILD_NO;
+#ifdef _OBSOLETED
        else if (children_no >= MAX_PROCESSES ) {
                fprintf(stderr, "ERROR: too many children processes configured;"
                                " maximum is %d\n",
                        MAX_PROCESSES-1 );
                goto error;
        }
+#endif
        
        if (working_dir==0) working_dir="/";
        /*alloc pids*/
 #ifdef SHM_MEM
-       pids=shm_malloc(sizeof(int)*(children_no+1));
+       pids=shm_malloc(sizeof(int)*(children_no+1/*timer */+1/*fifo*/));
 #else
        pids=malloc(sizeof(int)*(children_no+1));
 #endif
index b6e7e69..b66045d 100644 (file)
@@ -80,8 +80,7 @@ void MDStringArray (char *dst, str src[], int size)
   }
   MDFinal (digest, &context);
 
-  for (i=0; i<16; i++)
-    sprintf(dst+i*2, "%02x", digest[i] );
+  string2hex(digest, 16, dst );
+  DBG("DEBUG: MD5 calculated: %.*s\n", MD5_LEN, dst );
 
-  DBG("DEBUG: MD5 calculated: %32s\n", dst );
 }
index 9e116f4..d82f023 100644 (file)
--- a/mem/mem.c
+++ b/mem/mem.c
@@ -2,8 +2,10 @@
  * $Id *
  */
 
+#include <stdio.h>
 #include "../config.h"
 #include "../dprint.h"
+#include "../globals.h"
 #include "mem.h"
 
 #ifdef PKG_MALLOC
@@ -42,6 +44,8 @@ int init_mallocs()
        #endif
        if (mem_block==0){
                LOG(L_CRIT, "could not initialize memory pool\n");
+               fprintf(stderr, "Too much pkg memory demanded: %d\n",
+                       PKG_MEM_POOL_SIZE );
                return -1;
        }
 #endif
@@ -49,6 +53,8 @@ int init_mallocs()
 #ifdef SHM_MEM
        if (shm_mem_init()<0) {
                LOG(L_CRIT, "could not initialize shared memory pool, exiting...\n");
+                fprintf(stderr, "Too much shared memory demanded: %d\n",
+                       shm_mem_size );
                return -1;
        }
 #endif
index 8c432ef..bc764ce 100644 (file)
@@ -1,5 +1,10 @@
 #ifdef DBG_QM_MALLOC
 
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+
 #include "../globals.h"
 #include "../config.h"
 
@@ -33,7 +38,7 @@ void memtest()
                                                                 __LINE__);
        char tst_mem[TEST_SIZE];
        struct MY_BLOCK* mem_block;
-       char *p0,*p1,*p2,*p3,*p4,*p5,*p6,*p7,*p8,*p9;
+       char *p0,*p1,*p2,*p3,*p4,*p5,*p6/*,*p7,*p8,*p9*/;
        int i, j, f;
        char *p[TEST_RUN];
        int t;
index 9c43180..a7ece8f 100644 (file)
@@ -5,6 +5,8 @@
 
 #ifdef SHM_MEM
 
+#include <stdlib.h>
+
 #include "shm_mem.h"
 #include "../config.h"
 #include "../globals.h"
@@ -81,6 +83,37 @@ inline static void* sh_realloc(void* p, unsigned int size)
     guarantee for buffer content; if allocation fails, we return
     NULL
 */
+
+#ifdef DBG_QM_MALLOC
+void* _shm_resize( void* p, unsigned int s, char* file, char* func, unsigned int line)
+#else
+void* _shm_resize( void* p , unsigned int s)
+#endif
+{
+#ifdef VQ_MALLOC
+       struct vqm_frag *f;
+#endif
+       if (p==0) {
+               DBG("WARNING:vqm_resize: resize(0) called\n");
+               return shm_malloc( s );
+       }
+#      ifdef DBG_QM_MALLOC
+#      ifdef VQ_MALLOC
+       f=(struct  vqm_frag*) ((char*)p-sizeof(struct vqm_frag));
+       DBG("_shm_resize(%p, %d), called from %s: %s(%d)\n",  
+               p, s, file, func, line);
+       VQM_DEBUG_FRAG(shm_block, f);
+       if (p>(void *)shm_block->core_end || p<(void*)shm_block->init_core){
+               LOG(L_CRIT, "BUG: vqm_free: bad pointer %p (out of memory block!) - "
+                               "aborting\n", p);
+               abort();
+       }
+#endif
+#      endif
+       return sh_realloc( p, s ); 
+}
+
+#ifdef _OBSOLETED
 #ifdef DBG_QM_MALLOC
 void* _shm_resize( void* p, unsigned int s, char* file, char* func, unsigned int line)
 #else
@@ -108,10 +141,11 @@ void* _shm_resize( void* p , unsigned int s)
 #      ifdef VQ_MALLOC
        f=(struct  vqm_frag*) ((char*)p-sizeof(struct vqm_frag));
 #      ifdef DBG_QM_MALLOC
-       DBG("_shm_resize(%x, %d), called from %s: %s(%d)\n",  p, s, file, func, line);
+       DBG("_shm_resize(%p, %d), called from %s: %s(%d)\n",  
+               p, s, file, func, line);
        VQM_DEBUG_FRAG(shm_block, f);
        if (p>(void *)shm_block->core_end || p<(void*)shm_block->init_core){
-               LOG(L_CRIT, "BUG: vqm_free: bad pointer %x (out of memory block!) - "
+               LOG(L_CRIT, "BUG: vqm_free: bad pointer %p (out of memory block!) - "
                                "aborting\n", p);
                abort();
        }
@@ -126,6 +160,7 @@ void* _shm_resize( void* p , unsigned int s)
        /* we can't make the request happy with current size */
        return sh_realloc( p, s ); 
 }
+#endif
 
 
 
index fcbfa22..b93a265 100644 (file)
@@ -49,6 +49,8 @@
 
 #ifdef VQ_MALLOC
 
+#include <stdlib.h>
+
 #include "../config.h"
 #include "../globals.h"
 #include "vq_malloc.h"
@@ -79,7 +81,7 @@ void my_assert( int assertation, int line, char *file, char *function )
 {
        if (assertation) return;
 
-       LOG(L_CRIT,"CRIT: assertation failed in $s (%s:%d)\n",
+       LOG(L_CRIT,"CRIT: assertation failed in %s (%s:%d)\n",
                function, file, line);
        abort();
 }
@@ -88,16 +90,15 @@ void my_assert( int assertation, int line, char *file, char *function )
 void vqm_debug_frag(struct vqm_block* qm, struct vqm_frag* f)
 {
 
-       int r;
 
        if (f->check!=ST_CHECK_PATTERN){
-               LOG(L_CRIT, "BUG: vqm_*: fragm. %x beginning overwritten(%x)!\n",
+               LOG(L_CRIT, "BUG: vqm_*: fragm. %p beginning overwritten(%x)!\n",
                                f, f->check);
                vqm_status(qm);
                abort();
        };
        if (memcmp(f->end_check, END_CHECK_PATTERN, END_CHECK_PATTERN_LEN)!=0) {
-               LOG(L_CRIT, "BUG: vqm_*: fragm. %x end overwritten(%*s)!\n",
+               LOG(L_CRIT, "BUG: vqm_*: fragm. %p end overwritten(%*s)!\n",
                                f, END_CHECK_PATTERN_LEN, f->end_check );
                vqm_status(qm);
                abort();
@@ -235,7 +236,6 @@ static inline void vqm_detach_free( struct vqm_block* qm, struct vqm_frag* frag)
 {
 
        struct vqm_frag *prev, *next;
-       struct vqm_frag_end *end;
 
        prev=FRAG_END(frag)->prv_free; 
        next=frag->u.nxt_free;
@@ -260,8 +260,8 @@ void* vqm_malloc(struct vqm_block* qm, unsigned int size)
        
 #ifdef DBG_QM_MALLOC
        unsigned int demanded_size;
-       DBG("vqm_malloc(%x, %d) called from %s: %s(%d)\n", qm, size, file, func,
-                       line);
+       DBG("vqm_malloc(%p, %d) called from %s: %s(%d)\n", qm, size, file,
+        func, line);
        demanded_size = size;
 #endif
        new_chunk=0;
@@ -285,7 +285,11 @@ void* vqm_malloc(struct vqm_block* qm, unsigned int size)
 
        if (!new_chunk) { /* no chunk can be reused; slice one from the core */
                new_chunk=MORE_CORE( qm, bucket, size );
-               if (!new_chunk) return 0;
+               if (!new_chunk) {
+                       LOG(L_ERR, "vqm_malloc(%p, %d) called from %s: %s(%d)\n", 
+                               qm, size, file, func, line);
+                       return 0;
+               }
        }
        new_chunk->u.inuse.magic = FR_USED;
        new_chunk->u.inuse.bucket=bucket;
@@ -295,7 +299,7 @@ void* vqm_malloc(struct vqm_block* qm, unsigned int size)
        new_chunk->line=line;
        new_chunk->demanded_size=demanded_size;
        qm->usage[ bucket ]++;
-       DBG("vqm_malloc( %x, %d ) returns address %x in bucket %d, real-size %d \n",
+       DBG("vqm_malloc( %p, %d ) returns address %p in bucket %d, real-size %d \n",
                qm, demanded_size, (char*)new_chunk+sizeof(struct vqm_frag), 
                bucket, size );
 
@@ -317,9 +321,10 @@ void vqm_free(struct vqm_block* qm, void* p)
        unsigned char b;
 
 #ifdef DBG_QM_MALLOC
-       DBG("vqm_free(%x, %x), called from %s: %s(%d)\n", qm, p, file, func, line);
+       DBG("vqm_free(%p, %p), called from %s: %s(%d)\n", 
+               qm, p, file, func, line);
        if (p>(void *)qm->core_end || p<(void*)qm->init_core){
-               LOG(L_CRIT, "BUG: vqm_free: bad pointer %x (out of memory block!) - "
+               LOG(L_CRIT, "BUG: vqm_free: bad pointer %p (out of memory block!) - "
                                "aborting\n", p);
                abort();
        }
@@ -389,13 +394,13 @@ void vqm_free(struct vqm_block* qm, void* p)
 
 void dump_frag( struct vqm_frag* f, int i )
 {
-       LOG(L_INFO, "    %3d. address=%x  real size=%d bucket=%d\n", i, 
+       LOG(L_INFO, "    %3d. address=%p  real size=%d bucket=%d\n", i, 
                (char*)f+sizeof(struct vqm_frag), f->size, f->u.inuse.bucket);
 #ifdef DBG_QM_MALLOC
        LOG(L_INFO, "            demanded size=%d\n", f->demanded_size );
        LOG(L_INFO, "            alloc'd from %s: %s(%d)\n",
                f->file, f->func, f->line);
-       LOG(L_INFO, "        start check=%x, end check= %*s\n",
+       LOG(L_INFO, "        start check=%x, end check= %.*s\n",
                        f->check, END_CHECK_PATTERN_LEN, f->end_check );
 #endif
 }
@@ -403,9 +408,9 @@ void dump_frag( struct vqm_frag* f, int i )
 void vqm_status(struct vqm_block* qm)
 {
        struct vqm_frag* f;
-       unsigned int i,j,on_list;
+       unsigned int i,on_list;
 
-       LOG(L_INFO, "vqm_status (%x):\n", qm);
+       LOG(L_INFO, "vqm_status (%p):\n", qm);
        if (!qm) return;
        LOG(L_INFO, " heap size= %d, available: %d\n", 
                qm->core_end-qm->init_core, qm->free_core );
@@ -422,7 +427,7 @@ void vqm_status(struct vqm_block* qm)
        DBG("dumping bucket statistics:\n");
        for (i=0; i<=BIG_BUCKET(qm); i++) {
                for(on_list=0, f=qm->next_free[i]; f; f=f->u.nxt_free ) on_list++;
-               LOG(L_DBG, "    %3d. bucket: in use: %d, on free list: %d\n", 
+               LOG(L_DBG, "    %3d. bucket: in use: %ld, on free list: %d\n", 
                        i, qm->usage[i], on_list );
        }
 #endif
index c529024..adaef34 100644 (file)
@@ -113,6 +113,7 @@ struct vqm_block{
 struct vqm_block* vqm_malloc_init(char* address, unsigned int size);
 
 #ifdef DBG_QM_MALLOC
+void vqm_debug_frag(struct vqm_block* qm, struct vqm_frag* f);
 void* vqm_malloc(struct vqm_block*, unsigned int size, char* file, char* func, 
                                        unsigned int line);
 void  vqm_free(struct vqm_block*, void* p, char* file, char* func, 
index 66b9777..8e9c534 100644 (file)
@@ -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:mrx@iptel.org
+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
diff --git a/modules/tm/TODO b/modules/tm/TODO
deleted file mode 100644 (file)
index 46b2cf9..0000000
+++ /dev/null
@@ -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)
index 39368d4..88c0f63 100644 (file)
 #define T_TABLE_POWER    12
 #define TABLE_ENTRIES    (1 << (T_TABLE_POWER))
 
-/* maximum number of forks per transaction */
-enum fork_list { MAX_FORK=4 , NO_RPL_BRANCH ,NR_OF_CLIENTS};
-enum fork_type { DEFAULT, NO_RESPONSE };
+/* this is where table size is defined now -- sort of
+   ugly, core should not be bothered by TM table size,
+   but on the other, core's stateless forwarding should 
+   have consistent branch generation with stateful mode
+   and needs to calculate branch/hash, for which table size
+   is needed 
+*/
+#include "../../hash_func.h"
 
 /* maximumum length of localy generated acknowledgement */
 #define MAX_ACK_LEN   1024
@@ -41,11 +46,9 @@ enum fork_type { DEFAULT, NO_RESPONSE };
 #define REPLY_OVERBUFFER_LEN 160
 #define TAG_OVERBUFFER_LEN 32
 
-/* character which separates individual parts of MPLS-ized branch */
-#ifdef BRUT_HACK
-#      define BRANCH_SEPARATOR 'X'
-#else
-#      define BRANCH_SEPARATOR '.'
-#endif
-
+/* dimensions of FIFO server */
+#define MAX_METHOD     64
+#define MAX_HEADER     1024
+#define MAX_BODY       1024
+#define MAX_DST        512
 #endif
diff --git a/modules/tm/fix_lumps.h b/modules/tm/fix_lumps.h
new file mode 100644 (file)
index 0000000..eb2cbd4
--- /dev/null
@@ -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
index 373f4a8..2247b70 100644 (file)
@@ -2,15 +2,28 @@
  * $Id$
  */
 
-#include "hash_func.h"
+#include "../../mem/shm_mem.h"
+#include "../../hash_func.h"
 #include "h_table.h"
 #include "../../dprint.h"
-#include "sh_malloc.h"
 #include "../../md5utils.h"
 /* bogdan test */
 #include "../../ut.h"
 #include "../../globals.h"
 #include "../../error.h"
+#include "t_reply.h"
+#include "t_cancel.h"
+
+unsigned int transaction_count( void )
+{
+       unsigned int i;
+       unsigned int count;
+
+       count=0;        
+       for (i=0; i<TABLE_ENTRIES; i++) 
+               count+=hash_table->entrys[i].entries;
+       return count;
+}
 
 
 
@@ -18,6 +31,7 @@ void free_cell( struct cell* dead_cell )
 {
        char *b;
        int i;
+       struct sip_msg *rpl;
 
        release_cell_lock( dead_cell );
        shm_lock();
@@ -27,31 +41,35 @@ void free_cell( struct cell* dead_cell )
                sip_msg_free_unsafe( dead_cell->uas.request );
        if ( dead_cell->uas.response.buffer )
                shm_free_unsafe( dead_cell->uas.response.buffer );
+       if (dead_cell->uas.to_tag.s)
+               shm_free_unsafe(dead_cell->uas.to_tag.s);
+
+       /* completion callback */
+       if (dead_cell->cbp) shm_free_unsafe(dead_cell->cbp);
 
        /* UA Clients */
        for ( i =0 ; i<dead_cell->nr_of_outgoings;  i++ )
        {
                /* retransmission buffer */
                if ( (b=dead_cell->uac[i].request.buffer) )
-               {
                        shm_free_unsafe( b );
-                       b = 0;
-               }
+#ifdef OLD_CANCEL
                if ( (b=dead_cell->uac[i].request.ack) )
-               {
                        shm_free_unsafe( b );
-                       b = 0;
-               }
                if ( (b=dead_cell->uac[i].request.cancel) )
-               {
                        shm_free_unsafe( b );
-                       b = 0;
+#endif
+               b=dead_cell->uac[i].local_cancel.buffer;
+               if (b!=0 && b!=BUSY_BUFFER)
+                       shm_free_unsafe( b );
+               rpl=dead_cell->uac[i].reply;
+               if (rpl && rpl!=FAKED_REPLY) {
+                       sip_msg_free_unsafe( rpl );
                }
+#ifdef _OBSOLETED
                if ( (b=dead_cell->uac[i].rpl_buffer.s) )
-               {
                        shm_free_unsafe( b );
-                       b = 0;
-               }
+#endif
        }
 
        /* the cell's body */
@@ -67,16 +85,16 @@ struct cell*  build_cell( struct sip_msg* p_msg )
 {
        struct cell* new_cell;
        unsigned int i;
-#ifndef USE_SYNONIM
-       str          src[8];
-#endif
+       unsigned int rand;
+       int size;
+       char *c;
+       struct ua_client *uac;
 
-       /* do we have the source for the build process? */
-       if (!p_msg)
-               return NULL;
+       /* avoid 'unitialized var use' warning */
+       rand=0;
 
        /* allocs a new cell */
-       new_cell = (struct cell*)sh_malloc( sizeof( struct cell ) );
+       new_cell = (struct cell*)shm_malloc( sizeof( struct cell ) );
        if  ( !new_cell ) {
                ser_error=E_OUT_OF_MEM;
                return NULL;
@@ -90,66 +108,75 @@ struct cell*  build_cell( struct sip_msg* p_msg )
        new_cell->uas.response.fr_timer.tg=TG_FR;
        new_cell->uas.response.fr_timer.payload =
                new_cell->uas.response.retr_timer.payload = &(new_cell->uas.response);
+       new_cell->uas.response.my_T=new_cell;
 
        /* bogdan - debug */
        /*fprintf(stderr,"before clone VIA |%.*s|\n",via_len(p_msg->via1),
                via_s(p_msg->via1,p_msg));*/
 
-       new_cell->uas.request = sip_msg_cloner(p_msg);
-
-    /* bogdan - debug */
-    /*fprintf(stderr,"after clone VIA |%.*s|\n",
-               via_len(new_cell->uas.request->via1),
-               via_s(new_cell->uas.request->via1,new_cell->uas.request) );*/
+       if (p_msg) {
+               new_cell->uas.request = sip_msg_cloner(p_msg);
+               if (!new_cell->uas.request)
+                       goto error;
+       }
 
-       if (!new_cell->uas.request)
-               goto error;
-       new_cell->uas.tag = &( get_to(new_cell->uas.request)->tag_value );
+       /* new_cell->uas.to_tag = &( get_to(new_cell->uas.request)->tag_value ); */
        new_cell->uas.response.my_T = new_cell;
 
        /* UAC */
-       for(i=0;i<MAX_FORK;i++)
+       for(i=0;i<MAX_BRANCHES;i++)
        {
-               new_cell->uac[i].request.my_T = new_cell;
-               new_cell->uac[i].request.branch = i;
-               new_cell->uac[i].request.fr_timer.tg = TG_FR;
-               new_cell->uac[i].request.retr_timer.tg = TG_RT;
-               new_cell->uac[i].request.retr_timer.payload = 
-                       new_cell->uac[i].request.fr_timer.payload =
-                       &(new_cell->uac[i].request);
+               uac=&new_cell->uac[i];
+               uac->request.my_T = new_cell;
+               uac->request.branch = i;
+               uac->request.fr_timer.tg = TG_FR;
+               uac->request.retr_timer.tg = TG_RT;
+               uac->request.retr_timer.payload = 
+                       uac->request.fr_timer.payload =
+                       &uac->request;
+               uac->local_cancel=uac->request;
        }
 
        /* global data for transaction */
-       new_cell->hash_index = p_msg->hash_index;
+       if (p_msg) {
+               new_cell->hash_index = p_msg->hash_index;
+       } else {
+               rand = random();
+               new_cell->hash_index = rand % TABLE_ENTRIES ;
+       }
        new_cell->wait_tl.payload = new_cell;
        new_cell->dele_tl.payload = new_cell;
        new_cell->relaied_reply_branch   = -1;
-       new_cell->T_canceled = T_UNDEFINED;
+       /* new_cell->T_canceled = T_UNDEFINED; */
        new_cell->wait_tl.tg=TG_WT;
        new_cell->dele_tl.tg=TG_DEL;
-#ifndef USE_SYNONIM
-       src[0]= p_msg->from->body;
-       src[1]= p_msg->to->body;
-       src[2]= p_msg->callid->body;
-       src[3]= p_msg->first_line.u.request.uri;
-       src[4]= get_cseq( p_msg )->number;
-
-       /* topmost Via is part of transaction key as well ! */
-       src[5]= p_msg->via1->host;
-       src[6]= p_msg->via1->port_str;
-       if (p_msg->via1->branch) {
-               src[7]= p_msg->via1->branch->value;
-               MDStringArray ( new_cell->md5, src, 8 );
-       } else {
-               MDStringArray ( new_cell->md5, src, 7 );
+
+       if (!syn_branch) {
+               if (p_msg) {
+                       /* char value of a proxied transaction is
+                          calculated out of header-fileds forming
+                          transaction key
+                       */
+                       char_msg_val( p_msg, new_cell->md5 );
+               } else {
+                       /* char value for a UAC transaction is created
+                          randomly -- UAC is an originating stateful element 
+                          which cannot be refreshed, so the value can be
+                          anything
+                       */
+                       /* HACK : not long enough */
+                       c=new_cell->md5;
+                       size=MD5_LEN;
+                       memset(c, '0', size );
+                       int2reverse_hex( &c, &size, rand );
+               }
        }
- #endif
 
        init_cell_lock(  new_cell );
        return new_cell;
 
 error:
-       sh_free(new_cell);
+       shm_free(new_cell);
        return NULL;
 }
 
@@ -183,7 +210,7 @@ void free_hash_table( struct s_table *hash_table )
                for ( i=0 ; i<NR_OF_TIMER_LISTS ; i++ )
                        release_timerlist_lock( &(hash_table->timers[i]) );
 
-               sh_free( hash_table );
+               shm_free( hash_table );
        }
 }
 
@@ -198,7 +225,7 @@ struct s_table* init_hash_table()
        int              i;
 
        /*allocs the table*/
-       hash_table = (struct s_table*)sh_malloc( sizeof( struct s_table ) );
+       hash_table = (struct s_table*)shm_malloc( sizeof( struct s_table ) );
        if ( !hash_table )
                goto error;
 
@@ -248,6 +275,9 @@ void insert_into_hash_table_unsafe( struct s_table *hash_table,
        } else p_entry->first_cell = p_cell;
 
        p_entry->last_cell = p_cell;
+
+       /* update stats */
+       p_entry->entries++;
 }
 
 
@@ -255,21 +285,22 @@ void insert_into_hash_table_unsafe( struct s_table *hash_table,
 
 void insert_into_hash_table(struct s_table *hash_table,  struct cell * p_cell)
 {
-       lock( &(hash_table->entrys[ p_cell->hash_index ].mutex) );
+       LOCK_HASH(p_cell->hash_index);
        insert_into_hash_table_unsafe( hash_table,  p_cell );
-       unlock( &(hash_table->entrys[ p_cell->hash_index ].mutex) );
+       UNLOCK_HASH(p_cell->hash_index);
 }
 
 
 
 
 /*  Un-link a  cell from hash_table, but the cell itself is not released */
-void remove_from_hash_table(struct s_table *hash_table,  struct cell * p_cell)
+void remove_from_hash_table_unsafe(struct s_table *hash_table,  
+ struct cell * p_cell)
 {
        struct entry*  p_entry  = &(hash_table->entrys[p_cell->hash_index]);
 
        /* unlink the cell from entry list */
-       lock( &(p_entry->mutex) );
+       /* lock( &(p_entry->mutex) ); */
 
        if ( p_cell->prev_cell )
                p_cell->prev_cell->next_cell = p_cell->next_cell;
@@ -280,8 +311,10 @@ void remove_from_hash_table(struct s_table *hash_table,  struct cell * p_cell)
                p_cell->next_cell->prev_cell = p_cell->prev_cell;
        else
                p_entry->last_cell = p_cell->prev_cell;
+       /* update stats */
+       p_entry->entries--;
 
-       unlock( &(p_entry->mutex) );
+       /* unlock( &(p_entry->mutex) ); */
 }
 
 
index d92388d..57e6ef5 100644 (file)
 #include "../../types.h"
 #include "../../md5utils.h"
 #include "config.h"
-/*#include "t_flags.h"*/
 
 struct s_table;
 struct entry;
 struct cell;
 struct timer;
+struct retr_buf;
 
-#include "sh_malloc.h"
-
-#include "timer.h"
+#include "../../mem/shm_mem.h"
+#include "timer.h" 
 #include "lock.h"
 #include "sip_msg.h"
-
-
-#define T_UNDEFINED  ( (struct cell*) -1 )
-#define T_NULL       ( (struct cell*) 0 )
+#include "t_reply.h"
+#include "t_hooks.h"
 
 
 #define NO_CANCEL       ( (char*) 0 )
@@ -39,7 +36,14 @@ struct timer;
 #define TYPE_LOCAL_CANCEL -1
 #define TYPE_REQUEST       0
 
-
+/* to be able to assess whether a script writer forgot to
+   release a transaction and leave it for ever in memory,
+   we mark it with operations done over it; if none of these
+   flags is set and script is being left, it is a sign of
+   script error and we need to release on writer's
+   behalf
+*/
+enum kill_reason { REQ_FWDED=1, REQ_RPLD=2, REQ_RLSD=4, REQ_EXIST=8 };
 
 typedef struct retr_buf
 {
@@ -49,18 +53,10 @@ typedef struct retr_buf
 
        char *buffer;
        int   buffer_len;
-       char *ack;
-       int   ack_len;
-       char *cancel;
-       int   cancel_len;
 
-       /* v6 changes; -jiri
-       struct sockaddr_in to; */
        union sockaddr_union to;
        struct socket_info* send_sock;
 
-       size_t tolen;
-
        /* a message can be linked just to retransmission and FR list */
        struct timer_link retr_timer;
        struct timer_link fr_timer;
@@ -81,7 +77,7 @@ typedef struct ua_server
        struct sip_msg   *request;
        struct retr_buf  response;
        unsigned int     status;
-       str              *tag;
+       str              to_tag;
        unsigned int     isACKed;
 }ua_server_type;
 
@@ -92,11 +88,21 @@ typedef struct ua_server
 typedef struct ua_client
 {
        struct retr_buf  request;
-       unsigned int     status;
-       str              tag;
+       /* we maintain a separate copy of cancel rather than
+          reuse the strructure for original request; the 
+          original request is no longer needed but its delayed
+          timer may fire and interfere with whoever tries to
+          rewrite it
+       */
+       struct retr_buf local_cancel;
+       /* pointer to retransmission buffer where uri is printed;
+          good for generating ACK/CANCEL */
        str              uri;
-       str              rpl_buffer;
-       unsigned int     rpl_received;
+       /* if we store a reply (branch picking), this is where it is */
+       struct sip_msg  *reply;
+       /* if we don't store, we at least want to know the status */
+       int     last_received;
+
 }ua_client_type;
 
 
@@ -109,8 +115,40 @@ typedef struct cell
        struct cell*     next_cell;
        struct cell*     prev_cell;
 
-       /* indicates which process is currently processing this transaction */
-       process_bm_t  ref_bitmap;
+       /* needed for generating local ACK/CANCEL for local
+          transactions; all but cseq_n include the entire
+          header field value, cseq_n only Cseq number; with
+          local transactions, pointers point to outbound buffer,
+          with proxied transactions to inbound request */
+       str from, callid, cseq_n, to;
+       /* a short-cut for remember whether this transaction needs
+          INVITE-special handling (e.g., CANCEL, ACK, FR...)
+       */
+       short is_invite;
+       /* method shortcut -- for local transactions, pointer to
+          outbound buffer, for proxies transactions pointer to
+          original message; needed for reply matching
+       */
+       str method;
+
+       /* callback and parameter on completion of local transactions */
+       transaction_cb *completion_cb;
+       /* the parameter stores a pointer to shmem -- it will be released
+          during freeing transaction too
+       */
+       void *cbp;
+
+       /* how many processes are currently processing this transaction ;
+          note that only processes working on a request/reply belonging
+          to a transaction increase ref_count -- timers don't, since we
+          rely on transaction state machine to clean-up all but wait timer
+          when entering WAIT state and the wait timer is the only place
+          from which a transaction can be deleted (if ref_count==0); good
+          for protecting from conditions in which wait_timer hits and
+          tries to delete a transaction whereas at the same time 
+          a delayed message belonging to the transaction is received
+       */
+       volatile unsigned int ref_count;
        /* tells in which hash table entry the cell lives */
        unsigned int  hash_index;
        /* sequence number within hash collision slot */
@@ -120,47 +158,43 @@ typedef struct cell
        struct timer_link wait_tl;
        struct timer_link dele_tl;
 
-       /* useful data */
        /* number of forks */
        int nr_of_outgoings;
        /* nr of replied branch */
        int relaied_reply_branch;
-       /* transaction that is canceled (usefull only for CANCEL req) */
-       struct cell *T_canceled;
        /* UA Server */
        struct ua_server  uas;
        /* UA Clients */
-       struct ua_client  uac[ NR_OF_CLIENTS ];
+       struct ua_client  uac[ MAX_BRANCHES ];
 
        /* protection against concurrent reply processing */
        ser_lock_t   reply_mutex;
-       /* protection against concurrent ACK processing */
-       ser_lock_t      ack_mutex;
 
-/*     tflags_t        flags; */
+       /* the route to take if no final positive reply arrived */
+       unsigned int on_negative;
+       /* set to one if you want to disallow silent transaction
+          dropping when C timer hits
+       */
+       int noisy_ctimer;
+       /* is it a local transaction ? */
+       int local;
 
-#ifdef WAIT
+#ifdef _XWAIT
        /* protection against reentering WAIT state */
        ser_lock_t      wait_mutex;
        /* has the transaction been put on wait status ? */
        int on_wait;
 #endif
 
-       /* this is where destination is stored for picked branch;
-       good if a need to forward ACK later on */
-       /* v6 changes; -jiri
-       struct sockaddr_in ack_to; */
-       union sockaddr_union ack_to;
-#ifndef        USE_SYNONIM
-       /* MD5checksum */
+       /* MD5checksum  (meaningful only if syn_branch=0 */
        char md5[MD5_LEN];
-#endif
 
 #ifdef EXTRA_DEBUG
        /* scheduled for deletion ? */
        short damocles;
 #endif
-
+       /* has the transaction been scheduled to die? */
+       enum kill_reason kr;
 }cell_type;
 
 
@@ -174,6 +208,7 @@ typedef struct entry
        unsigned int    next_label;
        /* sync mutex */
        ser_lock_t      mutex;
+       unsigned int    entries;
 }entry_type;
 
 
@@ -193,11 +228,15 @@ struct s_table* init_hash_table();
 void   free_hash_table( struct s_table* hash_table );
 void   free_cell( struct cell* dead_cell );
 struct cell*  build_cell( struct sip_msg* p_msg );
-void   remove_from_hash_table(struct s_table *hash_table,struct cell * p_cell);
-void   insert_into_hash_table(struct s_table *hash_table,struct cell * p_cell);
+void   remove_from_hash_table_unsafe(struct s_table *hash_table,
+       struct cell * p_cell);
+void   insert_into_hash_table(struct s_table *hash_table,
+       struct cell * p_cell);
 void   insert_into_hash_table_unsafe( struct s_table *hash_table,
                struct cell * p_cell );
 
+unsigned int transaction_count( void );
+
 #endif
 
 
diff --git a/modules/tm/hash_func.c b/modules/tm/hash_func.c
deleted file mode 100644 (file)
index b4d04af..0000000
+++ /dev/null
@@ -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);
-}
-
diff --git a/modules/tm/hash_func.h b/modules/tm/hash_func.h
deleted file mode 100644 (file)
index c31c7ad..0000000
+++ /dev/null
@@ -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
index 00a7f59..747389e 100644 (file)
 static int
        entry_semaphore=0, 
        timer_semaphore=0, 
-       reply_semaphore=0,
+       reply_semaphore=0;
+#ifdef _OBSOLETED
        ack_semaphore=0;
-#ifdef WAIT
+#endif
+#ifdef _XWAIT
 static int  wait_semaphore=0;
 #endif
 /* and the maximum number of semaphores in the entry_semaphore set */
@@ -171,9 +173,11 @@ again:
                        semctl( entry_semaphore, 0 , IPC_RMID , 0 );
                if (reply_semaphore>0)
                        semctl(reply_semaphore, 0 , IPC_RMID , 0 );
+#ifdef _OBSOLETED
                if (ack_semaphore>0)
                        semctl(reply_semaphore, 0 , IPC_RMID , 0 );
-#ifdef WAIT
+#endif
+#ifdef _XWAIT
                if (wait_semaphore>0)
                        semctl(wait_semaphore, 0 , IPC_RMID , 0 );
 #endif
@@ -233,7 +237,7 @@ again:
                        goto error;
                }
        }
-       
+#ifdef _OBSOLETED      
        if ((ack_semaphore=init_semaphore_set(sem_nr))<0){
                if (errno==EINVAL || errno==ENOSPC ) {
                        DBG( "DEBUG:lock_initialize: ack semaphore initialization"
@@ -247,8 +251,9 @@ again:
                        goto error;
                }
        }
+#endif
 
-#ifdef WAIT
+#ifdef _XWAIT
        if ((wait_semaphore=init_semaphore_set(sem_nr))<0){
                if (errno==EINVAL || errno==ENOSPC ) {
                        DBG( "DEBUG:lock_initialize: wait semaphore initialization"
@@ -281,7 +286,6 @@ error:
 void lock_cleanup()
 {
        /* must check if someone uses them, for now just leave them allocated*/
-       LOG(L_INFO, "INFO: lock_cleanup:  clean-up still not implemented properly \n");
 }
 
 #else
@@ -294,7 +298,6 @@ void lock_cleanup()
           no other process lives 
        */
 
-       LOG(L_INFO, "INFO: lock_cleanup:  clean-up still not implemented properly (no sibling check)\n");
        /* sibling double-check missing here; install a signal handler */
 
        if (entry_semaphore > 0 && 
@@ -306,18 +309,24 @@ void lock_cleanup()
        if (reply_semaphore > 0 &&
            semctl( reply_semaphore, 0 , IPC_RMID , 0 )==-1)
                LOG(L_ERR, "ERROR: lock_cleanup, reply_semaphore cleanup failed\n");
+#ifdef _OBSOLETED
        if (ack_semaphore > 0 &&
            semctl( ack_semaphore, 0 , IPC_RMID , 0 )==-1)
                LOG(L_ERR, "ERROR: lock_cleanup, ack_semaphore cleanup failed\n");
-#ifdef WAIT
+#endif
+#ifdef _XWAIT
        if (wait_semaphore > 0 &&
                semctl( wait_semaphore, 0 , IPC_RMID , 0 )==-1)
                LOG(L_ERR, "ERROR: lock_cleanup, wait_semaphore cleanup failed\n");
 #endif
 
 
-       entry_semaphore = timer_semaphore = reply_semaphore = ack_semaphore = 0;
-#ifdef WAIT
+       entry_semaphore = timer_semaphore = reply_semaphore 
+#ifdef _OBSOLETED
+               = ack_semaphore 
+#endif
+               = 0;
+#ifdef _XWAIT
        wait_semaphore = 0;
 #endif
 
@@ -333,17 +342,21 @@ int init_cell_lock( struct cell *cell )
 {
 #ifdef FAST_LOCK
        init_lock(cell->reply_mutex);
+#ifdef _OBSOLETED
        init_lock(cell->ack_mutex);
-#ifdef WAIT
+#endif
+#ifdef _XWAIT
        init_lock(cell->wait_mutex);
 #endif
        return 0;
 #else
        cell->reply_mutex.semaphore_set=reply_semaphore;
        cell->reply_mutex.semaphore_index = cell->hash_index % sem_nr;
+#ifdef _OBSOLETED
        cell->ack_mutex.semaphore_set=ack_semaphore;
        cell->ack_mutex.semaphore_index = cell->hash_index % sem_nr;
-#ifdef WAIT
+#endif
+#ifdef _XWAIT
        cell->wait_mutex.semaphore_set=wait_semaphore;
        cell->wait_mutex.semaphore_index = cell->hash_index % sem_nr;
 #endif /* WAIT */
index 2fede6a..07c091f 100644 (file)
@@ -42,7 +42,7 @@ enum timer_groups {
 
 
 #include "h_table.h"
-#include "timer.h"
+#include "timer.h" 
 
 /* Uni*x permissions for IPC */
 #define IPC_PERMISSIONS 0666
diff --git a/modules/tm/sh_malloc.h b/modules/tm/sh_malloc.h
deleted file mode 100644 (file)
index 97098aa..0000000
+++ /dev/null
@@ -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
index f6a4463..cd12187 100644 (file)
@@ -1,5 +1,16 @@
 /*
  * $Id$
+ * 
+ * cloning a message into shared memory (TM keeps a snapshot
+ * of messages in memory); note that many operations, which
+ * allocate pkg memory (such as parsing) cannot be used with
+ * a cloned message -- it would result in linking pkg structures
+ * to shmem msg and eventually in a memory error 
+ *
+ * the cloned message is stored in a single memory fragment to
+ * save too many shm_mallocs -- these are expensive as they
+ * not only take lookup in fragment table but also a shmem lock
+ * operation (the same for shm_free)
  */
 
 #include <stdio.h>
@@ -154,6 +165,12 @@ struct sip_msg*  sip_msg_cloner( struct sip_msg *org_msg )
                                                len+=ROUND4(sizeof(struct via_param ));
                                }
                                break;
+                       default:
+                               if (hdr->parsed) {
+                                       LOG(L_WARN, "WARNING: sip_msg_cloner: "
+                                               "header body ignored: %d\n", hdr->type );
+                               }
+                               break;
                }/*switch*/
        }/*for all headers*/
 
@@ -184,7 +201,7 @@ struct sip_msg*  sip_msg_cloner( struct sip_msg *org_msg )
        for(rpl_lump=org_msg->reply_lump;rpl_lump;rpl_lump=rpl_lump->next)
                len+=rpl_lump->text.len;
 
-       p=(char *)sh_malloc(len);foo=p;
+       p=(char *)shm_malloc(len);foo=p;
        if (!p)
        {
                LOG(L_ERR , "ERROR: sip_msg_cloner: cannot allocate memory\n" );
@@ -252,6 +269,11 @@ struct sip_msg*  sip_msg_cloner( struct sip_msg *org_msg )
                        hdr->name.s);
                new_hdr->body.s = translate_pointer(new_msg->buf, org_msg->buf,
                        hdr->body.s);
+               /* by default, we assume we don't understand this header in TM
+                  and better set it to zero; if we do, we will set a specific
+                  valu in the following switch statement
+               */
+               new_hdr->parsed=0;
 
                switch (hdr->type)
                {
@@ -360,6 +382,42 @@ struct sip_msg*  sip_msg_cloner( struct sip_msg *org_msg )
                        case HDR_ROUTE :
                                new_msg->route = new_hdr;
                                break;
+                       case HDR_RECORDROUTE :
+                               new_msg->record_route = new_hdr;
+                               break;
+                       case HDR_CONTENTTYPE :
+                               new_msg->content_type = new_hdr;
+                               break;
+                       case HDR_CONTENTLENGTH :
+                               new_msg->content_length = new_hdr;
+                               break;
+                       case HDR_AUTHORIZATION :
+                               new_msg->authorization = new_hdr;
+                               break;
+                       case HDR_EXPIRES :
+                               new_msg->expires = new_hdr;
+                               break;
+                       case HDR_PROXYAUTH :
+                               new_msg->proxy_auth = new_hdr;
+                               break;
+                       case HDR_WWWAUTH :
+                               new_msg->www_auth = new_hdr;
+                               break;
+                       case HDR_SUPPORTED :
+                               new_msg->supported = new_hdr;
+                               break;
+                       case HDR_REQUIRE :
+                               new_msg->require = new_hdr;
+                               break;
+                       case HDR_PROXYREQUIRE :
+                               new_msg->proxy_require = new_hdr;
+                               break;
+                       case HDR_UNSUPPORTED :
+                               new_msg->unsupported = new_hdr;
+                               break;
+                       case HDR_ALLOW :
+                               new_msg->unsupported = new_hdr; 
+                               break;
                }/*switch*/
 
                if ( last_hdr )
index c3a102d..a3a4316 100644 (file)
@@ -7,8 +7,7 @@
 #define _SIP_MSG_H
 
 #include "../../parser/msg_parser.h"
-
-#include "sh_malloc.h"
+#include "../../mem/shm_mem.h"
 
 #define  sip_msg_free(_p_msg) shm_free( (_p_msg ))
 #define  sip_msg_free_unsafe(_p_msg) shm_free_unsafe( (_p_msg) )
diff --git a/modules/tm/t_cancel.c b/modules/tm/t_cancel.c
new file mode 100644 (file)
index 0000000..e0d8327
--- /dev/null
@@ -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 );
+}
+
diff --git a/modules/tm/t_cancel.h b/modules/tm/t_cancel.h
new file mode 100644 (file)
index 0000000..b29a855
--- /dev/null
@@ -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
diff --git a/modules/tm/t_dlg.c b/modules/tm/t_dlg.c
new file mode 100644 (file)
index 0000000..ad012b8
--- /dev/null
@@ -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;
+}
+
diff --git a/modules/tm/t_dlg.h b/modules/tm/t_dlg.h
new file mode 100644 (file)
index 0000000..1fcaaa4
--- /dev/null
@@ -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
diff --git a/modules/tm/t_fork.c b/modules/tm/t_fork.c
deleted file mode 100644 (file)
index 7d1e44d..0000000
+++ /dev/null
@@ -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;
-}
-
-
-
diff --git a/modules/tm/t_fork.h b/modules/tm/t_fork.h
deleted file mode 100644 (file)
index dce7f73..0000000
+++ /dev/null
@@ -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
index b14c65d..60cd034 100644 (file)
@@ -4,19 +4,43 @@
  * transaction maintenance functions
  */
 
+#include <limits.h>
 #include "../../dprint.h"
 #include "../../config.h"
 #include "../../parser/parser_f.h"
 #include "../../ut.h"
-#include "hash_func.h"
+#include "../../hash_func.h"
+#include "../../dset.h"
 #include "t_funcs.h"
-#include "t_fork.h"
+#include "t_fwd.h"
+#include "t_lookup.h"
 
-
-struct cell      *T;
-unsigned int     global_msg_id;
+/* pointer to the big table where all the transaction data
+   lives
+*/
 struct s_table*  hash_table;
 
+/* ----------------------------------------------------- */
+
+int send_pr_buffer( struct retr_buf *rb,
+       void *buf, int len, char *function, int line )
+{
+       if (buf && len && rb )
+               return udp_send( rb->send_sock, buf,
+                       len, &rb->to,  sizeof(union sockaddr_union) ) ;
+       else {
+               LOG(L_CRIT, "ERROR: sending an empty buffer from %s (%d)\n",
+                       function, line );
+               return -1;
+       }
+}
+
+void start_retr( struct retr_buf *rb )
+{
+       rb->retr_list=RT_T1_TO_1;
+       set_timer( hash_table, &rb->retr_timer, RT_T1_TO_1 );
+       set_timer( hash_table, &rb->fr_timer, FR_TIMER_LIST );
+}
 
 int tm_startup()
 {
@@ -35,15 +59,12 @@ int tm_startup()
        hash_table->timers[WT_TIMER_LIST].id     = WT_TIMER_LIST;
        hash_table->timers[DELETE_LIST].id       = DELETE_LIST;
 
-       /* register the timer function */
-       register_timer( timer_routine , hash_table , 1 );
 
        /* fork table */
-       nr_forks = 0;
+       /* nr_forks = 0; */     
 
-       /*first msg id*/
-       global_msg_id = 0;
-       T = T_UNDEFINED;
+       /* init static hidden values */
+       init_t();
 
        return 0;
 }
@@ -82,208 +103,182 @@ void tm_shutdown()
 }
 
 
-
-
-/* function returns:
- *       1 - a new transaction was created
- *      -1 - error, including retransmission
- */
-int t_add_transaction( struct sip_msg* p_msg )
-{
-       struct cell*    new_cell;
-
-       DBG("DEBUG: t_add_transaction: adding......\n");
-       /* sanity check: ACKs can never establish a transaction */
-       if ( p_msg->REQ_METHOD==METHOD_ACK )
-       {
-               LOG(L_ERR, "ERROR: add_transaction: ACK can't be used to add"
-                       " transaction\n");
-               return -1;
-       }
-
-       /* creates a new transaction */
-       new_cell = build_cell( p_msg ) ;
-       DBG("DEBUG: t_add_transaction: new transaction created %p\n", new_cell);
-       if  ( !new_cell ){
-               LOG(L_ERR, "ERROR: add_transaction: out of mem:\n");
-               sh_status();
-               return -1;
-       }
-       /*insert the transaction into hash table*/
-       insert_into_hash_table( hash_table , new_cell );
-       DBG("DEBUG: t_add_transaction: new transaction inserted, hash: %d\n",
-               new_cell->hash_index );
-
-       T = new_cell;
-       T_REF(T);
-       return 1;
-}
-
-
-
-
 /*   returns 1 if everything was OK or -1 for error
 */
-int t_release_transaction( struct sip_msg* p_msg)
+int t_release_transaction( struct cell *trans )
 {
-      return t_put_on_wait( T );
-}
-
+       trans->kr|=REQ_RLSD;
 
+       reset_timer( hash_table, & trans->uas.response.fr_timer );
+       reset_timer( hash_table, & trans->uas.response.retr_timer );
 
-int t_unref( /* struct sip_msg* p_msg */ )
-{
-       if (T==T_UNDEFINED || T==T_NULL)
-               return -1;
-       T_UNREF( T );
-       T=T_UNDEFINED;
+       cleanup_uac_timers( trans );
+       
+       put_on_wait( trans );
        return 1;
 }
 
 
-
-
-
 /* ----------------------------HELPER FUNCTIONS-------------------------------- */
 
 
-int t_update_timers_after_sending_reply( struct retr_buf *rb )
-{
-       struct cell *Trans = rb->my_T;
-
-       /* make sure that if we send something final upstream, everything else
-          will be cancelled */
-       if (Trans->uas.status>=300&&Trans->uas.request->REQ_METHOD==METHOD_INVITE)
-       {
-               rb->retr_list = RT_T1_TO_1;
-               set_timer( hash_table, &(rb->retr_timer), RT_T1_TO_1 );
-               set_timer( hash_table, &(rb->fr_timer), FR_TIMER_LIST );
-       } else if ( Trans->uas.request->REQ_METHOD==METHOD_CANCEL ) {
-               if ( Trans->T_canceled==T_UNDEFINED )
-                       Trans->T_canceled = t_lookupOriginalT( hash_table ,
-                               Trans->uas.request );
-               if ( Trans->T_canceled==T_NULL )
-                       return 1;
-               /* put CANCEL transaction on wait only if canceled transaction already
-                   is in final status and there is nothing to cancel; */
-               if ( Trans->T_canceled->uas.status>=200)
-                       t_put_on_wait( Trans );
-       } else if (Trans->uas.status>=200)
-               t_put_on_wait( Trans );
-   return 1;
-}
-
-
-
-
 /*
   */
-int t_put_on_wait(  struct cell  *Trans  )
+void put_on_wait(  struct cell  *Trans  )
 {
-       unsigned int i;
-       //struct retrans_buff* rb;
-
-#ifndef WAIT
-       if (is_in_timer_list2( &(Trans->wait_tl)))
-       {
-               DBG("DEBUG: t_put_on_wait: already on wait\n");
-               return 1;
-       }
-#else
-       /* have some race conditons occured and we already
-         entered/passed the wait status previously?
-         if so, exit now
-       */
 
+#ifdef _XWAIT
        LOCK_WAIT(Trans);
        if (Trans->on_wait)
        {
                DBG("DEBUG: t_put_on_wait: already on wait\n");
                UNLOCK_WAIT(Trans);
-               return 1;
        } else {
                Trans->on_wait=1;
                UNLOCK_WAIT(Trans);
        }
 #endif
-
-       /* remove from  retranssmision  and  final response   list */
-       DBG("DEBUG: t_put_on_wait: stopping timers (FR and RETR)\n");
-       reset_retr_timers(hash_table,Trans) ;
-
-#ifdef SILENT_FR
-       if (Trans->nr_of_outgoings>1)
+#ifdef EXTRA_DEBUG
+       DBG("DEBUG: --- out on WAIT --- \n");
 #endif
-       {
-       /* cancel pending client transactions, if any */
-       for( i=0 ; i<Trans->nr_of_outgoings ; i++ )
-               if ( Trans->uac[i].rpl_received && Trans->uac[i].status<200 )
-                       t_build_and_send_CANCEL(Trans , i);
-       }
 
-       /* adds to Wait list*/
-       set_timer( hash_table, &(Trans->wait_tl), WT_TIMER_LIST );
-       return 1;
+
+       /* we put the transaction on wait timer; we do it only once
+          in transaction's timelife because putting it multiple-times
+          might result in a second instance of a wait timer to be
+          set after the first one fired; on expiration of the second
+          instance, the transaction would be re-deleted
+
+                       PROCESS1                PROCESS2                TIMER PROCESS
+               0. 200/INVITE rx;
+                  put_on_wait
+               1.                                      200/INVITE rx;
+               2.                                                                      WAIT fires; transaction
+                                                                                       about to be deleted
+               3.                                      avoid putting
+                                                       on WAIT again
+               4.                                                                      WAIT timer executed,
+                                                                                       transaction deleted
+       */
+       set_1timer( hash_table, &(Trans->wait_tl), WT_TIMER_LIST );
 }
 
 
 
+static int kill_transaction( struct cell *trans )
+{
+       char err_buffer[128];
+       int sip_err;
+       int reply_ret;
+       int ret;
+
+       /*  we reply statefuly and enter WAIT state since error might
+               have occured in middle of forking and we do not
+               want to put the forking burden on upstream client;
+               howver, it may fail too due to lack of memory */
+
+       ret=err2reason_phrase( ser_error, &sip_err,
+               err_buffer, sizeof(err_buffer), "TM" );
+       if (ret>0) {
+               reply_ret=t_reply( trans, trans->uas.request, 
+                       sip_err, err_buffer);
+               /* t_release_transaction( T ); */
+               return reply_ret;
+       } else {
+               LOG(L_ERR, "ERROR: kill_transaction: err2reason failed\n");
+               return -1;
+       }
+}
+
 
 
-void delete_cell( struct cell *p_cell )
+int t_relay_to( struct sip_msg  *p_msg , struct proxy_l *proxy,
+       int replicate)
 {
-#ifdef EXTRA_DEBUG
-       int i;
+       int ret;
+       int new_tran;
+       str *uri;
+       int reply_ret;
+       /* struct hdr_field *hdr; */
+       struct cell *t;
 
-       if (is_in_timer_list2(& p_cell->wait_tl )) {
-               LOG( L_ERR, "ERROR: transaction %p scheduled for deletion and"
-                       " still on WAIT\n", p_cell);
-               abort();
-       }
-       /*
-       if (is_in_timer_list2(& p_cell->outbound_response.retr_timer )) {
-               LOG( L_ERR, "ERROR: transaction %p scheduled for deletion and"
-                       " still on RETR (rep)\n",
-                       p_cell);
-               abort();
+       ret=0;
+
+       new_tran = t_newtran( p_msg );
+       
+
+       /* parsing error, memory alloc, whatever ... if via is bad
+          and we are forced to reply there, return with 0 (->break),
+          pass error status otherwise
+       */
+       if (new_tran<0) {
+               ret = (ser_error==E_BAD_VIA && reply_to_via) ? 0 : new_tran;
+               goto done;
        }
-       if (is_in_timer_list2(& p_cell->outbound_response.fr_timer )) {
-               LOG( L_ERR, "ERROR: transaction %p scheduled for deletion and"
-                       " still on FR (rep)\n", p_cell);
-               abort();
+       /* if that was a retransmission, return we are happily done */
+       if (new_tran==0) {
+               ret = 1;
+               goto done;
        }
-       for (i=0; i<p_cell->nr_of_outgoings; i++) {
-               if (is_in_timer_list2(& p_cell->outbound_request[i]->retr_timer)) {
-                       LOG( L_ERR, "ERROR: transaction %p scheduled for deletion and"
-                               " still on RETR (req %d)\n", p_cell, i);
-                       abort();
-               }
-               if (is_in_timer_list2(& p_cell->outbound_request[i]->fr_timer)) {
-                       LOG( L_ERR, "ERROR: transaction %p scheduled for deletion and"
-                               " still on FR (req %d)\n", p_cell, i);
-                       abort();
+
+       /* new transaction */
+
+       /* ACKs do not establish a transaction and are fwd-ed statelessly */
+       if ( p_msg->REQ_METHOD==METHOD_ACK) {
+               DBG( "SER: forwarding ACK  statelessly \n");
+               if (proxy==0) {
+                       uri=(p_msg->new_uri.s==0 || p_msg->new_uri.len==0) ?
+                               &p_msg->first_line.u.request.uri :
+                               &p_msg->new_uri;
+                       proxy=uri2proxy( uri );
+                       if (proxy==0) {
+                                       ret=E_BAD_ADDRESS;
+                                       goto done;
+                       }
+                       ret=forward_request( p_msg , proxy ) ;
+                       free_proxy( proxy );    
+                       free( proxy );
+               } else {
+                       ret=forward_request( p_msg , proxy ) ;
                }
+               goto done;
        }
+
+       /* if replication flag is set, mark the transaction as local
+          so that replies will not be relaied
        */
-       reset_retr_timers( hash_table, p_cell );
-#endif
-       /* still in use ... don't delete */
-       if ( T_IS_REFED(p_cell) ) {
-#ifdef EXTRA_DEBUG
-               if (T_REFCOUNTER(p_cell)>1) {
-                       DBG("DEBUG: while debugging with a single process, ref_count>1\n");
-                       DBG("DEBUG: transaction =%p\n", p_cell );
-                       abort();
+       t=get_t();
+       t->local=replicate;
+
+       /* INVITE processing might take long, partcularly because of DNS
+          look-ups -- let upstream know we're working on it */
+       if (p_msg->REQ_METHOD==METHOD_INVITE )
+       {
+               DBG( "SER: new INVITE\n");
+               if (!t_reply( t, p_msg , 100 ,
+                       "trying -- your call is important to us"))
+                               DBG("SER: ERROR: t_reply (100)\n");
+       } 
+
+       /* now go ahead and forward ... */
+       ret=t_forward_nonack(t, p_msg, proxy);
+       if (ret<=0) {
+               DBG( "SER:ERROR: t_forward \n");
+               reply_ret=kill_transaction( t );
+               if (reply_ret>0) {
+                       /* we have taken care of all -- do nothing in
+                       script */
+                       DBG("ERROR: generation of a stateful reply "
+                               "on error succeeded\n");
+                       ret=0;
+               }  else {
+                       DBG("ERROR: generation of a stateful reply "
+                               "on error failed\n");
                }
-#endif
-               DBG("DEBUG: delete_cell: t=%p post for delete (refbitmap %x,"
-                       " refcount %d)\n",p_cell,p_cell->ref_bitmap,T_REFCOUNTER(p_cell));
-               /* it's added to del list for future del */
-               set_timer( hash_table, &(p_cell->dele_tl), DELETE_LIST );
        } else {
-               DBG("DEBUG: delete transaction %p\n", p_cell );
-               free_cell( p_cell );
+               DBG( "SER: new transaction fwd'ed\n");
        }
-}
-
 
+done:
+       return ret;
+}
index 6cdcf81..b614650 100644 (file)
@@ -10,6 +10,7 @@
 #include <netinet/in.h>
 #include <netdb.h>
 
+#include "../../mem/shm_mem.h"
 #include "../../parser/msg_parser.h"
 #include "../../globals.h"
 #include "../../udp_server.h"
@@ -23,8 +24,9 @@
 #include "config.h"
 #include "lock.h"
 #include "timer.h"
-#include "sh_malloc.h"
 #include "sip_msg.h"
+#include "h_table.h"
+#include "ut.h"
 
 
 struct s_table;
@@ -32,115 +34,56 @@ struct timer;
 struct entry;
 struct cell;
 
-extern struct cell      *T;
-extern unsigned int     global_msg_id;
 extern struct s_table*  hash_table;
+extern int noisy_ctimer;
 
 
+#define LOCK_HASH(_h) lock(&(hash_table->entrys[(_h)].mutex))
+#define UNLOCK_HASH(_h) unlock(&(hash_table->entrys[(_h)].mutex))
 
-#define LOCK_REPLIES(_t) lock(&(_t)->reply_mutex )
-#define UNLOCK_REPLIES(_t) unlock(&(_t)->reply_mutex )
+#ifdef _OBSOLETED
 #define LOCK_ACK(_t) lock(&(_t)->ack_mutex )
 #define UNLOCK_ACK(_t) unlock(&(_t)->ack_mutex )
-#define LOCK_WAIT(_t) lock(&(_t)->wait_mutex )
-#define UNLOCK_WAIT(_t) unlock(&(_t)->wait_mutex )
+#endif
 
+#ifdef _XWAIT
+       #define LOCK_WAIT(_t) lock(&(_t)->wait_mutex )
+       #define UNLOCK_WAIT(_t) unlock(&(_t)->wait_mutex )
+#else
+       #define LOCK_WAIT(_t)
+       #define UNLOCK_WAIT(_t)
+#endif
 
 /* send a private buffer: utilize a retransmission structure
    but take a separate buffer not refered by it; healthy
    for reducing time spend in REPLIES locks
 */
 
-inline static int send_pr_buffer( struct retr_buf *rb,
-       void *buf, int len, char *function, int line )
-{
-       if (buf && len && rb )
-               return udp_send( rb->send_sock, buf, 
-                       len, &rb->to,  sizeof(union sockaddr_union) ) ;
-       else {
-               LOG(L_CRIT, "ERROR: sending an empty buffer from %s (%d)\n",
-                       function, line );
-               return -1;
-       }
-}
+int send_pr_buffer( struct retr_buf *rb,
+       void *buf, int len, char *function, int line );
 
+/* send a buffer -- 'PR' means private, i.e., it is assumed noone
+   else can affect the buffer during sending time
+*/
 #define SEND_PR_BUFFER(_rb,_bf,_le ) \
        send_pr_buffer( (_rb), (_bf), (_le),  __FUNCTION__, __LINE__ )
 
-/*
-#define SEND_PR_BUFFER(_rb,_bf,_le ) \
-       ( ((_bf) && (_le) && (_bf)) ? \
-       udp_send( (_bf), (_le), &((_rb)->to), sizeof(union sockaddr_union) ) : \
-       log_send_error( __FUNCTION__, __LINE__ ) )
-*/
-
-/* just for understanding of authors of the following macros, who did not
-   include 'PR' in macro names though they use 'PR' macro: PR stands for
-   PRIVATE and indicates usage of memory buffers in PRIVATE memory space,
-   where -- as opposed to SHARED memory space -- no concurrent memory
-   access can occur and thus no locking is needed ! -jiri
-*/
-#define SEND_ACK_BUFFER( _rb ) \
-       SEND_PR_BUFFER( (_rb) , (_rb)->ack , (_rb)->ack_len )
-
-#define SEND_CANCEL_BUFFER( _rb ) \
-       SEND_PR_BUFFER( (_rb) , (_rb)->cancel , (_rb)->cancel_len )
-
 #define SEND_BUFFER( _rb ) \
        SEND_PR_BUFFER( (_rb) , (_rb)->buffer , (_rb)->buffer_len )
 
 
-/*
-  macros for reference bitmap (lock-less process non-exclusive ownership) 
-*/
-#define T_IS_REFED(_T_cell) ((_T_cell)->ref_bitmap)
-#define T_REFCOUNTER(_T_cell) \
-       ( { int _i=0; \
-               process_bm_t _b=(_T_cell)->ref_bitmap; \
-               while (_b) { \
-                       if ( (_b) & 1 ) _i++; \
-                       (_b) >>= 1; \
-               } ;\
-               (_i); \
-        } )
-               
-
-#ifdef EXTRA_DEBUG
-#define T_IS_REFED_BYSELF(_T_cell) ((_T_cell)->ref_bitmap & process_bit)
-#      define DBG_REF(_action, _t) DBG("DEBUG: XXXXX %s (%s:%d): T=%p , ref (bm=%x, cnt=%d)\n",\
-                       (_action), __FUNCTION__, __LINE__, (_t),(_t)->ref_bitmap, T_REFCOUNTER(_t));
-#      define T_UNREF(_T_cell) \
-       ( { \
-               DBG_REF("unref", (_T_cell)); \
-               if (!T_IS_REFED_BYSELF(_T_cell)) { \
-                       DBG("ERROR: unrefering unrefered transaction %p from %s , %s : %d\n", \
-                               (_T_cell), __FUNCTION__, __FILE__, __LINE__ ); \
-                       abort(); \
-               } \
-               (_T_cell)->ref_bitmap &= ~process_bit; \
-       } )
-
-#      define T_REF(_T_cell) \
-       ( { \
-               DBG_REF("ref", (_T_cell));       \
-               if (T_IS_REFED_BYSELF(_T_cell)) { \
-                       DBG("ERROR: refering already refered transaction %p from %s,%s :"\
-                               " %d\n",(_T_cell), __FUNCTION__, __FILE__, __LINE__ ); \
-                       abort(); \
-               } \
-               (_T_cell)->ref_bitmap |= process_bit; \
-       } )
-#else
-#      define T_UNREF(_T_cell) ({ (_T_cell)->ref_bitmap &= ~process_bit; })
-#      define T_REF(_T_cell) ({ (_T_cell)->ref_bitmap |= process_bit; })
-#endif
-
-
-
-/*
-enum addifnew_status { AIN_ERROR, AIN_RETR, AIN_NEW, AIN_NEWACK,
-       AIN_OLDACK, AIN_RTRACK } ;
-*/
+#define UNREF_UNSAFE(_T_cell) ({  (_T_cell)->ref_count--; })
+#define UNREF(_T_cell) ({ \
+       LOCK_HASH( (_T_cell)->hash_index ); \
+       UNREF_UNSAFE(_T_cell); \
+       UNLOCK_HASH( (_T_cell)->hash_index ); })
+#define REF_UNSAFE(_T_cell) ({  (_T_cell)->ref_count++; })
+#define REF(_T_cell) ({ \
+       LOCK_HASH( (_T_cell)->hash_index ); \
+       REF_UNSAFE(_T_cell); \
+       UNLOCK_HASH( (_T_cell)->hash_index ); })
+#define INIT_REF_UNSAFE(_T_cell) (_T_cell)->ref_count=1
+#define IS_REFFED_UNSAFE(_T_cell) ((_T_cell)->ref_count!=0)
 
 
 int   tm_startup();
@@ -154,225 +97,30 @@ void tm_shutdown();
 int  t_add_transaction( struct sip_msg* p_msg  );
 
 
-
-
-/* function returns:
- *      -1 - transaction wasn't found
- *       1 - transaction found
- */
-int t_check( struct sip_msg* , int *branch , int* is_cancel);
-
-
-
-
-/* Forwards the inbound request to a given IP and port.  Returns:
- *       1 - forward successfull
- *      -1 - error during forward
- */
-/* v6; -jiri
-int t_forward( struct sip_msg* p_msg , unsigned int dst_ip ,
-                                                                               unsigned int dst_port);
-*/
-
-
-
-
-/* Forwards the inbound request to dest. from via.  Returns:
- *       1 - forward successfull
- *      -1 - error during forward
- */
-/* v6; -jiri
-int t_forward_uri( struct sip_msg* p_msg  );
-*/
-
-
-
-
-/* This function is called whenever a reply for our module is received;
- * we need to register this function on module initialization;
- * Returns :   0 - core router stops
- *             1 - core router relay statelessly
- */
-int t_on_reply( struct sip_msg  *p_msg ) ;
-
-
-
-
-/* This function is called whenever a request for our module is received;
- * we need to register this function on module initialization;
- * Returns :   0 - core router stops
- *             1 - core router relay statelessly
- */
-int t_on_request_received( struct sip_msg  *p_msg , unsigned int ip, unsigned int port) ;
-
-
-
-
-/* This function is called whenever a request for our module is received;
- * we need to register this function on module initialization;
- * Returns :   0 - core router stops
- *             1 - core router relay statelessly
- */
-int t_on_request_received_uri( struct sip_msg  *p_msg ) ;
-
-
-
-
 /* returns 1 if everything was OK or -1 for error
  */
-int t_release_transaction( struct sip_msg* );
-
-
-
-
-/* Retransmits the last sent inbound reply.
- * Returns  -1 - error
- *           1 - OK
- */
-int t_retransmit_reply( /* struct sip_msg * */  );
-
+int t_release_transaction( struct cell *trans );
 
 
-
-/* Force a new response into inbound response buffer.
- * returns 1 if everything was OK or -1 for erro
- */
-int t_send_reply( struct sip_msg * , unsigned int , char *  , unsigned int);
-
-
-
-
-/* releases T-context */
-int t_unref( /* struct sip_msg* p_msg */ );
-
-
-
-/* v6; -jiri
-int t_forward_nonack( struct sip_msg* p_msg , unsigned int dest_ip_param ,
-       unsigned int dest_port_param );
-int t_forward_ack( struct sip_msg* p_msg , unsigned int dest_ip_param ,
-       unsigned int dest_port_param );
-*/
-int t_forward_nonack( struct sip_msg* p_msg, struct proxy_l * p );
-int t_forward_ack( struct sip_msg* p_msg );
-
-
-int forward_serial_branch(struct cell* Trans,int branch);
-struct cell* t_lookupOriginalT(  struct s_table* hash_table,
-       struct sip_msg* p_msg );
-int t_reply_matching( struct sip_msg* , int* ,  int* );
-int t_store_incoming_reply( struct cell* , unsigned int , struct sip_msg* );
-int t_lookup_request( struct sip_msg* p_msg , int leave_new_locked );
-int t_all_final( struct cell * );
-int t_build_and_send_ACK( struct cell *Trans , unsigned int brach ,
-       struct sip_msg* rpl);
-int t_should_relay_response( struct cell *Trans, int new_code, int branch,
-       int *should_store );
-int t_update_timers_after_sending_reply( struct retr_buf *rb );
+/* int forward_serial_branch(struct cell* Trans,int branch); */
 int t_put_on_wait(  struct cell  *Trans  );
-int relay_lowest_reply_upstream( struct cell *Trans , struct sip_msg *p_msg );
-int add_branch_label( struct cell *Trans, struct sip_msg *p_msg , int branch );
 int get_ip_and_port_from_uri( str* uri , unsigned int *param_ip,
        unsigned int *param_port);
-int t_build_and_send_CANCEL(struct cell *Trans, unsigned int branch);
-char *build_ack( struct sip_msg* rpl, struct cell *trans, int branch ,
-       int *ret_len);
 
-int t_addifnew( struct sip_msg* p_msg );
 
 void timer_routine(unsigned int, void*);
 
+int t_newtran( struct sip_msg* p_msg );
 
+void put_on_wait(  struct cell  *Trans  );
 
+void start_retr( struct retr_buf *rb );
 
-inline int static attach_ack(  struct cell *t, int branch,
-                                                                       char *ack, int ack_len )
-{
-       LOCK_ACK( t );
-       if (t->uac[branch].request.ack) {
-               UNLOCK_ACK(t);
-               shm_free( ack );
-               LOG(L_WARN, "attach_ack: Warning: ACK already sent out\n");
-               return 0;
-       }
-       t->uac[branch].request.ack = ack;
-       t->uac[branch].request.ack_len = ack_len;
-       UNLOCK_ACK( t );
-       return 1;
-}
-
-
-
-
-/* remove from timer list */
-static inline void reset_timer( struct s_table *hash_table,
-                                                                                                       struct timer_link* tl )
-{
-       /* lock(timer_group_lock[ tl->tg ]); */
-       /* hack to work arround this timer group thing*/
-       lock(hash_table->timers[timer_group[tl->tg]].mutex);
-       remove_timer_unsafe( tl );
-       unlock(hash_table->timers[timer_group[tl->tg]].mutex);
-       /*unlock(timer_group_lock[ tl->tg ]);*/
-}
-
-
-
-
-/* determine timer length and put on a correct timer list */
-static inline void set_timer( struct s_table *hash_table,
-                                                       struct timer_link *new_tl, enum lists list_id )
-{
-       unsigned int timeout;
-       struct timer* list;
+void cleanup_localcancel_timers( struct cell *t );
 
+int t_relay_to( struct sip_msg  *p_msg ,
+       struct proxy_l *proxy, int replicate ) ;
 
-       if (list_id<FR_TIMER_LIST || list_id>=NR_OF_TIMER_LISTS) {
-               LOG(L_CRIT, "ERROR: set_timer: unkown list: %d\n", list_id);
-#ifdef EXTRA_DEBUG
-               abort();
-#endif
-               return;
-       }
-       timeout = timer_id2timeout[ list_id ];
-       list= &(hash_table->timers[ list_id ]);
-
-       lock(list->mutex);
-       /* make sure I'm not already on a list */
-       remove_timer_unsafe( new_tl );
-       add_timer_unsafe( list, new_tl, get_ticks()+timeout);
-       unlock(list->mutex);
-}
-
-
-
-
-static inline void reset_retr_timers( struct s_table *h_table,
-                                                                                                       struct cell *p_cell )
-{
-       int ijk;
-
-       /* lock the first timer list of the FR group -- all other
-          lists share the same lock*/
-       lock(hash_table->timers[RT_T1_TO_1].mutex);
-       remove_timer_unsafe( & p_cell->uas.response.retr_timer );
-       for( ijk=0 ; ijk<(p_cell)->nr_of_outgoings ; ijk++ )  {
-               remove_timer_unsafe( & p_cell->uac[ijk].request.retr_timer );
-       }
-       unlock(hash_table->timers[RT_T1_TO_1].mutex);
-
-       lock(hash_table->timers[FR_TIMER_LIST].mutex);
-       remove_timer_unsafe( & p_cell->uas.response.fr_timer );
-       for( ijk=0 ; ijk<(p_cell)->nr_of_outgoings ; ijk++ )  {
-               remove_timer_unsafe( & p_cell->uac[ijk].request.fr_timer );
-       }
-       unlock(hash_table->timers[FR_TIMER_LIST].mutex);
-       DBG("DEBUG:stop_RETR_and_FR_timers : timers stopped\n");
-}
-
-void delete_cell( struct cell *p_cell );
-
-int t_newtran( struct sip_msg* p_msg );
 
 #endif
 
index f0de93f..0701224 100644 (file)
@@ -8,13 +8,20 @@
 #include "../../parser/parser_f.h"
 #include "../../ut.h"
 #include "../../timer.h"
-#include "hash_func.h"
+#include "../../hash_func.h"
+#include "../../globals.h"
+#include "../../dset.h"
 #include "t_funcs.h"
-#include "t_fork.h"
-
 #include "t_hooks.h"
+#include "t_msgbuilder.h"
+#include "ut.h"
+#include "t_cancel.h"
+#include "t_lookup.h"
+#include "t_fwd.h"
+#include "fix_lumps.h"
 
 
+#ifdef _OBSOLETED
 #define shm_free_lump( _lmp) \
        do{\
                if ((_lmp)) {\
                        shm_free((_lmp));\
                }\
        }while(0);
+#endif
 
-
-
-/* function returns:
- *       1 - forward successfull
- *      -1 - error during forward
- */
-int t_forward_nonack( struct sip_msg* p_msg , 
-                                         struct proxy_l * p )
-/* v6; -jiri                                                                   unsigned int dest_ip_param ,
-                                                                                               unsigned int dest_port_param )
-*/
+char *print_uac_request( struct cell *t, struct sip_msg *i_req,
+       int branch, str *uri, int *len, struct socket_info *send_sock )
 {
-       int          branch;
-       unsigned int len;
-       char         *buf, *shbuf;
-       struct cell  *T_source = T;
-       struct lump  *a,*b,*b1,*c;
-       str          backup_uri;
-       int                      ret;
-       struct socket_info* send_sock;
-       union sockaddr_union to;
-
+       char *buf, *shbuf;
 
-       /* default error value == -1; be more specific if you want to */
-       ret=-1;
-       buf    = 0;
-       shbuf  = 0;
-       backup_uri.s = p_msg->new_uri.s;
-       backup_uri.len = p_msg->new_uri.len;
+       shbuf=0;
 
+       /* ... we calculate branch ... */       
+       if (!t_setbranch( t, i_req, branch )) {
+               LOG(L_ERR, "ERROR: print_uac_request: branch computation failed\n");
+               goto error01;
+       }
 
+       /* ... update uri ... */
+       i_req->new_uri=*uri;
 
-       /* are we forwarding for the first time? */
-       if ( T->uac[0].request.buffer )
-       {       /* rewriting a request should really not happen -- retransmission
-                  does not rewrite, whereas a new request should be written
-                  somewhere else */
-               LOG( L_CRIT, "ERROR: t_forward_nonack: attempt to rewrite"
-                       " request structures\n");
-               ser_error=E_BUG;
-               return 0;
-       }
+       /* ... give apps a chance to change things ... */
+       callback_event( TMCB_REQUEST_OUT, t, i_req, -i_req->REQ_METHOD);
 
-       /* v6; -jiri ... copynpasted from forward_request */
-       /* if error try next ip address if possible */
-       if (p->ok==0){
-               if (p->host.h_addr_list[p->addr_idx+1])
-                       p->addr_idx++;
-               else p->addr_idx=0;
-               p->ok=1;
+       /* ... and build it now */
+       buf=build_req_buf_from_sip_req( i_req, len, send_sock );
+       if (!buf) {
+               LOG(L_ERR, "ERROR: print_uac_request: no pkg_mem\n"); 
+               ser_error=E_OUT_OF_MEM;
+               goto error01;
        }
-       hostent2su(&to, &p->host, p->addr_idx,
-       (p->port)?htons(p->port):htons(SIP_PORT));
-
-       /* sets as first fork the default outgoing */
-       nr_forks++;
-       /* v6; -jiri
-       t_forks[0].ip = dest_ip_param;
-       t_forks[0].port = dest_port_param;
+       /*      clean Via's we created now -- they would accumulate for
+               other branches  and for  shmem i_req they would mix up
+               shmem with pkg_mem
        */
-       t_forks[0].to=to;
-       t_forks[0].uri.len = p_msg->new_uri.len;
-       t_forks[0].uri.s =  p_msg->new_uri.s;
-       t_forks[0].free_flag = 0;
-
-       DBG("DEBUG: t_forward_nonack: first time forwarding\n");
-       /* special case : CANCEL */
-       if ( p_msg->REQ_METHOD==METHOD_CANCEL  )
-       {
-               DBG("DEBUG: t_forward_nonack: it's CANCEL\n");
-               /* find original cancelled transaction; if found, use its
-                  next-hops; otherwise use those passed by script */
-               if ( T->T_canceled==T_UNDEFINED )
-                       T->T_canceled = t_lookupOriginalT( hash_table , p_msg );
-               /* if found */
-               if ( T->T_canceled!=T_NULL )
-               {
-                       for(nr_forks=0;nr_forks<T->T_canceled->nr_of_outgoings;nr_forks++)
-                       {
-                               /* if in 1xx status, send to the same destination */
-                               if ( (T->T_canceled->uac[nr_forks].status/100)==1 )
-                               {
-                                       DBG("DEBUG: t_forward_nonack: branch %d not finalize"
-                                               ": sending CANCEL for it\n",nr_forks);
-                                       /* v6; -jiri
-                                       t_forks[nr_forks].ip =
-                                         T->T_canceled->uac[nr_forks].request.to.sin_addr.s_addr; 
-                                       t_forks[nr_forks].port =
-                                         T->T_canceled->uac[nr_forks].request.to.sin_port;
-                                       */
-                                       t_forks[nr_forks].to = T->T_canceled->uac[nr_forks].request.to;
-
-                                       t_forks[nr_forks].uri.len =
-                                         T->T_canceled->uac[nr_forks].uri.len;
-                                       t_forks[nr_forks].uri.s =
-                                         T->T_canceled->uac[nr_forks].uri.s;
-                                       t_forks[nr_forks].free_flag = 0;
-                               }else{
-                                       /* transaction exists, but nothing to cancel */
-                                       DBG("DEBUG: t_forward_nonack: branch %d finalized"
-                                               ": no CANCEL sent here\n",nr_forks);
-                                       /* -v6; -jiri
-                                       t_forks[nr_forks].ip = 0;
-                                       */
-                                       t_forks[nr_forks].inactive= 1;
-                               }
-                       }
-#ifdef USE_SYNONIM
-                       T_source = T->T_canceled;
-                       T->label  = T->T_canceled->label;
-#endif
-               } else { /* transaction doesnot exists  */
-                       DBG("DEBUG: t_forward_nonack: canceled request not found! "
-                       "nothing to CANCEL\n");
+#ifdef OBSOLETED
+       if (branch) for(b=i_req->add_rm,b1=0;b;b1=b,b=b->next)
+               if (b->type==HDR_VIA) {
+                       for(a=b->before;a;)
+                               {c=a->before;free_lump(a);pkg_free(a);a=c;}
+                       for(a=b->after;a;)
+                               {c=a->after;free_lump(a);pkg_free(a);a=c;}
+                       if (b1) b1->next = b->next;
+                       else i_req->add_rm = b->next;
+                       free_lump(b);pkg_free(b);
                }
-       }/* end special case CANCEL*/
+#endif
+       free_via_lump(&i_req->add_rm);
+
+       shbuf=(char *)shm_malloc(*len);
+       if (!shbuf) {
+               ser_error=E_OUT_OF_MEM;
+               LOG(L_ERR, "ERROR: print_uac_request: no shmem\n");
+               goto error02;
+       }
+       memcpy( shbuf, buf, *len );
+
+error02:
+       pkg_free( buf );
+error01:
+       return shbuf;
+}
+
+/* introduce a new uac to transaction; returns its branch id (>=0)
+   or error (<0); it doesn't send a message yet -- a reply to it
+   might itnerfere with the processes of adding multiple branches
+*/
+int add_uac( struct cell *t, struct sip_msg *request, str *uri, 
+       struct proxy_l *proxy )
+{
+
+       int ret;
+       short temp_proxy;
+       union sockaddr_union to;
+       unsigned short branch;
+       struct socket_info* send_sock;
+       char *shbuf;
+       unsigned int len;
 
-#ifndef USE_SYNONIM
-       branch=0;
-       if ( nr_forks && add_branch_label( T_source, T->uas.request , branch )==-1)
+       branch=t->nr_of_outgoings;
+       if (branch==MAX_BRANCHES) {
+               LOG(L_ERR, "ERROR: add_uac: maximum number of branches exceeded\n");
+               ret=E_CFG;
                goto error;
-#endif
+       }
 
-       DBG("DEBUG: t_forward_nonack: nr_forks=%d\n",nr_forks);
-       for(branch=0;branch<nr_forks;branch++)
-       {
-               /* -v6; -jiri if (!t_forks[branch].ip) */
-               if (t_forks[branch].inactive)
-                       goto end_loop;
-               DBG("DEBUG: t_forward_nonack: branch = %d\n",branch);
-               /*generates branch param*/
-               if ( add_branch_label( T_source, p_msg , branch )==-1)
-                       goto error;
-               /* remove all the HDR_VIA type lumps */
-               if (branch)
-                       for(b=p_msg->add_rm,b1=0;b;b1=b,b=b->next)
-                               if (b->type==HDR_VIA)
-                               {
-                                       for(a=b->before;a;)
-                                               {c=a->before;free_lump(a);pkg_free(a);a=c;}
-                                       for(a=b->after;a;)
-                                               {c=a->after;free_lump(a);pkg_free(a);a=c;}
-                                       if (b1) b1->next = b->next;
-                                               else p_msg->add_rm = b->next;
-                                       free_lump(b);pkg_free(b);
-                               }
-               /* updates the new uri*/
-               p_msg->new_uri.s = t_forks[branch].uri.s;
-               p_msg->new_uri.len = t_forks[branch].uri.len;
-
-               T->uac[branch].request.to = t_forks[branch].to;
-               send_sock=get_send_socket( & T->uac[branch].request.to );
-               if (send_sock==0) {
-                       LOG(L_ERR, "ERROR: t_forward_nonack: can't fwd to af %d "
-                               "no corresponding listening socket\n", 
-                               T->uac[branch].request.to.s.sa_family);
-                       ser_error=E_NO_SOCKET;
-                       goto error;
-               }
-               T->uac[branch].request.send_sock=send_sock;
-               
-               callback_event( TMCB_REQUEST_OUT, T, p_msg );   
-               /* _test_insert_to_reply(p_msg, "Foo: Bar\r\n");*/
-               if ( !(buf = build_req_buf_from_sip_req  ( p_msg, &len, send_sock ))) {
-                       ser_error=ret=E_OUT_OF_MEM;
-                       goto error;
-               }
-               /* allocates a new retrans_buff for the outbound request */
-               DBG("DEBUG: t_forward_nonack: building outbound request"
-                       " for branch %d.\n",branch);
-               shbuf = (char *) shm_malloc( len );
-               if (!shbuf)
-               {
-                       LOG(L_ERR, "ERROR: t_forward_nonack: out of shmem buffer\n");
-                       ser_error=ret=E_OUT_OF_MEM;
-                       goto error;
-               }
-               T->uac[branch].request.buffer = shbuf;
-               T->uac[branch].request.buffer_len = len ;
-               memcpy( T->uac[branch].request.buffer , buf , len );
-               /* keeps a hooker to uri inside buffer*/
-               T->uac[branch].uri.s = T->uac[branch].request.buffer +
-                       (p_msg->first_line.u.request.uri.s - p_msg->buf);
-               T->uac[branch].uri.len=t_forks[branch].uri.s?(t_forks[branch].uri.len)
-                       :(p_msg->first_line.u.request.uri.len);
-               /* send the request */
-               /* v6; -jiri
-               T->uac[branch].request.to.sin_addr.s_addr = t_forks[branch].ip;
-               T->uac[branch].request.to.sin_port = t_forks[branch].port;
-               T->uac[branch].request.to.sin_family = AF_INET;
-               */
-               T->uac[branch].request.to = t_forks[branch].to;
-               p->tx++;
-               p->tx_bytes+=len;
-               if (SEND_BUFFER( &(T->uac[branch].request) )==-1) {
-                       p->errors++;
-                       p->ok=0;
-                       ser_error=ret=E_SEND;
+       /* check existing buffer -- rewriting should never occur */
+       if (t->uac[branch].request.buffer) {
+               LOG(L_CRIT, "ERROR: add_uac: buffer rewrite attempt\n");
+               ret=ser_error=E_BUG;
+               goto error;
+       }
+
+       /* check DNS resolution */
+       if (proxy) temp_proxy=0; else {
+               proxy=uri2proxy( uri );
+               if (proxy==0)  {
+                       ret=E_BAD_ADDRESS;
                        goto error;
                }
-               /* should have p->errors++; p->ok=0; on error here... */
-
-
-               pkg_free( buf ) ;
-               buf=NULL;
-
-               DBG("DEBUG: t_forward_nonack: starting timers (retrans and FR) %d\n",
-                       get_ticks() );
-               /*sets and starts the FINAL RESPONSE timer */
-               set_timer( hash_table, &(T->uac[branch].request.fr_timer),
-               /*p_msg->REQ_METHOD==METHOD_INVITE?FR_INV_TIMER_LIST:FR_TIMER_LIST);*/
-                       FR_TIMER_LIST ); 
-               /* sets and starts the RETRANS timer */
-               T->uac[branch].request.retr_list = RT_T1_TO_1;
-               set_timer( hash_table, &(T->uac[branch].request.retr_timer),
-                       RT_T1_TO_1 );
-               end_loop:
-               T->nr_of_outgoings++ ;
-               DBG("DEBUG: branch %d done; outgoing uri=|%.*s|\n",branch,
-                       T->uac[branch].uri.len,T->uac[branch].uri.s);
+               temp_proxy=1;
        }
 
-       /* if we have a branch spec. for NO_RESPONSE_RECEIVED, we have to 
-       move it immediatly after the last parallel branch */
-       /* v6; -jiri 
-       if (t_forks[NO_RPL_BRANCH].ip && T->nr_of_outgoings!=NO_RPL_BRANCH ) */
-       if (!t_forks[NO_RPL_BRANCH].inactive && T->nr_of_outgoings!=NO_RPL_BRANCH )
-       {
-               branch = T->nr_of_outgoings;
-               /* v6; -jiri
-               T->uac[branch].request.to.sin_addr.s_addr = t_forks[NO_RPL_BRANCH].ip;
-               T->uac[branch].request.to.sin_port = t_forks[NO_RPL_BRANCH].port;
-               */
-               T->uac[branch].request.to = t_forks[NO_RPL_BRANCH].to;
-
-               T->uac[branch].uri.s = t_forks[NO_RPL_BRANCH].uri.s;
-               T->uac[branch].uri.len = t_forks[NO_RPL_BRANCH].uri.len;
+       if (proxy->ok==0) {
+               if (proxy->host.h_addr_list[proxy->addr_idx+1])
+                       proxy->addr_idx++;
+               else proxy->addr_idx=0;
+               proxy->ok=1;
        }
-       p_msg->new_uri.s = backup_uri.s;
-       p_msg->new_uri.len = backup_uri.len;
-       t_clear_forks();
-       return 1;
 
+       hostent2su( &to, &proxy->host, proxy->addr_idx, 
+               proxy->port ? htons(proxy->port):htons(SIP_PORT));
+
+       send_sock=get_send_socket( &to );
+       if (send_sock==0) {
+               LOG(L_ERR, "ERROR: add_uac: can't fwd to af %d "
+                       " (no corresponding listening socket)\n",
+                       to.s.sa_family );
+               ret=ser_error=E_NO_SOCKET;
+               goto error01;
+       }
+
+       /* now message printing starts ... */
+       shbuf=print_uac_request( t, request, branch, uri, 
+               &len, send_sock );
+       if (!shbuf) {
+               ret=ser_error=E_OUT_OF_MEM;
+               goto error01;
+       }
+
+       /* things went well, move ahead and install new buffer! */
+       t->uac[branch].request.to=to;
+       t->uac[branch].request.send_sock=send_sock;
+       t->uac[branch].request.buffer=shbuf;
+       t->uac[branch].request.buffer_len=len;
+       t->uac[branch].uri.s=t->uac[branch].request.buffer+
+               request->first_line.u.request.method.len+1;
+       t->uac[branch].uri.len=uri->len;
+       t->nr_of_outgoings++;
+
+       /* update stats */
+       proxy->tx++;
+       proxy->tx_bytes+=len;
+
+       /* done! */     
+       ret=branch;
+               
+error01:
+       if (temp_proxy) {
+               free_proxy( proxy );
+               free( proxy );
+       }
 error:
-       if (shbuf) shm_free(shbuf);
-       T->uac[branch].request.buffer=NULL;
-       if (buf) pkg_free( buf );
-       p_msg->new_uri.s = backup_uri.s;
-       p_msg->new_uri.len = backup_uri.len;
-       t_clear_forks();
        return ret;
 }
 
-
-int forward_serial_branch(struct cell* Trans,int branch)
+int e2e_cancel_branch( struct sip_msg *cancel_msg, struct cell *t_cancel, 
+       struct cell *t_invite, int branch )
 {
-       struct sip_msg*  p_msg = Trans->uas.request;
-       struct lump      *a, *b, *b1, *c;
-       unsigned int     len;
-       char             *buf=0, *shbuf=0;
-       str              backup_uri;
-       union sockaddr_union *to;
-       struct socket_info* send_sock;
+       int ret;
+       char *shbuf;
+       int len;
 
-       backup_uri.s = p_msg->new_uri.s;
-       backup_uri.len = p_msg->new_uri.len;
-
-       /*generates branch param*/
-       if ( add_branch_label( Trans, p_msg , branch )==-1)
+       if (t_cancel->uac[branch].request.buffer) {
+               LOG(L_CRIT, "ERROR: e2e_cancel_branch: buffer rewrite attempt\n");
+               ret=ser_error=E_BUG;
                goto error;
-       /* remove all the HDR_VIA type lumps - they are in SHM memory!!! */
-       for(b=p_msg->add_rm,b1=0;b;b1=b,b=b->next)
-               if (b->type==HDR_VIA)
-               {
-                       for(a=b->before;a;)
-                               {c=a->before;shm_free_lump(a);a=c;}
-                       for(a=b->after;a;)
-                               {c=a->after;shm_free_lump(a);a=c;}
-                       if (b1) b1->next = b->next;
-                               else p_msg->add_rm = b->next;
-                       shm_free_lump(b);
-               }
+       }       
 
-       LOG(L_ERR,"DEBUG: t_forward_serial_branch: building req for branch"
-               "%d; uri=|%.*s|.\n", branch, Trans->uac[branch].uri.len,
-               Trans->uac[branch].uri.s);
-       /* updates the new uri*/
-       p_msg->new_uri.s = Trans->uac[branch].uri.s;
-       p_msg->new_uri.len = Trans->uac[branch].uri.len;
+       /* note -- there is a gap in proxy stats -- we don't update 
+          proxy stats with CANCEL (proxy->ok, proxy->tx, etc.)
+       */
 
-       to=&Trans->uac[branch].request.to;
-       send_sock=get_send_socket(to);
-       if (send_sock==0) {
-               LOG(L_ERR, "ERROR: t_forward_nonack: can't fwd to af %d "
-               "no corresponding listening socket\n", to->s.sa_family );
-               ser_error=E_NO_SOCKET;
+       /* print */
+       shbuf=print_uac_request( t_cancel, cancel_msg, branch, 
+               &t_invite->uac[branch].uri, &len, 
+               t_invite->uac[branch].request.send_sock);
+       if (!shbuf) {
+               LOG(L_ERR, "ERROR: e2e_cancel_branch: printing e2e cancel failed\n");
+               ret=ser_error=E_OUT_OF_MEM;
                goto error;
        }
-       if ( !(buf = build_req_buf_from_sip_req  ( p_msg, &len, send_sock )))
-               goto error;
-       shm_free(Trans->uac[branch].uri.s);
+       
+       /* install buffer */
+       t_cancel->uac[branch].request.to=t_invite->uac[branch].request.to;
+       t_cancel->uac[branch].request.send_sock=t_invite->uac[branch].request.send_sock;
+       t_cancel->uac[branch].request.buffer=shbuf;
+       t_cancel->uac[branch].request.buffer_len=len;
+       t_cancel->uac[branch].uri.s=t_cancel->uac[branch].request.buffer+
+               cancel_msg->first_line.u.request.method.len+1;
+       t_cancel->uac[branch].uri.len=t_invite->uac[branch].uri.len;
+       
 
-       /* allocates a new retrans_buff for the outbound request */
-       shbuf = (char *) shm_malloc( len );
-       if (!shbuf)
-       {
-               LOG(L_ERR, "ERROR: t_forward_serial_branch: out of shmem buffer\n");
-               goto error;
-       }
-       Trans->uac[branch].request.buffer = shbuf;
-       Trans->uac[branch].request.buffer_len = len ;
-       memcpy( Trans->uac[branch].request.buffer , buf , len );
-       /* keeps a hooker to uri inside buffer*/
-       Trans->uac[branch].uri.s = Trans->uac[branch].request.buffer +
-               (p_msg->first_line.u.request.uri.s - p_msg->buf);
-       Trans->uac[branch].uri.len=p_msg->new_uri.len?(p_msg->new_uri.len)
-               :(p_msg->first_line.u.request.uri.len);
-       Trans->nr_of_outgoings++ ;
-       /* send the request */
-       /* -v6; -jiri Trans->uac[branch].request.to.sin_family = AF_INET; */
-       SEND_BUFFER( &(T->uac[branch].request) );
-
-       pkg_free( buf ) ;
-       buf=NULL;
-
-       DBG("DEBUG: t_forward_serial_branch:starting timers (retrans and FR) %d\n",
-               get_ticks() );
-       /*sets and starts the FINAL RESPONSE timer */
-       set_timer( hash_table, &(T->uac[branch].request.fr_timer), 
-                       FR_TIMER_LIST ); 
-                       /* p_msg->REQ_METHOD==METHOD_INVITE ? FR_INV_TIMER_LIST : FR_TIMER_LIST ); */
-       /* sets and starts the RETRANS timer */
-       T->uac[branch].request.retr_list = RT_T1_TO_1;
-       set_timer( hash_table, &(T->uac[branch].request.retr_timer), RT_T1_TO_1 );
-
-       p_msg->new_uri.s = backup_uri.s;
-       p_msg->new_uri.len = backup_uri.len;
-
-       for(b=p_msg->add_rm,b1=0;b;b1=b,b=b->next)
-               if (b->type==HDR_VIA)
-               {
-                       for(a=b->before;a;)
-                               {c=a->before;free_lump(a);pkg_free(a);a=c;}
-                       for(a=b->after;a;)
-                               {c=a->after;free_lump(a);pkg_free(a);a=c;}
-                       if (b1) b1->next = b->next;
-                               else p_msg->add_rm = b->next;
-                       free_lump(b);pkg_free(b);
-               }
+       /* success */
+       ret=1;
 
-       return 1;
 
 error:
-       if (shbuf) shm_free(shbuf);
-       T->uac[branch].request.buffer=NULL;
-       if (buf) pkg_free( buf );
-       p_msg->new_uri.s = backup_uri.s;
-       p_msg->new_uri.len = backup_uri.len;
-       return -1;
+       return ret;
 }
 
+void e2e_cancel( struct sip_msg *cancel_msg, 
+       struct cell *t_cancel, struct cell *t_invite )
+{
+       branch_bm_t cancel_bm;
+       int i;
+       int lowest_error;
+       str backup_uri;
+       int ret;
+
+       cancel_bm=0;
+       lowest_error=0;
+
+       backup_uri=cancel_msg->new_uri;
+       /* determine which branches to cancel ... */
+       which_cancel( t_invite, &cancel_bm );
+       /* ... and install CANCEL UACs */
+       for (i=0; i<t_invite->nr_of_outgoings; i++)
+               if (cancel_bm & (1<<i)) {
+                       ret=e2e_cancel_branch(cancel_msg, t_cancel, t_invite, i);
+                       if (ret<0) cancel_bm &= ~(1<<i);
+                       if (ret<lowest_error) lowest_error=ret;
+               }
+       t_cancel->nr_of_outgoings=t_invite->nr_of_outgoings;
+       t_cancel->label=t_invite->label;
+       cancel_msg->new_uri=backup_uri;
+
+       /* send them out */
+       for (i=0; i<t_cancel->nr_of_outgoings; i++) {
+               if (cancel_bm & (1<<i)) {
+                       if (SEND_BUFFER( &t_cancel->uac[i].request)==-1) {
+                               LOG(L_ERR, "ERROR: e2e_cancel: send failed\n");
+                       }
+                       start_retr( &t_cancel->uac[i].request );
+               }
+       }
+
 
+       /* if error occured, let it know upstream (final reply
+          will also move the transaction on wait state
+       */
+       if (lowest_error<0) {
+               LOG(L_ERR, "ERROR: cancel error\n");
+               t_reply( t_cancel, cancel_msg, 500, "cancel error");
+       /* if there are pending branches, let upstream know we
+          are working on it
+       */
+       } else if (cancel_bm) {
+               DBG("DEBUG: e2e_cancel: e2e cancel proceeding\n");
+               t_reply( t_cancel, cancel_msg, 100, "trying to cancel" );
+       /* if the transaction exists, but there is no more pending
+          branch, tell usptream we're done
+       */
+       } else {
+               DBG("DEBUG: e2e_cancel: e2e cancel -- no more pending branches\n");
+               t_reply( t_cancel, cancel_msg, 200, "ok, no more pending branches" );
+       }
+}
 
+
+/* function returns:
+ *       1 - forward successfull
+ *      -1 - error during forward
+ */
+int t_forward_nonack( struct cell *t, struct sip_msg* p_msg , 
+       struct proxy_l * proxy )
+{
+       str          backup_uri;
+       int branch_ret, lowest_ret;
+       str current_uri;
+       branch_bm_t     added_branches;
+       int first_branch;
+       int i;
+       struct cell *t_invite;
+
+       /* make -Wall happy */
+       current_uri.s=0;
+
+       t->kr|=REQ_FWDED;
+
+       if (p_msg->REQ_METHOD==METHOD_CANCEL) {
+               t_invite=t_lookupOriginalT( hash_table, p_msg );
+               if (t_invite!=T_NULL) {
+                       e2e_cancel( p_msg, t, t_invite );
+                       UNREF(t_invite);
+                       return 1;
+               }
+       }
+
+       /* backup current uri ... add_uac changes it */
+       backup_uri = p_msg->new_uri;
+       /* if no more specific error code is known, use this */
+       lowest_ret=E_BUG;
+       /* branches added */
+       added_branches=0;
+       /* branch to begin with */
+       first_branch=t->nr_of_outgoings;
+
+       /* on first-time forwarding, use current uri, later only what
+          is in additional branches (which may be continuously refilled
+       */
+       if (first_branch==0) {
+               branch_ret=add_uac( t, p_msg, 
+                       p_msg->new_uri.s ? &p_msg->new_uri :  
+                               &p_msg->first_line.u.request.uri,
+                       proxy );
+               if (branch_ret>=0) 
+                       added_branches |= 1<<branch_ret;
+               else
+                       lowest_ret=branch_ret;
+       }
+
+       init_branch_iterator(p_msg);
+       while((current_uri.s=next_branch( &current_uri.len))) {
+               branch_ret=add_uac( t, p_msg, &current_uri, proxy );
+               /* pick some of the errors in case things go wrong;
+                  note that picking lowest error is just as good as
+                  any other algorithm which picks any other negative
+                  branch result */
+               if (branch_ret>=0) 
+                       added_branches |= 1<<branch_ret;
+               else
+                       lowest_ret=branch_ret;
+       }
+       /* consume processed branches */
+       clear_branches();
+
+       /* restore original URI */
+       p_msg->new_uri=backup_uri;
+
+       /* don't forget to clear all branches processed so far */
+
+       /* things went wrong ... no new branch has been fwd-ed at all */
+       if (added_branches==0)
+               return lowest_ret;
+
+       /* if someone set on_negative, store in in T-context */
+       t->on_negative=get_on_negative();
+
+       /* send them out now */
+       for (i=first_branch; i<t->nr_of_outgoings; i++) {
+               if (added_branches & (1<<i)) {
+                       if (SEND_BUFFER( &t->uac[i].request)==-1) {
+                               LOG(L_ERR, "ERROR: add_uac: sending request failed\n");
+                               if (proxy) { proxy->errors++; proxy->ok=0; }
+                       }
+                       start_retr( &t->uac[i].request );
+               }
+       }
+       return 1;
+}      
+
+int t_replicate(struct sip_msg *p_msg,  struct proxy_l *proxy )
+{
+       /* this is a quite horrible hack -- we just take the message
+          as is, including Route-s, Record-route-s, and Vias ,
+          forward it downstream and prevent replies received
+          from relaying by setting the replication/local_trans bit;
+
+               nevertheless, it should be good enough for the primary
+               customer of this function, REGISTER replication
+
+               if we want later to make it thoroughly, we need to
+               introduce delete lumps for all the header fields above
+       */
+       return t_relay_to(p_msg, proxy, 1 /* replicate */);
+}
diff --git a/modules/tm/t_fwd.h b/modules/tm/t_fwd.h
new file mode 100644 (file)
index 0000000..6b2dd02
--- /dev/null
@@ -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
+
+
index 0dc34d5..0bfebd2 100644 (file)
@@ -2,16 +2,18 @@
  * $Id$
  */
 
-
+#include "stdlib.h"
+#include "../../dprint.h"
+#include "../../error.h"
 #include "t_hooks.h"
 
-static struct tm_callback_s* callback_array[ TMCB_END ] = { NULL, NULL } ;
+static struct tm_callback_s* callback_array[ TMCB_END ] = { 0, 0 } ;
 static int callback_id=0;
 
 /* register a callback function 'f' of type 'cbt'; will be called
    back whenever the event 'cbt' occurs in transaction module
 */
-int register_tmcb( tmcb_type cbt, transaction_cb f )
+int register_tmcb( tmcb_type cbt, transaction_cb f, void *param )
 {
        struct tm_callback_s *cbs;
 
@@ -30,19 +32,19 @@ int register_tmcb( tmcb_type cbt, transaction_cb f )
        cbs->id=callback_id;
        cbs->callback=f;
        cbs->next=callback_array[ cbt ];
+       cbs->param=param;
        callback_array[ cbt ]=cbs;
 
        return callback_id;
 }
 
 void callback_event( tmcb_type cbt , struct cell *trans,
-       struct sip_msg *msg )
+       struct sip_msg *msg, int code )
 {
        struct tm_callback_s *cbs;
 
-       DBG("DBG: callback type %d entered\n", cbt );
        for (cbs=callback_array[ cbt ]; cbs; cbs=cbs->next)  {
-               DBG("DBG: callback id %d entered\n", cbs->id );
-               cbs->callback( trans, msg );
+               DBG("DBG: callback type %d, id %d entered\n", cbt, cbs->id );
+               cbs->callback( trans, msg, code, cbs->param );
        }
 }
index 61a006f..d4f1dd8 100644 (file)
 #ifndef _HOOKS_H
 #define _HOOKS_H
 
-#include "h_table.h"
-#include "t_funcs.h"
+struct sip_msg;
+struct cell;
 
 typedef enum { TMCB_REPLY,  TMCB_E2EACK, TMCB_REPLY_IN, 
-       TMCB_REQUEST_OUT, TMCB_END } tmcb_type;
+       TMCB_REQUEST_OUT, TMCB_LOCAL_COMPLETED, TMCB_ON_NEGATIVE,
+       TMCB_END } tmcb_type;
 
 /* 
        TMCB_REPLY      -  a reply has been sent out
-                                 (no chance to change anything; still good enough
-                                 for reporting callbacks do not need to change anything
-                                 and better do not utilize TMCB_REPLY_IN, which would
-                                 resulting in the callback spending its time in
-                                 REPLY_LOCK unnecessarily)
-       TMCB_E2EACK - presumably, an end2end ACK was received and
-                                 is about to be processed statelessly (note that
-                                 we cannot reliably determine that an e2e really
-                                 does belong to a transaction -- it is about guessing;
-                                 the reason is e2e ACKs are stand-alone transactions
-                                 which may have r-uri/via different from INVITE and
-                                 take a different path; however, with RR, chances are
-                                 good to match (though transaction matching won't work
-                                 with spirals -- as all transaction instances "match"
-                                 the request and only the first will be taken)
+         no chance to change anything in the message; 
+         still good enough for many uses, such as accounting
+         of completed transactions; note well that the message
+         passed to the callback may also have value FAKED_REPLY,
+         i.e., refering to it will segfault
        TMCB_REPLY_IN - a reply was received and is about to be forwarded;
-       TMCB_REQUEST_OUT - a request was received and is about to be fwd-ed
+         compared to TMCB_REPLY, it is a very internal callback and
+         you should use it with lot of caution
+         - it allows you to change the message (called before printing
+           the relayed message)
+         - it is called from a reply lock -- it is mroe dangerous and
+           anything you do makes the processes spend more time in
+           the lock, decreasing overall performance
+         - is is called only for replies >100, <300 (final replies
+           might be cached on forking, stored in shmem -- then, there
+               is no more easy way to change messages)
+         - as it is called before printing and forwarding, there is
+           no guarantee the message will be sent out -- either can
+           fail
+
+               Note: none of the reply callbacks will be evoked if
+               "silent C timer" hits. Silent C timer is a feature which
+               prevents cancellation of a call in progress by a server
+               in the middle, when C timer expires. On one side, 
+               INVITE transactional state cannot be kept for ever,
+               on the other side you want to allow long ringing 
+               uninterrupted by a proxy server. The silent_c feature
+               -- if circumstances allow -- simply discards transaction
+               state when C timer hits, the transaction can then complete
+               statelessly. Then, however, the stateful callback will
+               NOT be called. If you do not wish this behaviour (e.g.,
+               for sake of transaction accounting, in which you do
+               not desire users to wait until silent C hits and
+               eventually complete an unaccounted transaction), turn
+               silent C off either globaly (TM option "noisy_ctimer"
+               set to 1) or for a specific transaction (you can for
+               example set the transaction member "noisy_timer"
+               from request callback.)
+
+       TMCB_E2EACK - presumably, an end2end ACK was received and
+               is about to be processed statelessly; you better don't
+           use this callback as there is no reliable way to match
+           an e2e ACK to an INVITE transaction, we just try it for
+           those, who believe they can't live without knowing about
+           the ACK; There are more reasons why the e2e ACK callback
+           is never triggered: 1) the e2eACK does not pass the server
+           at all 2) the e2e ACK does not match an INVITE transaction
+               because its r-uri or via is different
+       TMCB_REQUEST_OUT - a request was received and is about to be fwd-ed;
+               it is not called on retransmissions; it is called prior to
+               printing the relayed message, i.e., changes to it can
+               be done
+       TMCB_LOCAL_COMPLETED - a local transaction completed; note that
+           the callback parameter may be FAKED_REPLY
+       TMCB_MISSED -- transaction was replied with a negative value;
+               called from within a REPLY_LOCK, message may be FAKED_REPLY
+       TMCB_ON_NEGATIVE -- called whenever a transaction is about to complete
+           with a negative result; it's a great time to introduce a new
+           uac (serial forking) or change the reply code; be cautions
+           though -- it is called from within REPLY_LOCK and careless
+           usage of the callback can easily result in a deadlock; msg
+           is always 0 (callback refers to whole transaction and not
+           to individual message), code is the currently lowest status
+           code
        TMCB_END        - just a bumper
+
+       see the 'acc' module for an example of callback usage
+
+       note that callbacks MUST be installed before forking
+    (callback lists do not live in shmem and have no access
+       protection)
 */
 
-typedef void (transaction_cb) ( struct cell* t, struct sip_msg* msg );
+typedef void (transaction_cb) ( struct cell* t, struct sip_msg* msg, 
+       int code, void *param );
 
 struct tm_callback_s {
        int id;
        transaction_cb* callback;
        struct tm_callback_s* next;
+       void *param;
 };
 
 
 extern struct tm_callback_s* callback_array[ TMCB_END ];
 
-typedef int (*register_tmcb_f)(tmcb_type cbt, transaction_cb f);
+typedef int (*register_tmcb_f)(tmcb_type cbt, transaction_cb f, void *param);
 
-int register_tmcb( tmcb_type cbt, transaction_cb f );
+int register_tmcb( tmcb_type cbt, transaction_cb f, void *param );
 void callback_event( tmcb_type cbt, struct cell *trans,
-       struct sip_msg *msg );
+       struct sip_msg *msg, int code );
 
 #endif
index e8826be..38bfc97 100644 (file)
@@ -21,7 +21,7 @@
  *
  * The branch parameter is formed as follows:
  * SYNONYMS  on: hash.synonym.branch
- * SYNONYMS off: md5.hash.branch
+ * SYNONYMS off: hash.md5.branch
  *
  * -jiri
  *
 #include "../../parser/parser_f.h"
 #include "../../ut.h"
 #include "../../timer.h"
-#include "hash_func.h"
+#include "../../hash_func.h"
+#include "../../globals.h"
+#include "../../forward.h"
 #include "t_funcs.h"
 #include "config.h"
 #include "sip_msg.h"
 #include "t_hooks.h"
+#include "t_lookup.h"
 
 
 #define EQ_LEN(_hf) (t_msg->_hf->body.len==p_msg->_hf->body.len)
         (t_msg->via1->bsize-(t_msg->_via->name.s-(t_msg->_via->hdr.s+t_msg->_via->hdr.len)))\
        )==0 )
 
+#define HF_LEN(_hf) ((_hf)->body.s+(_hf)->body.len-(_hf)->name.s)
+
 /* presumably matching transaction for an e2e ACK */
 static struct cell *t_ack;
 
+/* this is a global variable which keeps pointer to
+   transaction currently processed by a process; it it
+   set by t_lookup_request or t_reply_matching; don't
+   dare to change it anywhere else as it would
+   break ref_counting
+*/
+
+#ifdef _OBSOLETED
+struct cell      *T;
+#endif
+
+static struct cell *T;
+
+/* number of currently processed message; good to know
+   to be able to doublecheck whether we are still working
+   on a current transaction or a new message arrived;
+   don't even think of changing it
+*/
+unsigned int     global_msg_id;
+
+struct cell *get_t() { return T; }
+void init_t() {global_msg_id=0; T=T_UNDEFINED;}
+
 
 /* function returns:
  *      negative - transaction wasn't found
@@ -87,7 +115,9 @@ int t_lookup_request( struct sip_msg* p_msg , int leave_new_locked )
        }
 
        /* start searching into the table */
-       p_msg->hash_index=hash( p_msg->callid->body , get_cseq(p_msg)->number ) ;
+       if (!p_msg->hash_index)
+               p_msg->hash_index=hash( p_msg->callid->body , 
+                       get_cseq(p_msg)->number ) ;
        isACK = p_msg->REQ_METHOD==METHOD_ACK;
        DBG("t_lookup_request: start searching: hash=%d, isACK=%d\n",
                p_msg->hash_index,isACK);
@@ -96,7 +126,7 @@ int t_lookup_request( struct sip_msg* p_msg , int leave_new_locked )
        ret=-1;
 
        /* lock the hole entry*/
-       lock(&(hash_table->entrys[p_msg->hash_index].mutex));
+       LOCK_HASH(p_msg->hash_index);
 
        /* all the transactions from the entry are compared */
        for ( p_cell = hash_table->entrys[p_msg->hash_index].first_cell;
@@ -138,7 +168,7 @@ int t_lookup_request( struct sip_msg* p_msg , int leave_new_locked )
                        if (get_to(t_msg)->uri.len!=get_to(p_msg)->uri.len)
                                continue;
                        /* ... its to-tag compared to reply's tag */
-                       if (p_cell->uas.tag->len!=get_to(p_msg)->tag_value.len)
+                       if (p_cell->uas.to_tag.len!=get_to(p_msg)->tag_value.len)
                                continue;
 
                        /* we first skip r-uri and Via and proceed with
@@ -151,8 +181,9 @@ int t_lookup_request( struct sip_msg* p_msg , int leave_new_locked )
                        if (!EQ_STR(from)) continue;
                        if (memcmp(get_to(t_msg)->uri.s, get_to(p_msg)->uri.s,
                                get_to(t_msg)->uri.len)!=0) continue;
-                       if (memcmp(p_cell->uas.tag->s, get_to(p_msg)->tag_value.s,
-                               p_cell->uas.tag->len)!=0) continue;
+                       if (p_cell->uas.to_tag.len!=0 /* to-tags empty */
+                               || memcmp(p_cell->uas.to_tag.s, get_to(p_msg)->tag_value.s,
+                               p_cell->uas.to_tag.len)!=0) continue;
        
                        /* ok, now only r-uri or via can mismatch; they must match
                           for non-2xx; if it is a 2xx, we don't try to match
@@ -187,177 +218,21 @@ int t_lookup_request( struct sip_msg* p_msg , int leave_new_locked )
 
        /* no transaction found */
        T = 0;
-       if (!leave_new_locked)
-               unlock(&(hash_table->entrys[p_msg->hash_index].mutex));
-       DBG("DEBUG: t_lookup_request: no transaction found\n");
-       return ret;
-
-found:
-       T=p_cell;
-       T_REF( T );
-       DBG("DEBUG: t_lookup_request: transaction found (T=%p , ref=%x)\n",
-               T,T->ref_bitmap);
-       unlock(&(hash_table->entrys[p_msg->hash_index].mutex));
-       return 1;
-}
-
-
-#ifdef __YOU_DONT_WANT_TO_DO_THIS
-
-/* function returns:
- *      -1 - transaction wasn't found
- *       1  - transaction found
- */
-int t_lookup_request( struct sip_msg* p_msg , int leave_new_locked )
-{
-       struct cell         *p_cell;
-       struct cell         *tmp_cell;
-       unsigned int       isACK;
-       struct sip_msg  *t_msg;
-
-       /* parse all*/
-       if (check_transaction_quadruple(p_msg)==0)
-       {
-               LOG(L_ERR, "ERROR: TM module: t_lookup_request: too few headers\n");
-               T=0;
-               /* stop processing */
-               return 0;
+       if (!leave_new_locked) {
+               UNLOCK_HASH(p_msg->hash_index);
        }
-
-       /* start searching into the table */
-       p_msg->hash_index=hash( p_msg->callid->body , get_cseq(p_msg)->number ) ;
-       isACK = p_msg->REQ_METHOD==METHOD_ACK;
-       DBG("t_lookup_request: start searching: hash=%d, isACK=%d\n",
-               p_msg->hash_index,isACK);
-
-       /* lock the hole entry*/
-       lock(&(hash_table->entrys[p_msg->hash_index].mutex));
-
-       /* all the transactions from the entry are compared */
-       p_cell   = hash_table->entrys[p_msg->hash_index].first_cell;
-       tmp_cell = 0;
-       while( p_cell )
-       {
-               t_msg = p_cell->uas.request;
-
-               /* is it the wanted transaction ? */
-               if ( !isACK )
-               { /* is not an ACK request */
-                       /* first only the length are checked */
-                       if ( /*callied*/EQ_LEN(callid) && /*cseq*/EQ_LEN(cseq)
-                       && /*req URI*/EQ_REQ_URI_LEN && /*VIA*/EQ_VIA_LEN(via1)
-                       && /*from*/EQ_LEN(from) && /*to*/EQ_LEN(to)  )
-                               /* so far the lengths are the same
-                               -> let's check the contents */
-                               if ( /*callid*/EQ_STR(callid) && /*cseq*/EQ_STR(cseq)
-                               && /*req URI*/EQ_REQ_URI_STR && /*VIA*/EQ_VIA_STR(via1)
-                               && /*from*/EQ_STR(from) && /*to*/EQ_STR(to) )
-                                       { /* WE FOUND THE GOLDEN EGG !!!! */
-                                               goto found;
-                                       }
-               }
-               else
-               { /* it's a ACK request*/
-                       /* first only the length are checked */
-                       /* use shortcut; -jiri
-                       if ( t_msg->first_line.u.request.method_value==METHOD_INVITE */
-                       if (t_msg->REQ_METHOD==METHOD_INVITE
-                       /* && (fprintf(stderr,"------Method name OK->testing callid len...\n")) */
-                       && /*callid length*/ EQ_LEN(callid)
-                       /* && (fprintf(stderr,"------CallID OK -> testing cseq nr len\n")) */
-                       && get_cseq(t_msg)->number.len==get_cseq(p_msg)->number.len
-                       /* && (fprintf(stderr,"------Cseq nr OK -> testing from len\n")) */
-                       && /*from length*/ EQ_LEN(from)
-                       /* && (fprintf(stderr,"------from OK -> testing To uri len\n")) */
-                       && /*to uri*/get_to(t_msg)->uri.len==get_to(p_msg)->uri.len
-                       /* && (fprintf(stderr,"------To uri OK -> testing To tag len\n")) */
-                       && /*to tag*/p_cell->uas.tag->len==get_to(p_msg)->tag_value.len
-                       /* && (fprintf(stderr,"------To tag OK -> testing uri len\n")) */
-
-                       /* in ACKs to 200, r-uri and Via may be different than in
-                          original INVITE; we still try to match the transaction
-                          so that we can retransmit an ACK on resent 200 -- different
-                          from SIP spec which kills transaction state after INVITE-200
-                          and considers 200-ACK a new transaction which just happens
-                          to have the same CSeq. -jiri
-                       */
-
-                       && /*req URI*/(p_cell->uas.status==200 || EQ_REQ_URI_LEN )
-                       /* && (fprintf(stderr,"------uri OK -> testing via len\n")) */
-                       && /*VIA*/(p_cell->uas.status==200 || EQ_VIA_LEN(via1)) )
-                               /* so far the lengths are the same
-                               -> let's check the contents */
-                               if ( /* fprintf(stderr,"------callid |%.*s| |%.*s|\n",
-                                       p_msg->callid->body.len,p_msg->callid->body.s,
-                                       t_msg->callid->body.len,t_msg->callid->body.s)
-                               && */ /*callid*/!memcmp( t_msg->callid->body.s,
-                                       p_msg->callid->body.s,p_msg->callid->body.len)
-                               /* && fprintf(stderr,"------cseq |%.*s| |%.*s|\n",
-                                       get_cseq(p_msg)->number.len,get_cseq(p_msg)->number.s,
-                                       get_cseq(t_msg)->number.len,get_cseq(t_msg)->number.s) */
-                               && /*cseq nr*/!memcmp(get_cseq(t_msg)->number.s,
-                                       get_cseq(p_msg)->number.s,get_cseq(p_msg)->number.len)
-                               /* &&  fprintf(stderr,"------from |%.*s| |%.*s|\n",
-                                       p_msg->from->body.len, translate_pointer(p_msg->orig,
-                                               p_msg->buf,p_msg->from->body.s),
-                                       t_msg->from->body.len,t_msg->from->body.s) */
-                               && /*from*/EQ_STR(from)
-                               /* && fprintf(stderr,"------to uri |%.*s| |%.*s|\n",
-                                       get_to(p_msg)->uri.len,get_to(p_msg)->uri.s,
-                                       get_to(t_msg)->uri.len,get_to(t_msg)->uri.s) */
-                               && /*to uri*/!memcmp(get_to(t_msg)->uri.s,
-                                       get_to(p_msg)->uri.s,get_to(t_msg)->uri.len)
-                               /* && fprintf(stderr,"------to tag |%.*s| |%.*s|\n",
-                    get_to(p_msg)->tag_value.len,get_to(p_msg)->tag_value.s,
-                    p_cell->uas.tag->len, p_cell->uas.tag->s) */
-                               && /*to tag*/!memcmp(p_cell->uas.tag->s,
-                                       get_to(p_msg)->tag_value.s,p_cell->uas.tag->len)
-                               /* && fprintf(stderr,"------URI %d |%.*s| |%.*s|\n",
-                                       p_cell->uas.status,p_msg->first_line.u.request.uri.len,
-                                       translate_pointer(p_msg->orig, p_msg->buf,
-                                               p_msg->first_line.u.request.uri.s),
-                                       t_msg->first_line.u.request.uri.len,
-                                       t_msg->first_line.u.request.uri.s) */
-                               && /*req URI*/(p_cell->uas.status==200 || EQ_REQ_URI_STR)
-                               /* && fprintf(stderr,"------VIA %d |%.*s| |%.*s|\n",
-                                       p_cell->uas.status, 
-                                       (p_msg->via1->bsize-(p_msg->via1->name.s-
-                                               (p_msg->via1->hdr.s+p_msg->via1->hdr.len))),
-                                       translate_pointer(p_msg->orig,p_msg->buf,
-                                               p_msg->via1->name.s),
-                    (t_msg->via1->bsize-(t_msg->via1->name.s-
-                                               (t_msg->via1->hdr.s+t_msg->via1->hdr.len))),
-                                       t_msg->via1->name.s) */
-                               && /*VAI*/(p_cell->uas.status==200 ||EQ_VIA_STR(via1)) )
-                                       { /* WE FOUND THE GOLDEN EGG !!!! */
-                                               goto found;
-                                       }
-               }
-               /* next transaction */
-               tmp_cell = p_cell;
-               p_cell = p_cell->next_cell;
-       } /* synonym loop */
-
-       /* no transaction found */
-       T = 0;
-       if (!leave_new_locked)
-               unlock(&(hash_table->entrys[p_msg->hash_index].mutex));
        DBG("DEBUG: t_lookup_request: no transaction found\n");
-       /* DON'T FORGET TO REMOVE IT!!!!! bogdan */
-       //if (isACK) assert(0);
-       return -1;
+       return ret;
 
 found:
        T=p_cell;
-       T_REF( T );
-       DBG("DEBUG: t_lookup_request: transaction found (T=%p , ref=%x)\n",
-               T,T->ref_bitmap);
-       unlock(&(hash_table->entrys[p_msg->hash_index].mutex));
+       REF_UNSAFE( T );
+       T->kr|=REQ_EXIST;
+       UNLOCK_HASH( p_msg->hash_index );
+       DBG("DEBUG: t_lookup_request: transaction found (T=%p)\n",T);
        return 1;
 }
 
-#endif
-
 
 
 /* function returns:
@@ -365,99 +240,67 @@ found:
  *       T - transaction found
  */
 struct cell* t_lookupOriginalT(  struct s_table* hash_table ,
-                                                                                                       struct sip_msg* p_msg )
+       struct sip_msg* p_msg )
 {
        struct cell     *p_cell;
-       struct cell     *tmp_cell;
-       unsigned int     hash_index=0;
-       struct sip_msg  *t_msg=0;
+       unsigned int     hash_index;
+       struct sip_msg  *t_msg;
 
 
-       /* start searching into the table */
+       /* start searching in the table */
        hash_index = p_msg->hash_index;
+       LOCK_HASH(hash_index);
        DBG("DEBUG: t_lookupOriginalT: searching on hash entry %d\n",hash_index );
 
        /* all the transactions from the entry are compared */
-       p_cell   = hash_table->entrys[hash_index].first_cell;
-       tmp_cell = 0;
-       while( p_cell )
+       for (p_cell=hash_table->entrys[hash_index].first_cell;
+               p_cell; p_cell = p_cell->next_cell )
        {
                t_msg = p_cell->uas.request;
 
-               /* is it the wanted transaction ? */
-               /* first only the length are checked */
-               if ( /* fprintf(stderr,"starting\n")  && */ p_cell->uas.request->REQ_METHOD!=METHOD_CANCEL
-                       /* && fprintf(stderr,"checking callid length....\n") */
-                       && /*callid length*/ EQ_LEN(callid)
-                       /* && fprintf(stderr,"OK. checking cseg nr len....\n")   */
-                       && get_cseq(t_msg)->number.len==get_cseq(p_msg)->number.len
-                       /* && fprintf(stderr,"OK. checking REQ_URI len.... \n") */
-                       && EQ_REQ_URI_LEN
-                       /* && fprintf(stderr,"OK. checking VIA %d %d....\n",
-                               (p_msg->via1->bsize-(p_msg->via1->name.s-
-                                       (p_msg->via1->hdr.s+p_msg->via1->hdr.len))),
-                               (t_msg->via1->bsize-(t_msg->via1->name.s-
-                                       (t_msg->via1->hdr.s+t_msg->via1->hdr.len)))) */
-            /* && fprintf(stderr,"OK. VIA |%.*s| |%.*s|\n",
-                (p_msg->via1->bsize-(p_msg->via1->name.s-
-                     (p_msg->via1->hdr.s+p_msg->via1->hdr.len))),
-                translate_pointer(p_msg->orig,p_msg->buf,
-                       p_msg->via1->name.s),
-                (t_msg->via1->bsize-(t_msg->via1->name.s-
-                      (t_msg->via1->hdr.s+t_msg->via1->hdr.len))),
-                t_msg->via1->name.s) */
-                       && EQ_VIA_LEN(via1) 
-                       /* && fprintf(stderr,"OK. checking FROM len... \n") */
-                       && EQ_LEN(from)
-                       /* && fprintf(stderr,"OK. checking TO len... \n") */
-                       && EQ_LEN(to)
-                       /* && fprintf(stderr,"OK\n") */ )
-                               /* so far the lengths are the same
-                                let's check the contents */
-                               if (
-                       /* fprintf(stderr,"checking callid |%.*s| |%.*s|\n",
-                       p_msg->callid->body.len, translate_pointer(p_msg->orig,
-                               p_msg->buf,p_msg->callid->body.s),
-                       t_msg->callid->body.len,t_msg->callid->body.s) 
-                                       && *//*callid*/ EQ_STR(callid)
-                                       /* && fprintf(stderr,"OK. cseq nr |%.*s| |%.*s|\n",
-                                               get_cseq(p_msg)->number.len,get_cseq(p_msg)->number.s,
-                                               get_cseq(t_msg)->number.len,get_cseq(t_msg)->number.s) */
-                                       && /*cseq_nr*/ !memcmp(get_cseq(t_msg)->number.s,
-                                               get_cseq(p_msg)->number.s,get_cseq(p_msg)->number.len)
-                       /* && fprintf(stderr,"OK. URI %d |%.*s| |%.*s|\n",
-                       p_cell->uas.status,p_msg->first_line.u.request.uri.len,
-                       translate_pointer(p_msg->orig, p_msg->buf,
-                               p_msg->first_line.u.request.uri.s),
-                       t_msg->first_line.u.request.uri.len,
-                       t_msg->first_line.u.request.uri.s) */
-                                       && EQ_REQ_URI_STR
-                       /* && fprintf(stderr,"OK. VIA |%.*s| |%.*s|\n",
-                           (p_msg->via1->bsize-(p_msg->via1->name.s-
-                           (p_msg->via1->hdr.s+p_msg->via1->hdr.len))),
-                           translate_pointer(p_msg->orig,p_msg->buf,
-                           p_msg->via1->name.s),
-                                       (t_msg->via1->bsize-(t_msg->via1->name.s-
-                           (t_msg->via1->hdr.s+t_msg->via1->hdr.len))),
-                                               t_msg->via1->name.s) */
-                                       && EQ_VIA_STR(via1)
-                       /* && fprintf(stderr,"OK. from |%.*s| |%.*s|\n",
-                        p_msg->from->body.len, translate_pointer(p_msg->orig,
-                            p_msg->buf,p_msg->from->body.s),
-                        t_msg->from->body.len,t_msg->from->body.s) */
-                                       && EQ_STR(from)
-                                       /* && fprintf(stderr,"OK\n") */ )
-                                       { /* WE FOUND THE GOLDEN EGG !!!! */
-                                               DBG("DEBUG: t_lookupOriginalT: canceled transaction"
-                                                       " found (%p)! \n",p_cell );
-                                               return p_cell;
-                                       }
-               /* next transaction */
-               tmp_cell = p_cell;
-               p_cell = p_cell->next_cell;
+               /* we don't cancel CANCELs ;-) */
+               if (p_cell->uas.request->REQ_METHOD==METHOD_CANCEL)
+                       continue;
+
+               /* check lengths now */ 
+               if (!EQ_LEN(callid))
+                       continue;
+               if (get_cseq(t_msg)->number.len!=get_cseq(p_msg)->number.len)
+                       continue;
+               if (!EQ_LEN(from))
+                       continue;
+               if (!EQ_LEN(to))
+                       continue;
+               if (!EQ_REQ_URI_LEN)
+                       continue;
+               if (!EQ_VIA_LEN(via1))
+                       continue;
+
+               /* check the content now */
+               if (!EQ_STR(callid))
+                       continue;
+               if (memcmp(get_cseq(t_msg)->number.s,
+                       get_cseq(p_msg)->number.s,get_cseq(p_msg)->number.len)!=0)
+                       continue;
+               if (!EQ_STR(from))
+                       continue;
+               if (!EQ_STR(to))
+                       continue;
+               if (!EQ_REQ_URI_STR)
+                       continue;
+               if (!EQ_VIA_STR(via1))
+                       continue;
+
+               /* found */
+               REF( p_cell );
+               UNLOCK_HASH(hash_index);
+               DBG("DEBUG: t_lookupOriginalT: canceled transaction"
+                       " found (%p)! \n",p_cell );
+               return p_cell;
        }
 
        /* no transaction found */
+       UNLOCK_HASH(hash_index);
        DBG("DEBUG: t_lookupOriginalT: no CANCEL maching found! \n" );
        return 0;
 }
@@ -468,8 +311,7 @@ struct cell* t_lookupOriginalT(  struct s_table* hash_table ,
 /* Returns 0 - nothing found
  *         1  - T found
  */
-int t_reply_matching( struct sip_msg *p_msg , int *p_branch ,
-                                                                                        int *local_cancel)
+int t_reply_matching( struct sip_msg *p_msg , int *p_branch )
 {
        struct cell*  p_cell;
        int hash_index   = 0;
@@ -478,13 +320,21 @@ int t_reply_matching( struct sip_msg *p_msg , int *p_branch ,
        char  *hashi, *branchi, *p, *n;
        int hashl, branchl;
        int scan_space;
-#ifndef USE_SYNONIM
+       str cseq_method;
+       str req_method;
+
        char *loopi;
        int loopl;
-#else
        char *syni;
        int synl;
-#endif
+       
+       short is_cancel;
+
+       /* make compiler warnnings happy */
+       loopi=0;
+       loopl=0;
+       syni=0;
+       synl=0;
 
        /* split the branch into pieces: loop_detection_check(ignored),
         hash_table_id, synonym_id, branch_id */
@@ -495,15 +345,6 @@ int t_reply_matching( struct sip_msg *p_msg , int *p_branch ,
        p=p_msg->via1->branch->value.s;
        scan_space=p_msg->via1->branch->value.len;
 
-#ifndef USE_SYNONIM
-       /* loop detection ... ignore */
-       n=eat_token2_end( p, p+scan_space, BRANCH_SEPARATOR );
-       loopl = n-p;
-       scan_space-= loopl;
-       if (n==p || scan_space<2 || *n!=BRANCH_SEPARATOR) goto nomatch2;
-       loopi=p;
-       p=n+1; scan_space--;
-#endif
 
        /* hash_id */
        n=eat_token2_end( p, p+scan_space, BRANCH_SEPARATOR);
@@ -513,15 +354,25 @@ int t_reply_matching( struct sip_msg *p_msg , int *p_branch ,
        hashi=p;
        p=n+1;scan_space--;
 
-#ifdef USE_SYNONIM
-       /* sequence id */
-       n=eat_token2_end( p, p+scan_space, BRANCH_SEPARATOR);
-       synl=n-p;
-       scan_space-=synl;
-       if (!synl || scan_space<2 || *n!=BRANCH_SEPARATOR) goto nomatch2;
-       syni=p;
-       p=n+1;scan_space--;
-#endif
+       if (!syn_branch) {
+               /* md5 value */
+               n=eat_token2_end( p, p+scan_space, BRANCH_SEPARATOR );
+               loopl = n-p;
+               scan_space-= loopl;
+               if (n==p || scan_space<2 || *n!=BRANCH_SEPARATOR) 
+                       goto nomatch2;
+               loopi=p;
+               p=n+1; scan_space--;
+       } else {
+               /* synonym id */
+               n=eat_token2_end( p, p+scan_space, BRANCH_SEPARATOR);
+               synl=n-p;
+               scan_space-=synl;
+               if (!synl || scan_space<2 || *n!=BRANCH_SEPARATOR) 
+                       goto nomatch2;
+               syni=p;
+               p=n+1;scan_space--;
+       }
 
        /* branch id  -  should exceed the scan_space */
        n=eat_token_end( p, p+scan_space );
@@ -530,13 +381,12 @@ int t_reply_matching( struct sip_msg *p_msg , int *p_branch ,
        branchi=p;
 
        /* sanity check */
-       if ((hash_index=reverse_hex2int(hashi, hashl))<0||hash_index>=TABLE_ENTRIES
-               || (branch_id=reverse_hex2int(branchi, branchl))<0||branch_id>=MAX_FORK
-#ifdef USE_SYNONIM
-               || (entry_label=reverse_hex2int(syni, synl))<0
-#else
-               || loopl!=MD5_LEN
-#endif
+       if ((hash_index=reverse_hex2int(hashi, hashl))<0
+               ||hash_index>=TABLE_ENTRIES
+               || (branch_id=reverse_hex2int(branchi, branchl))<0
+               ||branch_id>=MAX_BRANCHES
+               || (syn_branch ? (entry_label=reverse_hex2int(syni, synl))<0 
+                       : loopl!=MD5_LEN )
        ) {
                DBG("DEBUG: t_reply_matching: poor reply lables %d label %d "
                        "branch %d\n",hash_index, entry_label, branch_id );
@@ -551,51 +401,52 @@ int t_reply_matching( struct sip_msg *p_msg , int *p_branch ,
        /* search the hash table list at entry 'hash_index'; lock the
           entry first 
        */
-       lock(&(hash_table->entrys[hash_index].mutex));
+       cseq_method=get_cseq(p_msg)->method;
+       is_cancel=cseq_method.len==CANCEL_LEN 
+               && memcmp(cseq_method.s, CANCEL, CANCEL_LEN)==0;
+       LOCK_HASH(hash_index);
        for (p_cell = hash_table->entrys[hash_index].first_cell; p_cell; 
                p_cell=p_cell->next_cell) {
 
-               /* does method match ? */
-               if (get_cseq(p_msg)->method.len==
-                         get_cseq(p_cell->uas.request)->method.len 
-                       && get_cseq(p_msg)->method.s[0]==
-                         get_cseq(p_cell->uas.request)->method.s[0]) {
-                               *local_cancel=0;
-               /* or is it perhaps a CANCEL ? */
-               } else if ( p_cell->uas.request->REQ_METHOD==METHOD_INVITE 
-                       && get_cseq(p_msg)->method.len==CANCEL_LEN 
-                       && memcmp( get_cseq(p_msg)->method.s, CANCEL, CANCEL_LEN )==0 
-                       && p_cell->uac[branch_id].request.cancel!=NO_CANCEL 
-                       && p_cell->uac[branch_id].request.cancel!=EXTERNAL_CANCEL ) {
-                               *local_cancel=1;
-               } else { /* method mismatched */
-                       continue;
-               };
-               #ifdef USE_SYNONIM
-               if (p_cell->label != entry_label) 
-                       continue;
-               #else
-               if ( p_cell->uas.request->add_to_branch_len<MD5_LEN 
-                        || memcmp(p_cell->uas.request->add_to_branch_s,loopi,MD5_LEN)!=0)
+               /* first look if branch matches */
+
+               if (syn_branch) {
+                       if (p_cell->label != entry_label) 
                                continue;
-               #endif
+               } else {
+                       if ( memcmp(p_cell->md5, loopi,MD5_LEN)!=0)
+                                       continue;
+               }
+
                /* sanity check ... too high branch ? */
                if ( branch_id>=p_cell->nr_of_outgoings )
                        continue;
+
+               /* does method match ? (remember -- CANCELs have the same branch
+                  as cancelled transactions) */
+               req_method=p_cell->method;
+               if ( /* method match */
+                       ! ((cseq_method.len==req_method.len 
+                       && memcmp( cseq_method.s, req_method.s, cseq_method.len )==0)
+                       /* or it is a local cancel */
+                       || (is_cancel && p_cell->is_invite 
+                               && p_cell->uac[branch_id].local_cancel.buffer )))
+                       continue;
+
+
                /* we passed all disqualifying factors .... the transaction has been
                   matched !
                */
                T=p_cell;
                *p_branch = branch_id;
-               T_REF( T );
-               unlock(&(hash_table->entrys[hash_index].mutex));
-               DBG("DEBUG: t_reply_matching: reply matched (T=%p,ref=%x)!\n",
-                       T,T->ref_bitmap);
+               REF_UNSAFE( T );
+               UNLOCK_HASH(hash_index);
+               DBG("DEBUG: t_reply_matching: reply matched (T=%p)!\n",T);
                return 1;
        } /* for cycle */
 
        /* nothing found */
-       unlock(&(hash_table->entrys[hash_index].mutex));
+       UNLOCK_HASH(hash_index);
        DBG("DEBUG: t_reply_matching: no matching transaction exists\n");
 
 nomatch2:
@@ -612,10 +463,9 @@ nomatch2:
   * for current message exists;
   * it returns 1 if found, 0 if not found, -1 on error
   */
-int t_check( struct sip_msg* p_msg , int *param_branch, int *param_cancel)
+int t_check( struct sip_msg* p_msg , int *param_branch )
 {
        int local_branch;
-       int local_cancel;
 
        /* is T still up-to-date ? */
        DBG("DEBUG: t_check: msg id=%d global id=%d T start=%p\n", 
@@ -631,12 +481,29 @@ int t_check( struct sip_msg* p_msg , int *param_branch, int *param_cancel)
                                return -1;
                        t_lookup_request( p_msg , 0 /* unlock before returning */ );
                } else {
-                       if ( parse_headers(p_msg, HDR_VIA1|HDR_TO|HDR_CSEQ, 0 )==-1
-                       || !p_msg->via1 || !p_msg->to || !p_msg->cseq )
+                       /* we need Via for branch and Cseq method to distinguish
+                          replies with the same branch/cseqNr (CANCEL)
+                       */
+                       if ( parse_headers(p_msg, HDR_VIA1|HDR_CSEQ, 0 )==-1
+                       || !p_msg->via1 || !p_msg->cseq ) {
+                               LOG(L_ERR, "ERROR: reply cannot be parsed\n");
                                return -1;
+                       }
+
+                       /* if that is an INVITE, we will also need to-tag
+                          for later ACK matching
+                       */
+            if ( get_cseq(p_msg)->method.len==INVITE_LEN 
+                               && memcmp( get_cseq(p_msg)->method.s, INVITE, INVITE_LEN )==0 ) {
+                                       if (parse_headers(p_msg, HDR_TO, 0)==-1
+                                               || !p_msg->to)  {
+                                               LOG(L_ERR, "ERROR: INVITE reply cannot be parsed\n");
+                                               return -1;
+                                       }
+                       }
+
                        t_reply_matching( p_msg ,
-                               ((param_branch!=0)?(param_branch):(&local_branch)),
-                               ((param_cancel!=0)?(param_cancel):(&local_cancel)));
+                               param_branch!=0?param_branch:&local_branch );
 
                }
 #ifdef EXTRA_DEBUG
@@ -658,59 +525,39 @@ int t_check( struct sip_msg* p_msg , int *param_branch, int *param_cancel)
        return ((T)?1:0) ;
 }
 
-
-
-/* append appropriate branch labels for fast reply-transaction matching
-   to outgoing requests
-*/
-int add_branch_label( struct cell *trans, struct sip_msg *p_msg, int branch )
+int init_rb( struct retr_buf *rb, struct sip_msg *msg )
 {
-       char *begin;
-       int size, orig_size;
-
-       /* this is actually a hack made by Bogdan; I wanted to have a structure
-          to which anybody can append some branch stuff which may be utilizied
-          during reply processing; Bogdan ignored that and resets it all the
-          time to construct multiple branches for multiple via's during
-          forking (otherwise, the next branch would be now appended to
-          previous branch)
-
-          keywords: HACK
-       */
-       
-       p_msg->add_to_branch_len = 0; /*bogdan*/
-
-
-       begin=p_msg->add_to_branch_s+p_msg->add_to_branch_len;
-       orig_size = size=MAX_BRANCH_PARAM_LEN - p_msg->add_to_branch_len;
-
-#ifndef USE_SYNONIM
-       if (memcpy(begin,trans->md5,MD5_LEN)) {begin+=MD5_LEN;size-=MD5_LEN;} else return -1;
-       if (size) { *begin=BRANCH_SEPARATOR; begin++; size--; } else return -1;
-#endif
-       if (int2reverse_hex( &begin, &size, trans->hash_index)==-1) return -1;
-#ifdef USE_SYNONIM
-       if (size) { *begin=BRANCH_SEPARATOR; begin++; size--; } else return -1;
-       if (int2reverse_hex( &begin, &size, trans->label)==-1) return -1;
-#endif
-       if (size) { *begin=BRANCH_SEPARATOR; begin++; size--; } else return -1;
-       if (int2reverse_hex( &begin, &size, branch)==-1) return -1;
-
-       p_msg->add_to_branch_len+=(orig_size-size);
-       DBG("DEBUG: XXX branch label created now: %.*s\n",
-               p_msg->add_to_branch_len, p_msg->add_to_branch_s );
-       return 0;
+       struct socket_info* send_sock;
+       struct via_body* via;
 
+       if (!reply_to_via) {
+               update_sock_struct_from_ip( &rb->to, msg );
+       } else {
+               via=msg->via1;
+               /*init retrans buffer*/
+               if (update_sock_struct_from_via( &(rb->to),via )==-1) {
+                       LOG(L_ERR, "ERROR: init_rb: cannot lookup reply dst: %.*s\n",
+                               via->host.len, via->host.s );
+                       ser_error=E_BAD_VIA;
+                       return 0;
+               }
+       }
+       send_sock=get_send_socket(&rb->to);
+       if (send_sock==0) {
+               LOG(L_ERR, "ERROR: init_rb: cannot fwd to af %d "
+                       "no socket\n", rb->to.s.sa_family);
+               ser_error=E_BAD_VIA;
+               return 0;
+       }
+       rb->send_sock=send_sock;
+    return 1;
 }
 
 
+
 /* atomic "new_tran" construct; it returns:
 
-       -1      if      a request matched a transaction
-               - if that was an ack, the calling function
-                 shall reset timers
-               - otherwise the calling function shall 
-                 attempt to retransmit
+       <0      on error
 
        +1      if a request did not match a transaction
                - it that was an ack, the calling function
@@ -719,71 +566,143 @@ int add_branch_label( struct cell *trans, struct sip_msg *p_msg, int branch )
                  introduced and the calling function
                  shall reply/relay/whatever_appropriate
 
-       0 on error
+       0 on retransmission
 */
 int t_newtran( struct sip_msg* p_msg )
 {
 
        int ret, lret;
        struct cell *new_cell;
+       struct sip_msg *shm_msg;
+       int a,b,c;
+
+       ret=1;
 
        /* is T still up-to-date ? */
        DBG("DEBUG: t_addifnew: msg id=%d , global msg id=%d ,"
                " T on entrance=%p\n",p_msg->id,global_msg_id,T);
 
-       if ( !(p_msg->id != global_msg_id || T==T_UNDEFINED 
-               /* if someone tried to do something previously by mistake with
-                  a transaction which did not exist yet, try to look-up
-                  the transacion too */
-               || T==T_NULL)) 
-       {
-               LOG(L_ERR, "ERROR: t_newtran: alreaddy processing this message"
-                       ", T %s found\n", T ? "" : "not" );
-               return 0;
+       if ( T && T!=T_UNDEFINED  ) {
+               LOG(L_ERR, "ERROR: t_newtran: "
+                       "transaction already in process %p\n", T );
+               return E_SCRIPT;
        }
 
        global_msg_id = p_msg->id;
        T = T_UNDEFINED;
-       /* transaction lookup */
-       /* force parsing all the needed headers*/
-       if (parse_headers(p_msg, HDR_EOH, 0 )==-1)
-               return 0;
-       lret = t_lookup_request( p_msg, 1 /* leave locked */ );
+       /* first of all, parse everything -- we will store
+          in shared memory and need to have all headers
+          ready for generating potential replies later;
+          parsing later on demand is not an option since
+          the request will be in shmem and applying 
+          parse_headers to it would intermix shmem with
+          pkg_mem
+       */
+       
+       if (parse_headers(p_msg, HDR_EOH, 0 )) {
+               LOG(L_ERR, "ERROR: t_newtran: parse_headers failed\n");
+               return E_BAD_REQ;
+       }
+       if ((p_msg->parsed_flag & HDR_EOH)!=HDR_EOH) {
+                       LOG(L_ERR, "ERROR: t_newtran: EoH not parsed\n");
+                       return E_OUT_OF_MEM;
+       }
+       /* t_lookup_requests attmpts to find the transaction; 
+          it also calls check_transaction_quadruple -> it is
+          safe to assume we have from/callid/cseq/to
+       */ 
+       lret = t_lookup_request( p_msg, 1 /* leave locked if not found */ );
        /* on error, pass the error in the stack ... */
-       if (lret==0) return 0;
+       if (lret==0) return E_BAD_TUPEL;
        if (lret<0) {
+               new_cell=0;
                /* transaction not found, it's a new request;
                   establish a new transaction (unless it is an ACK) */
-               ret=1;
                if ( p_msg->REQ_METHOD!=METHOD_ACK ) {
                        /* add new transaction */
                        new_cell = build_cell( p_msg ) ;
                        if  ( !new_cell ){
                                LOG(L_ERR, "ERROR: t_addifnew: out of mem:\n");
-                               ret = 0;
+                               ret = E_OUT_OF_MEM;
                        } else {
                                insert_into_hash_table_unsafe( hash_table , new_cell );
                                T=new_cell;
-                               T_REF(T);
+                               INIT_REF_UNSAFE(T);
+                               /* init pointers to headers needed to construct local
+                                  requests such as CANCEL/ACK
+                               */
+
+                               shm_msg=new_cell->uas.request;
+                               new_cell->from.s=shm_msg->from->name.s;
+                               new_cell->from.len=HF_LEN(shm_msg->from);
+                               new_cell->to.s=shm_msg->to->name.s;
+                               new_cell->to.len=HF_LEN(shm_msg->to);
+                               new_cell->callid.s=shm_msg->callid->name.s;
+                               new_cell->callid.len=HF_LEN(shm_msg->callid);
+                               new_cell->cseq_n.s=shm_msg->cseq->name.s;
+                               new_cell->cseq_n.len=get_cseq(shm_msg)->number.s
+                                       +get_cseq(shm_msg)->number.len
+                                       -shm_msg->cseq->name.s;
+
+                               new_cell->method=new_cell->uas.request->first_line.u.request.method;
+                               new_cell->is_invite=p_msg->REQ_METHOD==METHOD_INVITE;
                        }
+
                }
 
                /* was it an e2e ACK ? if so, trigger a callback */
                if (lret==-2) {
-                               T_REF(t_ack);
-                               unlock(&(hash_table->entrys[p_msg->hash_index].mutex));
-                               callback_event( TMCB_E2EACK, t_ack, p_msg );
-                               T_UNREF(t_ack);
+                               REF_UNSAFE(t_ack);
+                               UNLOCK_HASH(p_msg->hash_index);
+                               callback_event( TMCB_E2EACK, t_ack, p_msg, p_msg->REQ_METHOD );
+                               UNREF(t_ack);
                } else { /* not e2e ACK */
-                       unlock(&(hash_table->entrys[p_msg->hash_index].mutex));
+                       UNLOCK_HASH(p_msg->hash_index);
+                       /* now, when the transaction state exists, check if
+                          there is a meaningful Via and calculate it; better
+                          do it now than later: state is established so that
+                          subsequent retransmissions will be absorbed and will
+                          not possibly block during Via DNS resolution; doing
+                          it later would only burn more CPU as if there is an
+                          error, we cannot relay later whatever comes out of the
+                          the transaction 
+                       */
+                       if (new_cell && p_msg->REQ_METHOD!=METHOD_ACK) {
+                               if (!init_rb( &T->uas.response, p_msg)) {
+                                       LOG(L_ERR, "ERROR: t_newtran: unresolveable via1\n");
+                                       put_on_wait( T );
+                                       t_unref(p_msg);
+                                       ret=E_BAD_VIA;
+                               }
+                       }
                }
 
                return ret;
        } else {
-               /* transaction found, it's a retransmission  or ACK */
-                       return -1;
+               /* transaction found, it's a retransmission  or hbh ACK */
+               if (p_msg->REQ_METHOD==METHOD_ACK) {
+                       t_release_transaction(T);
+               } else {
+                       t_retransmit_reply(T);
+               }
+               /* things are done -- return from script */
+               return 0;
        }
+
 }
 
 
+int t_unref( struct sip_msg* p_msg  )
+{
+       if (T==T_UNDEFINED || T==T_NULL)
+               return -1;
+       if (T->kr==0 
+               ||(p_msg->REQ_METHOD==METHOD_ACK && !(T->kr & REQ_RLSD))) {
+               LOG(L_WARN, "WARNING: script writer didn't release transaction\n");
+               t_release_transaction(T);
+       }
+       UNREF( T );
+       T=T_UNDEFINED;
+       return 1;
+}
 
diff --git a/modules/tm/t_lookup.h b/modules/tm/t_lookup.h
new file mode 100644 (file)
index 0000000..f56d404
--- /dev/null
@@ -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
+
index b4f4e19..9fcb74e 100644 (file)
@@ -4,13 +4,16 @@
  * message printing
  */
 
-#include "hash_func.h"
+#include "../../hash_func.h"
+#include "../../globals.h"
 #include "t_funcs.h"
 #include "../../dprint.h"
 #include "../../config.h"
 #include "../../parser/parser_f.h"
 #include "../../ut.h"
 #include "../../parser/msg_parser.h"
+#include "t_msgbuilder.h"
+#include "uac.h"
 
 
 
                        memcpy((_d),(_s),(_len));\
                        (_d) += (_len);\
                }while(0);
+#define append_str( _p, _str ) \
+               do { \
+                       memcpy((_p), (_str).s, (_str).len); \
+                       (_p)+=(_str).len; \
+               } while(0);
 
 
-/* Builds a CANCEL request based on an INVITE request. CANCEL is send
- * to same address as the INVITE */
-int t_build_and_send_CANCEL(struct cell *Trans,unsigned int branch)
+/* Build a local request based on a previous request; main
+   customers of this function are local ACK and local CANCEL
+ */
+char *build_local(struct cell *Trans,unsigned int branch,
+       unsigned int *len, char *method, int method_len, str *to)
 {
-       struct sip_msg      *p_msg;
-       struct hdr_field    *hdr;
        char                *cancel_buf, *p, *via;
-       unsigned int         len, via_len;
-
-       if ( !Trans->uac[branch].rpl_received )
-       {
-               DBG("DEBUG: t_build_and_send_CANCEL: no response ever received"
-                       " : dropping local cancel! \n");
-               return 1;
-       }
+       unsigned int         via_len;
+       struct hdr_field    *hdr;
+       char branch_buf[MAX_BRANCH_PARAM_LEN];
+       int branch_len;
 
-       if (Trans->uac[branch].request.cancel!=NO_CANCEL)
+       if ( Trans->uac[branch].last_received<100)
        {
-               DBG("DEBUG: t_build_and_send_CANCEL: branch (%d)was already canceled"
-                       " : dropping local cancel! \n",branch);
-               return 1;
+               DBG("DEBUG: build_local: no response ever received"
+                       " : dropping local request! \n");
+               goto error;
        }
 
-       cancel_buf = 0;
-       via = 0;
-       p_msg = Trans->uas.request;
-
-       /* method, separators, version */
-       len=SIP_VERSION_LEN + CANCEL_LEN + 2 /* spaces */ + CRLF_LEN;
-       /* if URL was overridden .... */
-       if (Trans->uac[branch].uri.s)
-               len+=Trans->uac[branch].uri.len;
-       else
-       /* ... otherwise use the inbound URL */
-               len+=REQ_LINE(p_msg).uri.len;
+       /* method, separators, version: "CANCEL sip:p2@iptel.org SIP/2.0" */
+       *len=SIP_VERSION_LEN + method_len + 2 /* spaces */ + CRLF_LEN;
+       *len+=Trans->uac[branch].uri.len;
 
        /*via*/
-       if ( add_branch_label(Trans,p_msg,branch)==-1 )
+       if (!t_calc_branch(Trans,  branch, 
+               branch_buf, &branch_len ))
                goto error;
-       via = via_builder(p_msg , &via_len, Trans->uac[branch].request.send_sock );
+       via=via_builder(&via_len, Trans->uac[branch].request.send_sock,
+               branch_buf, branch_len );
        if (!via)
        {
                LOG(L_ERR, "ERROR: t_build_and_send_CANCEL: "
                        "no via header got from builder\n");
                goto error;
        }
-       len+= via_len;
+       *len+= via_len;
        /*headers*/
-       for ( hdr=p_msg->headers ; hdr ; hdr=hdr->next ) {
-               if (hdr->type==HDR_FROM || hdr->type==HDR_CALLID 
-                       || hdr->type==HDR_TO )
-                       len += ((hdr->body.s+hdr->body.len ) - hdr->name.s ) + CRLF_LEN ;
-               else if (hdr->type==HDR_CSEQ)
-                       len += hdr->name.len + 2 + ((struct cseq_body*)hdr->parsed)->number.len +
-                               1+CANCEL_LEN+CRLF_LEN;
+       *len+=Trans->from.len+CRLF_LEN
+               +Trans->callid.len+CRLF_LEN
+               +to->len+CRLF_LEN
+               /* CSeq: 101 CANCEL */
+               +Trans->cseq_n.len+1+method_len+CRLF_LEN; 
+
+       /* copy'n'paste Route headers */
+       if (!Trans->local) {
+               for ( hdr=Trans->uas.request->headers ; hdr ; hdr=hdr->next )
+                        if (hdr->type==HDR_ROUTE)
+                               len+=((hdr->body.s+hdr->body.len ) - hdr->name.s ) + 
+                                       CRLF_LEN ;
+       }
+
+       /* User Agent */
+       if (server_signature) {
+               *len += USER_AGENT_LEN + CRLF_LEN;
        }
-       /* User Agent, Conteny Length, EoM */
-       len += USER_AGENT_LEN + CRLF_LEN +
-               CONTENT_LEN_LEN + CRLF_LEN +
-               CRLF_LEN;
+       /* Content Length, EoM */
+       *len+=CONTENT_LEN_LEN + CRLF_LEN + CRLF_LEN;
 
-       cancel_buf=sh_malloc( len+1 );
+       cancel_buf=shm_malloc( *len+1 );
        if (!cancel_buf)
        {
                LOG(L_ERR, "ERROR: t_build_and_send_CANCEL: cannot allocate memory\n");
-               goto error;
+               goto error01;
        }
        p = cancel_buf;
 
-       append_mem_block( p, CANCEL " ", CANCEL_LEN +1 );
-       if (Trans->uac[branch].uri.s) {
-               append_mem_block( p, Trans->uac[branch].uri.s, 
-                       Trans->uac[branch].uri.len);
-       } else {
-               append_mem_block(p,REQ_LINE(p_msg).uri.s,
-                       REQ_LINE(p_msg).uri.len );
-       }
+       append_mem_block( p, method, method_len );
+       append_mem_block( p, " ", 1 );
+       append_str( p, Trans->uac[branch].uri );
        append_mem_block( p, " " SIP_VERSION CRLF, 1+SIP_VERSION_LEN+CRLF_LEN );
 
        /* insert our via */
        append_mem_block(p,via,via_len);
 
        /*other headers*/
-       for ( hdr=p_msg->headers ; hdr ; hdr=hdr->next )
-       {
-               if(hdr->type==HDR_FROM||hdr->type==HDR_CALLID||hdr->type==HDR_TO)
-               {
-                       append_mem_block(p,hdr->name.s,
-                               ((hdr->body.s+hdr->body.len)-hdr->name.s) );
-                       append_mem_block(p, CRLF, CRLF_LEN );
-               } else if ( hdr->type==HDR_CSEQ )
-               {
-                       append_mem_block(p,hdr->name.s, hdr->name.len );
-                       append_mem_block(p,": ", 2 );
-                       append_mem_block(p, ((struct cseq_body*)hdr->parsed)->number.s,
-                               ((struct cseq_body*)hdr->parsed)->number.len );
-                       append_mem_block(p, " " CANCEL CRLF, 1+CANCEL_LEN+CRLF_LEN);
-               }
+       append_str( p, Trans->from );
+       append_mem_block( p, CRLF, CRLF_LEN );
+       append_str( p, Trans->callid );
+       append_mem_block( p, CRLF, CRLF_LEN );
+       append_str( p, *to );
+       append_mem_block( p, CRLF, CRLF_LEN );
+       append_str( p, Trans->cseq_n );
+       append_mem_block( p, " ", 1 );
+       append_mem_block( p, method, method_len );
+       append_mem_block( p, CRLF, CRLF_LEN );
+
+       if (!Trans->local)  {
+               for ( hdr=Trans->uas.request->headers ; hdr ; hdr=hdr->next )
+                       if(hdr->type==HDR_ROUTE) {
+                               append_mem_block(p, hdr->name.s,
+                                       hdr->body.s+hdr->body.len-hdr->name.s );
+                               append_mem_block(p, CRLF, CRLF_LEN );
+                       }
        }
 
-       /* User Agent header, Content Length, EoM */
-       append_mem_block(p,USER_AGENT CRLF CONTENT_LEN CRLF CRLF ,
-               USER_AGENT_LEN + CRLF_LEN + CONTENT_LEN_LEN + CRLF_LEN + CRLF_LEN);
-       *p=0;
-
-       if (Trans->uac[branch].request.cancel) {
-               shm_free( cancel_buf );
-               LOG(L_WARN, "send_cancel: Warning: CANCEL already sent out\n");
-               goto error;
+       /* User Agent header */
+       if (server_signature) {
+               append_mem_block(p,USER_AGENT CRLF, USER_AGENT_LEN+CRLF_LEN );
        }
-
-       Trans->uac[branch].request.activ_type = TYPE_LOCAL_CANCEL;
-       Trans->uac[branch].request.cancel = cancel_buf;
-       Trans->uac[branch].request.cancel_len = len;
-
-       /*sets and starts the FINAL RESPONSE timer */
-       set_timer(hash_table,&(Trans->uac[branch].request.fr_timer),FR_TIMER_LIST);
-       /* sets and starts the RETRANS timer */
-       Trans->uac[branch].request.retr_list = RT_T1_TO_1;
-       set_timer(hash_table,&(Trans->uac[branch].request.retr_timer),RT_T1_TO_1);
-       DBG("DEBUG: T_build_and_send_CANCEL : sending cancel...\n");
-       SEND_CANCEL_BUFFER( &(Trans->uac[branch].request) );
+       /* Content Length, EoM */
+       append_mem_block(p, CONTENT_LEN CRLF CRLF ,
+               CONTENT_LEN_LEN + CRLF_LEN + CRLF_LEN);
+       *p=0;
 
        pkg_free(via);
-       return 1;
+       return cancel_buf;
+error01:
+       pkg_free(via);
 error:
-       if (via) pkg_free(via);
-       return -1;
+       return NULL;
 }
 
 
-/* Builds an ACK request based on an INVITE request. ACK is send
- * to same address */
-char *build_ack(struct sip_msg* rpl,struct cell *trans,int branch,int *ret_len)
-{
-       struct sip_msg      *p_msg , *r_msg;
-       struct hdr_field    *hdr;
-       char                *ack_buf, *p, *via;
-       unsigned int         len, via_len;
-
-       ack_buf = 0;
-       via =0;
-       p_msg = trans->uas.request;
-       r_msg = rpl;
 
-       if ( parse_headers(rpl,HDR_TO, 0)==-1 || !rpl->to )
-       {
-               LOG(L_ERR, "ERROR: t_build_ACK: "
-                       "cannot generate a HBH ACK if key HFs in reply missing\n");
+char *build_uac_request(  str msg_type, str dst,
+       str headers, str body, int branch, 
+               struct cell *t, int *len)
+{
+       char *via;
+       int via_len;
+       char content_len[10];
+       int content_len_len;
+       char *buf;
+       char *w;
+       int dummy;
+
+       char branch_buf[MAX_BRANCH_PARAM_LEN];
+       int branch_len;
+
+       static int from_len=0;
+
+       buf=0;
+       if (from_len==0) from_len=strlen(uac_from);
+       
+       *len=SIP_VERSION_LEN+msg_type.len+2/*spaces*/+CRLF_LEN+
+               dst.len;
+
+       if (!t_calc_branch(t, branch, branch_buf, &branch_len )) {
+               LOG(L_ERR, "ERROR: build_uac_request: branch calculation failed\n");
                goto error;
        }
-
-       len = USER_AGENT_LEN + CRLF_LEN;
-       /*first line's len */
-       len += 4/*reply code and one space*/+
-               p_msg->first_line.u.request.version.len+CRLF_LEN;
-       /*uri's len*/
-       if (trans->uac[branch].uri.s)
-               len += trans->uac[branch].uri.len +1;
-       else
-               len += p_msg->first_line.u.request.uri.len +1;
-       /*adding branch param*/
-       if ( add_branch_label( trans , trans->uas.request , branch)==-1 )
-               goto error;
-       /*via*/
-       via = via_builder(p_msg , &via_len, trans->uac[branch].request.send_sock );
-       if (!via)
-       {
-               LOG(L_ERR, "ERROR: t_build_ACK: "
-                       "no via header got from builder\n");
+       via=via_builder(&via_len, t->uac[branch].request.send_sock,
+               branch_buf, branch_len );
+       
+       if (!via) {
+               LOG(L_ERR, "ERROR: build_uac_request: via building failed\n");
                goto error;
        }
-       len+= via_len;
-       /*headers*/
-       for ( hdr=p_msg->headers ; hdr ; hdr=hdr->next )
-               if (hdr->type==HDR_FROM||hdr->type==HDR_CALLID||hdr->type==HDR_CSEQ)
-                       len += ((hdr->body.s+hdr->body.len ) - hdr->name.s ) + CRLF_LEN ;
-               else if ( hdr->type==HDR_TO )
-                       len += ((r_msg->to->body.s+r_msg->to->body.len ) -
-                               r_msg->to->name.s ) + CRLF_LEN ;
-       /* CSEQ method : from INVITE-> ACK */
-       len -= 3  ;
-       /* end of message */
-       len += CRLF_LEN; /*new line*/
-
-       ack_buf = sh_malloc(len+1);
-       if (!ack_buf)
-       {
-               LOG(L_ERR, "ERROR: t_build_and_ACK: cannot allocate memory\n");
+       *len+=via_len;
+       /* content length */
+       content_len_len=snprintf(
+               content_len, sizeof(content_len), "%d", body.len );
+       /* header names and separators */
+       *len+=
+               +CSEQ_LEN+CRLF_LEN
+               +TO_LEN+CRLF_LEN
+               +CALLID_LEN+CRLF_LEN
+               +CONTENT_LENGTH_LEN+CRLF_LEN
+               + (server_signature ? USER_AGENT_LEN + CRLF_LEN : 0 )
+               +FROM_LEN+CRLF_LEN
+               +CRLF_LEN; /* EoM */
+       /* header field value and body length */
+       *len+= msg_type.len+1+UAC_CSEQNR_LEN /* CSeq: method, delimitor, number  */
+               + dst.len /* To */
+               + RAND_DIGITS+1+MAX_PID_LEN+1+MAX_SEQ_LEN /* call-id */
+               + from_len+FROMTAG_LEN+MD5_LEN+
+               + content_len_len
+               + headers.len
+               + body.len;
+       
+       buf=shm_malloc( *len+1 );
+       if (!buf) {
+               LOG(L_ERR, "ERROR: t_uac: no shmem\n");
                goto error1;
        }
-       p = ack_buf;
-
-       /* first line */
-       memcpy( p , "ACK " , 4);
-       p += 4;
-       /* uri */
-       if ( trans->uac[branch].uri.s )
-       {
-               memcpy(p,trans->uac[branch].uri.s,trans->uac[branch].uri.len);
-               p +=trans->uac[branch].uri.len;
-       }else{
-               memcpy(p,p_msg->orig+(p_msg->first_line.u.request.uri.s-p_msg->buf),
-                       p_msg->first_line.u.request.uri.len );
-               p += p_msg->first_line.u.request.uri.len;
+       w=buf;
+       memapp( w, msg_type.s, msg_type.len ); 
+       memapp( w, " ", 1); 
+       t->uac[branch].uri.s=w; t->uac[branch].uri.len=dst.len;
+       memapp( w, dst.s, dst.len ); 
+       memapp( w, " " SIP_VERSION CRLF, 1+SIP_VERSION_LEN+CRLF_LEN );
+       memapp( w, via, via_len );
+       t->cseq_n.s=w; t->cseq_n.len=CSEQ_LEN+UAC_CSEQNR_LEN;
+       memapp( w, CSEQ UAC_CSEQNR " ", CSEQ_LEN + UAC_CSEQNR_LEN+ 1 );
+       memapp( w, msg_type.s, msg_type.len );
+       t->to.s=w+CRLF_LEN; t->to.len=TO_LEN+dst.len;
+       memapp( w, CRLF TO, CRLF_LEN + TO_LEN  );
+       memapp( w, dst.s, dst.len );
+       t->callid.s=w+CRLF_LEN; t->callid.len=CALLID_LEN+RAND_DIGITS+1+
+               MAX_PID_LEN+1+MAX_SEQ_LEN;
+       memapp( w, CRLF CALLID, CRLF_LEN + CALLID_LEN  );
+       memapp( w, call_id, RAND_DIGITS+1+MAX_PID_LEN+1+MAX_SEQ_LEN );
+       memapp( w, CRLF CONTENT_LEN, CRLF_LEN + CONTENT_LEN_LEN);
+       memapp( w, content_len, content_len_len );
+       if (server_signature) {
+               memapp( w, CRLF USER_AGENT CRLF FROM, 
+                       CRLF_LEN+USER_AGENT_LEN+CRLF_LEN+FROM_LEN);
+       } else {
+               memapp( w, CRLF  FROM, 
+                       CRLF_LEN+FROM_LEN);
        }
-       /* SIP version */
-       *(p++) = ' ';
-       memcpy(p,p_msg->orig+(p_msg->first_line.u.request.version.s-p_msg->buf),
-               p_msg->first_line.u.request.version.len );
-       p += p_msg->first_line.u.request.version.len;
-       memcpy( p, CRLF, CRLF_LEN );
-       p+=CRLF_LEN;
-
-       /* insert our via */
-       memcpy( p , via , via_len );
-       p += via_len;
-
-       /*other headers*/
-       for ( hdr=p_msg->headers ; hdr ; hdr=hdr->next )
-       {
-               if ( hdr->type==HDR_FROM || hdr->type==HDR_CALLID  )
-               {
-                       memcpy( p , p_msg->orig+(hdr->name.s-p_msg->buf) ,
-                               ((hdr->body.s+hdr->body.len ) - hdr->name.s ) );
-                       p += ((hdr->body.s+hdr->body.len ) - hdr->name.s );
-                       memcpy( p, CRLF, CRLF_LEN );
-                       p+=CRLF_LEN;
-               }
-               else if ( hdr->type==HDR_TO )
-               {
-                       memcpy( p , r_msg->orig+(r_msg->to->name.s-r_msg->buf) ,
-                               ((r_msg->to->body.s+r_msg->to->body.len)-r_msg->to->name.s));
-                       p+=((r_msg->to->body.s+r_msg->to->body.len)-r_msg->to->name.s);
-                       memcpy( p, CRLF, CRLF_LEN );
-                       p+=CRLF_LEN;
-               }
-               else if ( hdr->type==HDR_CSEQ )
-               {
-                       memcpy( p , p_msg->orig+(hdr->name.s-p_msg->buf) ,
-                               ((((struct cseq_body*)hdr->parsed)->method.s)-hdr->name.s));
-                       p+=((((struct cseq_body*)hdr->parsed)->method.s)-hdr->name.s);
-                       memcpy( p , "ACK" CRLF, 3+CRLF_LEN );
-                       p += 3+CRLF_LEN;
-               }
+       t->from.s=w-FROM_LEN; t->from.len=FROM_LEN+from_len+FROMTAG_LEN+MD5_LEN;
+       memapp( w, uac_from, from_len );
+       memapp( w, FROMTAG, FROMTAG_LEN );
+       memapp( w, from_tag, MD5_LEN );
+       memapp( w, CRLF, CRLF_LEN );
+
+       memapp( w, headers.s, headers.len );
+       /* EoH */
+       memapp( w, CRLF, CRLF_LEN );
+       if ( body.s ) {
+               memapp( w, body.s, body.len );
        }
-
-       /* end of message */
-       memcpy( p , USER_AGENT CRLF CRLF , USER_AGENT_LEN + CRLF_LEN + CRLF_LEN );
-       p +=  USER_AGENT_LEN + CRLF_LEN + CRLF_LEN;
-
-       pkg_free( via );
-       DBG("DEBUG: t_build_ACK: ACK generated\n");
-
-       *(ret_len) = p-ack_buf;
-       return ack_buf;
-
+       /* ugly HACK -- debugging has shown len shorter by one */
+       dummy=*len+1;
+       *len=dummy;
+#      ifdef EXTRA_DEBUG
+       if (w-buf != *len ) abort();
+#      endif
+       
+       
 error1:
-       pkg_free(via );
+       pkg_free(via);  
 error:
-       return 0;
+       return buf;
+       
 }
 
 
+int t_calc_branch(struct cell *t, 
+       int b, char *branch, int *branch_len)
+{
+       return syn_branch ?
+               branch_builder( t->hash_index,
+                       t->label, 0,
+                       b, branch, branch_len )
+               : branch_builder( t->hash_index,
+                       0, t->md5,
+                       b, branch, branch_len );
+}
 
+int t_setbranch( struct cell *t, struct sip_msg *msg, int b )
+{
+       return t_calc_branch( t, b, 
+               msg->add_to_branch_s, &msg->add_to_branch_len );
+}
diff --git a/modules/tm/t_msgbuilder.h b/modules/tm/t_msgbuilder.h
new file mode 100644 (file)
index 0000000..7278d58
--- /dev/null
@@ -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
index 46bc8f8..290cff8 100644 (file)
  */
 
 
-#include "hash_func.h"
+#include "../../hash_func.h"
 #include "t_funcs.h"
 #include "../../dprint.h"
 #include "../../config.h"
 #include "../../parser/parser_f.h"
 #include "../../ut.h"
 #include "../../timer.h"
+#include "../../error.h"
+#include "../../action.h"
+#include "../../dset.h"
 
 #include "t_hooks.h"
+#include "t_funcs.h"
+#include "t_reply.h"
+#include "t_cancel.h"
+#include "t_msgbuilder.h"
+#include "t_lookup.h"
+#include "t_fwd.h"
+#include "fix_lumps.h"
+
+/* where to go if there is no positive reply */
+static int goto_on_negative=0;
+
+/* we store the reply_route # in private memory which is
+   then processed during t_relay; we cannot set this value
+   before t_relay creates transaction context or after
+   t_relay when a reply may arrive after we set this
+   value; that's why we do it how we do it, i.e.,
+   *inside*  t_relay using hints stored in private memory
+   before t_reay is called
+*/
+  
+  
+int t_on_negative( unsigned int go_to )
+{
+       goto_on_negative=go_to;
+       return 1;
+}
 
 
-
-
-inline int check_for_no_response( struct cell *Trans ,int code, int relay)
+unsigned int get_on_negative()
 {
-       if ( code/100>=3 && Trans->uac[Trans->nr_of_outgoings].uri.s )
-       {
-               forward_serial_branch( Trans , Trans->nr_of_outgoings );
-               return -1;
-       }
-       return relay;
+       return goto_on_negative;
 }
 
 
+/* the main code of stateful replying */
+static int _reply( struct cell *t, struct sip_msg* p_msg, unsigned int code,
+    char * text, int lock );
 
 /* Retransmits the last sent inbound reply.
  * input: p_msg==request for which I want to retransmit an associated reply
  * Returns  -1 - error
  *           1 - OK
  */
-int t_retransmit_reply( /* struct sip_msg* p_msg    */ )
+int t_retransmit_reply( struct cell *t )
 {
        static char b[BUF_SIZE];
        int len;
 
-       if (!T->uas.response.buffer)
+       /* first check if we managed to resolve topmost Via -- if
+          not yet, don't try to retransmit
+       */
+       if (!t->uas.response.send_sock) {
+               LOG(L_ERR, "ERROR: no resolved dst to retransmit\n");
                return -1;
+       }
+
+       /* we need to lock the transaction as messages from
+          upstream may change it continuously
+       */
+       LOCK_REPLIES( t );
+
+       if (!t->uas.response.buffer) {
+               DBG("DBG: t_retransmit_reply: nothing to retransmit\n");
+               goto error;
+       }
 
-       if ( (len=T->uas.response.buffer_len)==0 || len>BUF_SIZE ) {
-               UNLOCK_REPLIES( T );
-               return -2;
+       len=t->uas.response.buffer_len;
+       if ( len==0 || len>BUF_SIZE )  {
+               DBG("DBG: t_retransmit_reply: "
+                       "zero length or too big to retransmit: %d\n", len);
+               goto error;
        }
-       memcpy( b, T->uas.response.buffer, len );
-       UNLOCK_REPLIES( T );
-       SEND_PR_BUFFER( & T->uas.response, b, len );
+       memcpy( b, t->uas.response.buffer, len );
+       UNLOCK_REPLIES( t );
+       SEND_PR_BUFFER( & t->uas.response, b, len );
+       DBG("DEBUG: reply retransmitted. buf=%p: %.9s..., shmem=%p: %.9s\n", 
+               b, b, t->uas.response.buffer, t->uas.response.buffer );
        return 1;
+
+error:
+       UNLOCK_REPLIES(t);
+       return -1;
 }
 
 
 
+int t_reply( struct cell *t, struct sip_msg* p_msg, unsigned int code, 
+       char * text )
+{
+       return _reply( t, p_msg, code, text, 1 /* lock replies */ );
+}
+
+int t_reply_unsafe( struct cell *t, struct sip_msg* p_msg, unsigned int code, 
+       char * text )
+{
+       return _reply( t, p_msg, code, text, 0 /* don't lock replies */ );
+}
+
 
 
-/* Force a new response into inbound response buffer.
 * returns 1 if everything was OK or -1 for error
 */
-int t_send_reply( struct sip_msg* p_msg, unsigned int code
-       char * text, unsigned int branch)
+/* send a UAS reply
+ * returns 1 if everything