Browse Source

Merge branch 'ser_core_cvs'

* ser_core_cvs:
  tcp: fix compilation problem on solaris (FIONREAD)
  core: fix bad level name in new LOG()
  t_check_status() checks also the blind UACs if t_pick_branch()
  Documenting t_lookup_cancel() script function.
  Removing set_t() from t_lookup_cancel() function, and introducing
  updated udp_mtu handling code - fixes SER-433
  documenting t_is_expired() function
  t_is_expired() script function is introduced.
  * logging API updated (see doc/logging-api.txt for details)
  - AS support disabled by default.
  When building the route set of ACKs for local UACs, only the reply is now
  In case the AVP is a regexp, an allocation is required to build an AVP
  Currently, SER matches E2E ACKs only if there is an equality between From HF
  The calculate_routeset_length() produces an invalid result in the case
  'memapp' and 'append_mem_block' are now both only used in source
  "Route :" prefix (and separator) is used some more time across the
  script: udp_mtu fallback script config & commands
  core: forward: tcp fallback for big udp packets

Conflicts:
	action.c
	cfg.y
	cfg_core.c
	cfg_core.h
	dprint.h   - updated to the new logging api from ser, while
	             keeping the kamailio compatibility macros and
	              CRIT().
	usr_avp.h
Andrei Pelinescu-Onciul 16 years ago
parent
commit
a07e7447c3
39 changed files with 1889 additions and 455 deletions
  1. 12 0
      Makefile.defs
  2. 2 0
      Makefile.modules
  3. 32 3
      NEWS
  4. 13 11
      action.c
  5. 5 0
      cfg.lex
  6. 16 0
      cfg.y
  7. 12 1
      cfg_core.c
  8. 3 0
      cfg_core.h
  9. 6 0
      config.h
  10. 2 2
      dns_cache.c
  11. 134 0
      doc/logging-api.txt
  12. 28 28
      dprint.c
  13. 174 202
      dprint.h
  14. 8 6
      forward.c
  15. 8 8
      main.c
  16. 57 9
      modules/tm/README
  17. 33 28
      modules/tm/dlg.c
  18. 67 0
      modules/tm/doc/functions.xml
  19. 6 0
      modules/tm/h_table.c
  20. 23 2
      modules/tm/h_table.h
  21. 18 4
      modules/tm/t_hooks.h
  22. 51 3
      modules/tm/t_lookup.c
  23. 512 85
      modules/tm/t_msgbuilder.c
  24. 2 15
      modules/tm/t_msgbuilder.h
  25. 78 9
      modules/tm/t_reply.c
  26. 2 0
      modules/tm/t_reply.h
  27. 43 5
      modules/tm/tm.c
  28. 143 14
      modules/tm/uac.c
  29. 16 0
      modules/tm/uac.h
  30. 134 19
      msg_translator.c
  31. 2 0
      msg_translator.h
  32. 5 0
      parser/msg_parser.h
  33. 26 0
      route.c
  34. 3 1
      route_struct.h
  35. 155 0
      scripts/logging/fix-logs
  36. 37 0
      scripts/logging/fix-logs-all
  37. 5 0
      tcp_main.c
  38. 14 0
      usr_avp.c
  39. 2 0
      usr_avp.h

+ 12 - 0
Makefile.defs

@@ -456,6 +456,8 @@ endif
 #		core that the DNS servers are down. No DNS query is performed
 #		when the servers are unreachable, and even expired resource
 #		records are used from the cache. (requires external watchdog)
+# -DWITH_AS_SUPPORT
+#		adds support for Application Server interface
 # Sometimes is needes correct non-quoted $OS. HACK: gcc translates known OS to number ('linux'), so there is added underscore
 
 DEFS= $(extra_defs) \
@@ -1438,6 +1440,16 @@ ifeq  ($(OS), solaris)
 	ifeq ($(NO_SELECT),)
 		DEFS+=-DHAVE_SELECT
 	endif
+	# check for filio.h
+	filio_h_locations= /usr/include/sys/filio.h \
+						$(LOCALBASE)/include/sys/filio.h
+	has_filio_h=$(shell for r in $(filio_h_locations); do \
+						if  [ -r "$$r" ] ; then echo yes; exit; fi \
+						done;\
+				)
+	ifeq ($(has_filio_h), yes)
+		DEFS+=-DHAVE_FILIO_H
+	endif
 	ifeq ($(mode), release)
 		#use these only if you're using gcc with Solaris ld
 		#LDFLAGS=-O2 $(PROFILE)

+ 2 - 0
Makefile.modules

@@ -29,6 +29,8 @@ override static_modules_path=
 # should be set in Makefile of apart module
 # INCLUDES += -I$(COREPATH)
 
+DEFS += -DMOD_NAME='"$(MOD_NAME)"'
+
 ifneq ($(makefile_defs_included),1)
 $(error "the local makefile does not include Makefile.defs!")
 endif

+ 32 - 3
NEWS

@@ -97,7 +97,16 @@ modules:
  - blst      - new module containing script blacklist manipulations functions
                (the source of a message can be blacklisted, removed from the
                 blacklist or checked for presence in the blacklist).
- - tm        - added t_reset_fr(), t_reset_retr(), t_reset_max_lifetime()
+ - tm        - added API function t_get_canceled_ident(): returns the hash 
+               coordinates (bucket/index) of the transaction the currently 
+               processed CANCEL is targeting. Requires AS support enabled.
+             - added API function ack_local_uac(): allow generating the ACKs 
+               for 2xx'ed locally originated INVITEs - new headers and body can
+               now also be appended to it. Requires AS support enabled.
+             - matching of E2E ACKs no longer requires full From HF identity,
+               but rather only tag equality (this behaviour can be changed by
+               defining TM_E2E_ACK_CHECK_FROM_URI)
+             - added t_reset_fr(), t_reset_retr(), t_reset_max_lifetime()
              - t_relay_to renamed to t_relay_to_avp (undocumented function)
              - t_relay() can now also take host and port parameters (e.g.
                t_relay(host, port)), behaving like a statefull 
@@ -220,6 +229,10 @@ modules:
                         - t_set_retr(t1, t2) - changes the retransmissions
                            intervals on the fly, on a per transaction basis.
 core:
+             - fallback to tcp or other congestion controlled transport 
+               protocol if a forwarded udp sip request is greater then 
+               udp_mtu (config). Default off. See udp_mtu and 
+               udp_mtu_try_proto.
              - sctp support (one-to-many, work in progress, for now linux
                only with no fallback to one-to-one on full send buffers)
              - partial cygwin (windows) support revived: core+static modules, 
@@ -240,6 +253,17 @@ core:
                between the short name and long name in cache as CNAME record
 
 new config variables:
+  udp_mtu = number - fallback to another protocol (udp_mtu_try_proto must be
+                     set also either globally or per packet) if the constructed
+                     request size is greater then udp_mtu.
+                     Recommended size: 1300. Default: 0 (off).
+  udp_mtu_try_proto = TCP|TLS|SCTP|UDP - if udp_mtu !=0 and udp forwarded
+                     request size (after adding all the "local" headers) >
+                     udp_mtu, use this protocol instead of udp. Only the
+                     Via header will be updated (e.g. The Record-Route
+                     will be the one built for udp).
+                     Default: UDP (off). Recommended: TCP.
+  force_rport =yes/no - like force_rport(), but works globally.
   disable_sctp = yes/no - disable sctp support (default auto, see enable_sctp)
   enable_sctp = 0/1/2  - disable (0)/enable (1)/auto (2) sctp support, 
                          default auto (2)
@@ -260,8 +284,8 @@ new config variables:
                       hosts (default: 0).
   server_id = number - A configurable unique server id that can be used to
                        discriminate server instances within a cluster of
-                       servers when all other information, such as IP adddresses
-                       are same.
+                       servers when all other information, such as IP addresses
+                       are the same.
   loadpath = <modules path> - directory where to load the modules from (-L
      equivalent); modules can be loaded simply by specifying their name
      (loadmodule "maxfwd")
@@ -356,6 +380,11 @@ new config variables:
     is not initialized at startup and cannot be enabled runtime,
     that saves some memory.
 
+new script commands:
+  udp_mtu_try_proto(TCP|TLS|SCTP|UDP) - same as udp_mtu_try_proto=... (see
+    above), but works per packet and not globally.
+
+
 build system:
  - check defines and includes used at compile time and if different 
    force rebuilding everything in the current dir (creates a new file: 

+ 13 - 11
action.c

@@ -44,10 +44,11 @@
  *  2006-07-27  dns cache and dns based send address failover support (andrei)
  *  2006-12-06  on popular request last_retcode set also by module functions
  *              (andrei)
- *  2007-06-14  run_actions & do_action need a ctx or handle now, no more 
+ *  2007-06-14  run_actions & do_action need a ctx or handle now, no more
  *               static vars (andrei)
  *  2008-11-18  support for variable parameter module functions (andrei)
  *  2008-12-03  use lvalues/rvalues for assignments (andrei)
+ *  2008-12-17  added UDP_MTU_TRY_PROTO_T (andrei)
  */
 
 
@@ -228,7 +229,7 @@ int do_action(struct run_act_ctx* h, struct action* a, struct sip_msg* msg)
 #endif
 				}
 
-#ifdef HONOR_MADDR				
+#ifdef HONOR_MADDR
 				if (u->maddr_val.s && u->maddr_val.len)
 					dst_host=&u->maddr_val;
 				else
@@ -310,7 +311,7 @@ int do_action(struct run_act_ctx* h, struct action* a, struct sip_msg* msg)
 				ret=E_BUG;
 				break;
 			}
-			LOG(a->val[0].u.number, "%s", a->val[1].u.string);
+			LOG_(a->val[0].u.number, "<script>: ", "%s", a->val[1].u.string);
 			ret=1;
 			break;
 
@@ -715,11 +716,11 @@ int do_action(struct run_act_ctx* h, struct action* a, struct sip_msg* msg)
 					ret=1;  /*default is continue */
 					if (v>0) {
 						if ((a->val[1].type==ACTIONS_ST)&&a->val[1].u.data){
-							ret=run_actions(h, 
+							ret=run_actions(h,
 										(struct action*)a->val[1].u.data, msg);
 						}
 					}else if ((a->val[2].type==ACTIONS_ST)&&a->val[2].u.data){
-							ret=run_actions(h, 
+							ret=run_actions(h,
 										(struct action*)a->val[2].u.data, msg);
 					}
 				}
@@ -935,6 +936,10 @@ sw_jt_def:
 			msg->msg_flags|=FL_FORCE_RPORT;
 			ret=1; /* continue processing */
 			break;
+		case UDP_MTU_TRY_PROTO_T:
+			msg->msg_flags|= (unsigned int)a->val[0].u.number & FL_MTU_FB_MASK;
+			ret=1; /* continue processing */
+			break;
 		case SET_ADV_ADDR_T:
 			if (a->val[0].type!=STR_ST){
 				LOG(L_CRIT, "BUG: do_action: bad set_advertised_address() "
@@ -993,11 +998,11 @@ sw_jt_def:
 			ret=1; /* continue processing */
 			break;
 
-	 case ADD_T:
-	case ASSIGN_T:
+		case ADD_T:
+		case ASSIGN_T:
 			v=lval_assign(h, msg, (struct lvalue*)a->val[0].u.data,
 								  (struct rval_expr*)a->val[1].u.data);
-			if (likely(v>=0)) 
+			if (likely(v>=0))
 				ret = 1;
 			else if (unlikely (v == EXPR_DROP)) /* hack to quit on DROP*/
 				ret=0;
@@ -1090,6 +1095,3 @@ error:
 	h->rec_lev--;
 	return ret;
 }
-
-
-

+ 5 - 0
cfg.lex

@@ -162,6 +162,8 @@ ROUTE_SEND onsend_route
 EXEC	exec
 FORCE_RPORT		"force_rport"|"add_rport"
 FORCE_TCP_ALIAS		"force_tcp_alias"|"add_tcp_alias"
+UDP_MTU		"udp_mtu"
+UDP_MTU_TRY_PROTO	"udp_mtu_try_proto"
 SETFLAG		setflag
 RESETFLAG	resetflag
 ISFLAGSET	isflagset
@@ -491,6 +493,9 @@ EAT_ABLE	[\ \t\b\r]
 <INITIAL>{FORCE_RPORT}	{ count(); yylval.strval=yytext; return FORCE_RPORT; }
 <INITIAL>{FORCE_TCP_ALIAS}	{ count(); yylval.strval=yytext;
 								return FORCE_TCP_ALIAS; }
+<INITIAL>{UDP_MTU}	{ count(); yylval.strval=yytext; return UDP_MTU; }
+<INITIAL>{UDP_MTU_TRY_PROTO}	{ count(); yylval.strval=yytext;
+									return UDP_MTU_TRY_PROTO; }
 <INITIAL>{IF}	{ count(); yylval.strval=yytext; return IF; }
 <INITIAL>{ELSE}	{ count(); yylval.strval=yytext; return ELSE; }
 

+ 16 - 0
cfg.y

@@ -129,6 +129,7 @@
 #include "lvalue.h"
 #include "rvalue.h"
 #include "sr_compat.h"
+#include "msg_translator.h"
 
 #include "config.h"
 #include "cfg_core.h"
@@ -274,6 +275,8 @@ static struct case_stms* mk_case_stm(struct rval_expr* ct, struct action* a);
 %token REVERT_URI
 %token FORCE_RPORT
 %token FORCE_TCP_ALIAS
+%token UDP_MTU
+%token UDP_MTU_TRY_PROTO
 %token IF
 %token ELSE
 %token SET_ADV_ADDRESS
@@ -1296,6 +1299,15 @@ assign_stm:
 	| STUN_ALLOW_FP EQUAL NUMBER { IF_STUN(stun_allow_fp=$3) ; }
 	| STUN_ALLOW_FP EQUAL error{ yyerror("number expected"); }
     | SERVER_ID EQUAL NUMBER { server_id=$3; }
+	| UDP_MTU EQUAL NUMBER { default_core_cfg.udp_mtu=$3; }
+	| UDP_MTU EQUAL error { yyerror("number expected"); }
+	| FORCE_RPORT EQUAL NUMBER 
+		{ default_core_cfg.force_rport=$3; fix_global_req_flags(0); }
+	| FORCE_RPORT EQUAL error { yyerror("boolean value expected"); }
+	| UDP_MTU_TRY_PROTO EQUAL proto
+		{ default_core_cfg.udp_mtu_try_proto=$3; fix_global_req_flags(0); }
+	| UDP_MTU_TRY_PROTO EQUAL error
+		{ yyerror("TCP, TLS, SCTP or UDP expected"); }
 	| cfg_var
 	| error EQUAL { yyerror("unknown config variable"); }
 	;
@@ -2556,6 +2568,10 @@ cmd:
 		#endif
 	}
 	| FORCE_TCP_ALIAS LPAREN error RPAREN	{$$=0; yyerror("bad argument, number expected"); }
+	| UDP_MTU_TRY_PROTO LPAREN proto RPAREN
+		{ $$=mk_action(UDP_MTU_TRY_PROTO_T, 1, NUMBER_ST, $3); }
+	| UDP_MTU_TRY_PROTO LPAREN error RPAREN
+		{ $$=0; yyerror("bad argument, UDP, TCP, TLS or SCTP expected"); }
 	| SET_ADV_ADDRESS LPAREN listen_id RPAREN {
 		$$=0;
 		if ((str_tmp=pkg_malloc(sizeof(str)))==0) {

+ 12 - 1
cfg_core.c

@@ -41,11 +41,12 @@
 #if defined PKG_MALLOC || defined SHM_MEM
 #include "pt.h"
 #endif
+#include "msg_translator.h" /* fix_global_req_flags() */
 #include "cfg/cfg.h"
 #include "cfg_core.h"
 
 struct cfg_group_core default_core_cfg = {
-	L_DEFAULT, /*  print only msg. < L_WARN */
+	L_WARN, 	/*  print only msg. < L_WARN */
 	LOG_DAEMON,	/* log_facility -- see syslog(3) */
 #ifdef USE_DST_BLACKLIST
 	/* blacklist */
@@ -89,6 +90,9 @@ struct cfg_group_core default_core_cfg = {
 	0, /* mem_dump_shm */
 #endif
 	DEFAULT_MAX_WHILE_LOOPS, /* max_while_loops */
+	0, /* udp_mtu (disabled by default) */
+	0, /* udp_mtu_try_proto -> default disabled */
+	0  /* force_rport */ 
 };
 
 void	*core_cfg = &default_core_cfg;
@@ -180,5 +184,12 @@ cfg_def_t core_cfg_def[] = {
 #endif
 	{"max_while_loops",	CFG_VAR_INT|CFG_ATOMIC,	0, 0, 0, 0,
 		"maximum iterations allowed for a while loop" },
+	{"udp_mtu",	CFG_VAR_INT|CFG_ATOMIC,	0, 65535, 0, 0,
+		"fallback to a congestion controlled protocol if send size"
+			" exceeds udp_mtu"},
+	{"udp_mtu_try_proto", CFG_VAR_INT, 1, 4, 0, fix_global_req_flags,
+		"if send size > udp_mtu use proto (1 udp, 2 tcp, 3 tls, 4 sctp)"},
+	{"force_rport",     CFG_VAR_INT, 0, 1,  0, fix_global_req_flags,
+		"force rport for all the received messages" },
 	{0, 0, 0, 0, 0, 0}
 };

+ 3 - 0
cfg_core.h

@@ -86,6 +86,9 @@ struct cfg_group_core {
 	int mem_dump_shm;
 #endif
 	int max_while_loops;
+	int udp_mtu; /**< maximum send size for udp, if > try another protocol*/
+	int udp_mtu_try_proto; /**< if packet> udp_mtu, try proto (e.g. TCP) */
+	int force_rport; /**< if set rport will always be forced*/
 };
 
 extern struct cfg_group_core default_core_cfg;

+ 6 - 0
config.h

@@ -73,6 +73,12 @@
 #define MY_VIA "Via: SIP/2.0/UDP "
 #define MY_VIA_LEN (sizeof(MY_VIA) - 1)
 
+#define ROUTE_PREFIX "Route: "
+#define ROUTE_PREFIX_LEN (sizeof(ROUTE_PREFIX) - 1)
+
+#define ROUTE_SEPARATOR ", "
+#define ROUTE_SEPARATOR_LEN (sizeof(ROUTE_SEPARATOR) - 1)
+
 #define CONTENT_LENGTH "Content-Length: "
 #define CONTENT_LENGTH_LEN (sizeof(CONTENT_LENGTH)-1)
 

+ 2 - 2
dns_cache.c

@@ -284,13 +284,13 @@ void fix_dns_flags(str *name)
 #ifdef DNS_SRV_LB
 		dns_flags|=DNS_SRV_RR_LB;
 #else
-		LOG(L_WARN, "WARING: fix_dns_flags: SRV loadbalaning is set, but"
+		LOG(L_WARN, "WARNING: fix_dns_flags: SRV loadbalaning is set, but"
 					" support for it is not compiled -- ignoring\n");
 #endif
 	}
 	if (cfg_get(core, core_cfg, dns_try_naptr)) {
 #ifndef USE_NAPTR
-	LOG(L_WARN, "WARING: fix_dns_flags: NAPTR support is enabled, but"
+	LOG(L_WARN, "WARNING: fix_dns_flags: NAPTR support is enabled, but"
 				" support for it is not compiled -- ignoring\n");
 #endif
 		dns_flags|=DNS_TRY_NAPTR;

+ 134 - 0
doc/logging-api.txt

@@ -0,0 +1,134 @@
+  _                      _                  _    ____ ___ 
+ | |    ___   __ _  __ _(_)_ __   __ _     / \  |  _ \_ _|
+ | |   / _ \ / _` |/ _` | | '_ \ / _` |   / _ \ | |_) | | 
+ | |__| (_) | (_| | (_| | | | | | (_| |  / ___ \|  __/| | 
+ |_____\___/ \__, |\__, |_|_| |_|\__, | /_/   \_\_|  |___|
+            |___/ |___/         |___/                    
+                         Ondrej Martinek <[email protected]>
+                                              January 2009
+
+This document contains the short description of the logging API in SER
+for developers.
+
+Source files:
+    dprint.h
+    dprint.c
+
+ Compile-time control macros
+=============================
+
+    NO_LOG
+	If defined, logging is completely disabled in SER and no messages
+        are produced at all
+	       
+    NO_DEBUG
+	If defined, logging messages do not include the source filename and
+	line location info
+
+ Logging levels
+================
+
+    L_DBG   ... Debugging message (the lowest level)
+    L_INFO  ... Info message
+    L_WARN  ... Warning message
+    L_ERR   ... Error message
+    L_CRIT  ... Critical message
+    L_ALERT ... Alert message (the highest level)
+
+    The levels are implemented as integer macros.
+
+ Related variables
+===================
+
+    debug
+	The config.framework setting that contains the current logging level.
+	The initial value can be specified by "debug" parameter in ser.cfg or
+	by -d options on the command-line.  The default value is L_WARN.
+
+    log_stderror
+	The global variable which specifies whether the log messages should be
+	send to the standard error output or syslog (equals to zero).
+	Its value can be specified by "log_stderr" parameter in ser.cfg or
+	-E option on the command-line.
+	
+    log_facility
+	The config.framework setting that contains the current facility for
+	logging to syslog.
+	The initial value can be specified by "log_facility" parameter in
+	ser.cfg.  The default value is LOG_DAEMON.
+
+ Macro functions
+=================
+
+    * short macro aliases:
+	DBG(FMT, ARGS...)   alias for LOG(L_DBG, FMT, ARGS...)
+        INFO(FMT, ARGS...)  alias for LOG(L_INFO, FMT, ARGS...)
+        WARN(FMT, ARGS...)  alias for LOG(L_WARN, FMT, ARGS...)
+        ERR(FMT, ARGS...)   alias for LOG(L_ERR, FMT, ARGS...)
+        BUG(FMT, ARGS...)   alias for LOG(L_CRIT, FMT, ARGS...)
+        ALERT(FMT, ARGS...) alias for LOG(L_ALERT, FMT, ARGS...)
+
+    * LOG(LEVEL, FMT, ARGS...) macro
+	Prints the log message on stderr or syslog if the current debug level
+	is greater or equal to LEVEL.  The message has the following format:
+
+          - for messages by core:
+              PROC(PID) LEVEL: <core> [FILE:LINE]: MESSAGE
+
+          - for messages by modules:
+              PROC(PID) LEVEL: MODULE [FILE:LINE]: MESSAGE
+	      
+          - for messages by log(), xlog(), xdbg() script funcitons:
+              PROC(PID) LEVEL: <script>: MESSAGE
+
+	PROC is the SER process number and PID is the linux process ID.
+        LEVEL is one of "DEBUG", "INFO", "NOTICE", "WARNING", "ERROR",
+	"ALERT" and "BUG" strings.  MESSAGE is constructed from printf-like
+	arguments FMT and ARGS.
+
+        [FILE:LINE] location info is not present if NO_DEBUG macro is defined.
+  
+	Use of shorter aliases is preferred if LEVEL is a preprocess-time
+	constant.
+	
+    * LOG_(LEVEL, PREFIX, FMT, ARGS...) macro
+	Prints the log message on stderr or syslog if the current debug level
+	is greater or equal to LEVEL.  The message has the following format:
+	
+              PROC(PID) LEVEL: PREFIXMESSAGE
+
+	This is an internal macro try to avoid using it.
+
+
+--------------------------------------------------------------------------------
+
+ APPENDIX: Summary of the changes to the original API
+======================================================
+
+  - LOG(LEVEL, FMT, ARGS...) and the short macro corresponding to LEVEL level
+    made eqvivalent (eg. LOG(L_DBG, FMT, ARGS...) and DBG(FMT, ARGS...) prints
+    always the same message)
+
+  - changed the format of log messages produced by the macros to include
+    the log level, module name, filename, line (if applicable)
+     
+  - added new, internal LOG_(LEVEL, PREFIX, FORMAT, ARGS...) macro
+
+  - removed DPrint() and DEBUG() macros, L_DEFAULT log level and dprint()
+    function
+
+!!!
+!!! IMPORTANT! READ ME!
+!!!
+!!!  These changes (mainly the first two) require reformating of the most log
+!!!  messages in SER core and module source files.  This step can be done
+!!!  automatically by running "scripts/logging/fix-logs-all" script BUT it
+!!!  was NOT originally performed because it would have generated too many
+!!!  changes in CVS which was discouraged by Andrei.  Instead, the developers
+!!!  are expected to run it when ready.
+!!!
+!!! IMPORTANT! READ ME!
+!!!  
+
+--
+$Id$

+ 28 - 28
dprint.c

@@ -29,48 +29,48 @@
  */
 
  
-#include "dprint.h"
 #include "globals.h"
-#include "pt.h"
+#include "dprint.h"
  
 #include <stdarg.h>
 #include <stdio.h>
 #include <strings.h>
 
-volatile int dprint_crit=0; /* signal protection: !=0 when dprint/LOG/DBG are
-								printing */
+#ifndef NO_SIG_DEBUG
+/* signal protection: !=0 when LOG/DBG/... are printing */
+volatile int dprint_crit = 0; 
+#endif
 
 static char* str_fac[]={"LOG_AUTH","LOG_CRON","LOG_DAEMON",
-					"LOG_KERN","LOG_LOCAL0","LOG_LOCAL1",
-					"LOG_LOCAL2","LOG_LOCAL3","LOG_LOCAL4","LOG_LOCAL5",
-					"LOG_LOCAL6","LOG_LOCAL7","LOG_LPR","LOG_MAIL",
-					"LOG_NEWS","LOG_USER","LOG_UUCP",
+			"LOG_KERN","LOG_LOCAL0","LOG_LOCAL1",
+			"LOG_LOCAL2","LOG_LOCAL3","LOG_LOCAL4","LOG_LOCAL5",
+			"LOG_LOCAL6","LOG_LOCAL7","LOG_LPR","LOG_MAIL",
+			"LOG_NEWS","LOG_USER","LOG_UUCP",
 #ifndef __OS_solaris
-					"LOG_AUTHPRIV","LOG_FTP","LOG_SYSLOG",
+			"LOG_AUTHPRIV","LOG_FTP","LOG_SYSLOG",
 #endif
-					0};
+			0};
+			
 static int int_fac[]={LOG_AUTH ,  LOG_CRON , LOG_DAEMON ,
-					LOG_KERN , LOG_LOCAL0 , LOG_LOCAL1 ,
-					LOG_LOCAL2 , LOG_LOCAL3 , LOG_LOCAL4 , LOG_LOCAL5 ,
-					LOG_LOCAL6 , LOG_LOCAL7 , LOG_LPR , LOG_MAIL ,
-					LOG_NEWS , LOG_USER , LOG_UUCP
+		      LOG_KERN , LOG_LOCAL0 , LOG_LOCAL1 ,
+		      LOG_LOCAL2 , LOG_LOCAL3 , LOG_LOCAL4 , LOG_LOCAL5 ,
+		      LOG_LOCAL6 , LOG_LOCAL7 , LOG_LPR , LOG_MAIL ,
+		      LOG_NEWS , LOG_USER , LOG_UUCP,
 #ifndef __OS_solaris
-					,LOG_AUTHPRIV,LOG_FTP,LOG_SYSLOG
+		      LOG_AUTHPRIV,LOG_FTP,LOG_SYSLOG,
 #endif
-					};
-
-
-void dprint(char * format, ...)
-{
-	va_list ap;
-
-	fprintf(stderr, "%2d(%d) ", process_no, my_pid());
-	va_start(ap, format);
-	vfprintf(stderr,format,ap);
-	fflush(stderr);
-	va_end(ap);
-}
+		      0};
 
+struct log_level_info log_level_info[] = {
+	{"ALERT", LOG_ALERT},	  /* L_ALERT */
+	{"CRITICAL", LOG_CRIT},   /* L_CRIT2 */
+	{"BUG", LOG_CRIT},        /* L_CRIT */
+	{"ERROR", LOG_ERR},       /* L_ERR */
+	{"WARNING", LOG_WARNING}, /* L_WARN */
+	{"NOTICE", LOG_NOTICE},   /* L_NOTICE */
+	{"INFO", LOG_INFO},       /* L_INFO */
+	{"DEBUG", LOG_DEBUG}	  /* L_DBG */
+};
 
 int str2facility(char *s)
 {

+ 174 - 202
dprint.h

@@ -25,242 +25,215 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 
-
-
 #ifndef dprint_h
 #define dprint_h
 
+#include <assert.h>
 #include <syslog.h>
-#include "cfg_core.h"
-
-
-#define L_ALERT -3
-#define L_CRIT  -2
-#define L_ERR   -1
-#define L_DEFAULT 0
-#define L_WARN   1
-#define L_NOTICE 2
-#define L_INFO   3
-#define L_DBG    4
-
-/* vars:*/
-
-extern int log_stderr;
-extern volatile int dprint_crit; /* protection against "simultaneous"
-									printing from signal handlers */
-
-#ifdef NO_SIG_DEBUG
-#define DPRINT_NON_CRIT		(1)
-#define DPRINT_CRIT_ENTER
-#define DPRINT_CRIT_EXIT
-#else
-#define DPRINT_NON_CRIT		(dprint_crit==0)
-#define DPRINT_CRIT_ENTER	(dprint_crit++)
-#define DPRINT_CRIT_EXIT	(dprint_crit--)
-#endif
-
-#define DPRINT_LEV	1
-/* priority at which we log */
-#define DPRINT_PRIO LOG_DEBUG
+#include <stdio.h> /* stderr, fprintf() */
 
+#include "cfg_core.h"
 
-void dprint (char* format, ...);
-
-int str2facility(char *s);
-int log_facility_fixup(void *handle, str *name, void **val);
 
 /* C >= 99 has __func__, older gcc versions have __FUNCTION__ */
 #if __STDC_VERSION__ < 199901L
-# if __GNUC__ >= 2
-#  define _FUNC_NAME_ __FUNCTION__
-# else
-#  define _FUNC_NAME_ ""
-# endif
+#	if __GNUC__ >= 2
+#		define _FUNC_NAME_ __FUNCTION__
+#	else
+#		define _FUNC_NAME_ ""
+#	endif
 #else
-# define _FUNC_NAME_ __func__
+#	define _FUNC_NAME_ __func__
 #endif
 
+#ifdef NO_DEBUG
+#	ifdef MOD_NAME
+#		define LOC_INFO		MOD_NAME ": "
+#	else
+#		define LOC_INFO		"<core>: "
+#	endif
+#else
+#	define XCT2STR(i) #i
+#	define CT2STR(l)  XCT2STR(l)
+#
+#	ifdef MOD_NAME
+#		define LOC_INFO		MOD_NAME " [" __FILE__ ":" CT2STR(__LINE__) "]: "
+#	else
+#		define LOC_INFO		"<core> [" __FILE__ ":" CT2STR(__LINE__) "]: "
+#	endif
+#
+#	ifdef NO_LOG
+#		undef NO_LOG
+#	endif
+#endif /* NO_DEBUG */
 
-#define XCT2STR(i) #i
-#define CT2STR(l) XCT2STR(l)
 
-#define LOC_INFO	__FILE__ ":" CT2STR(__LINE__) ": "
+/*
+ * Log levels
+ */
+#define L_ALERT		-4
+#define L_CRIT2		-3  /* like L_CRIT, but not used for BUGs */
+#define L_CRIT  	-2  /* used only for BUG */
+#define L_ERR   	-1
+#define L_WARN   	0
+#define L_NOTICE 	1
+#define L_INFO   	2
+#define L_DBG    	3
+
+#define LOG_LEVEL2NAME(level)	(log_level_info[(level) - (L_ALERT)].name)
+#define LOG2SYSLOG_LEVEL(level) \
+	(log_level_info[(level) - (L_ALERT)].syslog_level)
+
+
+/* my_pid(), process_no are from pt.h but we cannot #include it here
+   because of circular dependencies */
+extern int process_no;
+extern int my_pid();
+
+/* non-zero if logging to stderr instead to the syslog */
+extern int log_stderr;
 
+/* maps log levels to their string name and corresponding syslog level */
 
-#define is_printable(level) (cfg_get(core, core_cfg, debug)>=(level))
+struct log_level_info {
+ 	char *name;
+	int syslog_level;
+};
 
-#ifdef NO_DEBUG
-	#ifdef __SUNPRO_C
-		#define DPrint(...)
-	#else
-		#define DPrint(fmt, args...)
-	#endif
-#else
-	#ifdef __SUNPRO_C
-		#define DPrint( ...) \
-			do{ \
-				if ((cfg_get(core, core_cfg, debug)>=DPRINT_LEV) && DPRINT_NON_CRIT){ \
-					DPRINT_CRIT_ENTER; \
-					if (log_stderr){ \
-						dprint (__VA_ARGS__); \
-					}else{ \
-						syslog(DPRINT_LEV|cfg_get(core, core_cfg, log_facility), \
-							__VA_ARGS__); \
-					}\
-					DPRINT_CRIT_EXIT; \
-				} \
-			}while(0)
-	#else
-			#define DPrint(fmt,args...) \
-			do{ \
-				if ((cfg_get(core, core_cfg, debug)>=DPRINT_LEV) && DPRINT_NON_CRIT){ \
-					DPRINT_CRIT_ENTER; \
-					if (log_stderr){ \
-						dprint (fmt, ## args); \
-					}else{ \
-						syslog(DPRINT_LEV|cfg_get(core, core_cfg, log_facility), \
-							fmt, ## args); \
-					}\
-					DPRINT_CRIT_EXIT; \
-				} \
-			}while(0)
-	#endif
+#define is_printable(level) (cfg_get(core, core_cfg, debug)>=(level))
+extern struct log_level_info log_level_info[];
 
+#ifndef NO_SIG_DEBUG
+/* protection against "simultaneous" printing from signal handlers */
+extern volatile int dprint_crit; 
 #endif
 
-#ifndef NO_DEBUG
-	#undef NO_LOG
-#endif
+int str2facility(char *s);
+int log_facility_fixup(void *handle, str *name, void **val);
 
+
+/*
+ * General logging macros
+ *
+ * LOG_(level, prefix, fmt, ...) prints "printf"-formatted log message to
+ * stderr (if `log_stderr' is non-zero) or to syslog.  Note that `fmt' must
+ * be constant. `prefix' is added to the beginning of the message.
+ *
+ * LOG(level, fmt, ...) is same as LOG_() with LOC_INFO prefix.
+ */
 #ifdef NO_LOG
-	#ifdef __SUNPRO_C
-		#define LOG(lev, ...)
-	#else
-		#define LOG(lev, fmt, args...)
-	#endif
+
+#	ifdef __SUNPRO_C
+#		define LOG_(level, prefix, fmt, ...)
+#		define LOG(level, fmt, ...)
+#	else
+#		define LOG_(level, prefix, fmt, args...)
+#		define LOG(level, fmt, args...)
+#	endif
+
 #else
-	#ifdef __SUNPRO_C
-		#define LOG(lev, ...) \
+
+#	ifdef NO_SIG_DEBUG
+#		define DPRINT_NON_CRIT		(1)
+#		define DPRINT_CRIT_ENTER
+#		define DPRINT_CRIT_EXIT
+#	else
+#		define DPRINT_NON_CRIT		(dprint_crit==0)
+#		define DPRINT_CRIT_ENTER	(dprint_crit++)
+#		define DPRINT_CRIT_EXIT		(dprint_crit--)
+#	endif
+
+#	ifdef __SUNPRO_C
+#		define LOG_(level, prefix, fmt, ...) \
 			do { \
-				if ((cfg_get(core, core_cfg, debug)>=(lev)) && DPRINT_NON_CRIT){ \
+				if (cfg_get(core, core_cfg, debug) >= (level) && \
+						DPRINT_NON_CRIT) { \
 					DPRINT_CRIT_ENTER; \
-					if (log_stderr) dprint (__VA_ARGS__); \
-					else { \
-						switch(lev){ \
-							case L_CRIT: \
-								syslog(LOG_CRIT|cfg_get(core, core_cfg, log_facility), \
-									__VA_ARGS__); \
-								break; \
-							case L_ALERT: \
-								syslog(LOG_ALERT|cfg_get(core, core_cfg, log_facility), \
-									__VA_ARGS__); \
-								break; \
-							case L_ERR: \
-								syslog(LOG_ERR|cfg_get(core, core_cfg, log_facility), \
-									__VA_ARGS__); \
-								break; \
-							case L_WARN: \
-								syslog(LOG_WARNING|cfg_get(core, core_cfg, log_facility), \
-									__VA_ARGS__);\
-								break; \
-							case L_NOTICE: \
-								syslog(LOG_NOTICE|cfg_get(core, core_cfg, log_facility), \
-									__VA_ARGS__); \
-								break; \
-							case L_INFO: \
-								syslog(LOG_INFO|cfg_get(core, core_cfg, log_facility), \
-									__VA_ARGS__); \
-								break; \
-							case L_DBG: \
-								syslog(LOG_DEBUG|cfg_get(core, core_cfg, log_facility), \
-									__VA_ARGS__); \
-								break; \
-						} \
+					assert(((level) >= L_ALERT) && ((level) <= L_DBG)); \
+					if (log_stderr) { \
+						fprintf(stderr, "%2d(%d) %s: %s" fmt, \
+								process_no, my_pid(), LOG_LEVEL2NAME(level),\
+								(prefix), __VA_ARGS__); \
+					} else { \
+						syslog(LOG2SYSLOG_LEVEL(level) | \
+									cfg_get(core, core_cfg, log_facility), \
+								"%s: %s" fmt, LOG_LEVEL2NAME(level),\
+								(prefix), __VA_ARGS__); \
 					} \
 					DPRINT_CRIT_EXIT; \
 				} \
-			}while(0)
-	#else
-		#define LOG(lev, fmt, args...) \
+			} while(0)
+			
+#		define LOG(level, fmt, ...)  LOG_((level), LOC_INFO, fmt, __VA_ARGS__)
+
+#	else
+#		define LOG_(level, prefix, fmt, args...) \
 			do { \
-				if ((cfg_get(core, core_cfg, debug)>=(lev)) && DPRINT_NON_CRIT){ \
+				if (cfg_get(core, core_cfg, debug) >= (level) && \
+						DPRINT_NON_CRIT) { \
 					DPRINT_CRIT_ENTER; \
-					if (log_stderr) dprint (fmt, ## args); \
-					else { \
-						switch(lev){ \
-							case L_CRIT: \
-								syslog(LOG_CRIT|cfg_get(core, core_cfg, log_facility), \
-									fmt, ##args); \
-								break; \
-							case L_ALERT: \
-								syslog(LOG_ALERT|cfg_get(core, core_cfg, log_facility), \
-									fmt, ##args); \
-								break; \
-							case L_ERR: \
-								syslog(LOG_ERR|cfg_get(core, core_cfg, log_facility), \
-									fmt, ##args); \
-								break; \
-							case L_WARN: \
-								syslog(LOG_WARNING|cfg_get(core, core_cfg, log_facility), \
-									fmt, ##args);\
-								break; \
-							case L_NOTICE: \
-								syslog(LOG_NOTICE|cfg_get(core, core_cfg, log_facility), \
-									fmt, ##args); \
-								break; \
-							case L_INFO: \
-								syslog(LOG_INFO|cfg_get(core, core_cfg, log_facility), \
-									fmt, ##args); \
-								break; \
-							case L_DBG: \
-								syslog(LOG_DEBUG|cfg_get(core, core_cfg, log_facility), \
-									fmt, ##args); \
-								break; \
-						} \
+					assert(((level) >= L_ALERT) && ((level) <= L_DBG)); \
+					if (log_stderr) { \
+						fprintf(stderr, "%2d(%d) %s: %s" fmt, \
+								process_no, my_pid(), LOG_LEVEL2NAME(level),\
+								(prefix), ## args); \
+					} else { \
+						syslog(LOG2SYSLOG_LEVEL(level) |\
+									cfg_get(core, core_cfg, log_facility), \
+						 		"%s: %s" fmt, LOG_LEVEL2NAME(level),\
+								(prefix), ## args); \
 					} \
 					DPRINT_CRIT_EXIT; \
 				} \
-			}while(0)
-	#endif /*SUN_PRO_C*/
-#endif
-
+			} while(0)
+			
+#		define LOG(level, fmt, args...)  LOG_((level), LOC_INFO, fmt, ## args)
+		
+#	endif /* __SUNPRO_C */
+#endif /* NO_LOG */
 
-#ifdef NO_DEBUG
-	#ifdef __SUNPRO_C
-		#define DBG(...)
-	#else
-		#define DBG(fmt, args...)
-	#endif
-#else
-	#ifdef __SUNPRO_C
-		#define DBG(...) LOG(L_DBG, __VA_ARGS__)
-	#else
-		#define DBG(fmt, args...) LOG(L_DBG, fmt, ## args)
-	#endif
-#endif
 
+/*
+ * Simplier, prefered logging macros for constant log level
+ */
 #ifdef __SUNPRO_C
-		#define DEBUG(...) DBG("DEBUG: "          LOC_INFO __VA_ARGS__)
-		#define ERR(...)  LOG(L_ERR, "ERROR: "    LOC_INFO __VA_ARGS__)
-		#define WARN(...) LOG(L_WARN, "WARNING: " LOC_INFO __VA_ARGS__)
-		#define INFO(...) LOG(L_INFO, "INFO: "    LOC_INFO __VA_ARGS__)
-		#define BUG(...) LOG(L_CRIT, "BUG: "      LOC_INFO __VA_ARGS__)
-		#define NOTICE(...) LOG(L_NOTICE, "NOTICE: " LOC_INFO __VA_ARGS__)
-		#define ALERT(...) LOG(L_ALERT, "ALERT: " LOC_INFO __VA_ARGS__)
-		#define CRIT(...) LOG(L_CRIT, "CRITICAL: " LOC_INFO __VA_ARGS__)
-#else
-		#define DEBUG(fmt, args...) DBG("DEBUG: "       LOC_INFO fmt, ## args)
-		#define ERR(fmt, args...) LOG(L_ERR, "ERROR: "  LOC_INFO fmt, ## args)
-		#define WARN(fmt, args...) LOG(L_WARN, "WARN: " LOC_INFO fmt, ## args)
-		#define INFO(fmt, args...) LOG(L_INFO, "INFO: " LOC_INFO fmt, ## args)
-		#define BUG(fmt, args...) LOG(L_CRIT, "BUG: "   LOC_INFO fmt, ## args)
-		#define NOTICE(fmt, args...) \
-			LOG(L_NOTICE, "NOTICE: " LOC_INFO fmt, ## args)
-		#define ALERT(fmt, args...) \
-			LOG(L_ALERT, "ALERT: " LOC_INFO fmt, ## args)
-		#define CRIT(fmt, args...) \
-			LOG(L_CRIT, "CRITICAL: " LOC_INFO fmt, ## args)
-#endif
+#	define ALERT(...)  LOG(L_ALERT,  __VA_ARGS__)
+#	define BUG(...)    LOG(L_CRIT,   __VA_ARGS__)
+#	define ERR(...)    LOG(L_ERR,    __VA_ARGS__)
+#	define WARN(...)   LOG(L_WARN,   __VA_ARGS__)
+#	define NOTICE(...) LOG(L_NOTICE, __VA_ARGS__)
+#	define INFO(...)   LOG(L_INFO,   __VA_ARGS__)
+#	define CRIT(...)    LOG(L_CRIT2,   __VA_ARGS__)
+
+#	ifdef NO_DEBUG
+#		define DBG(...)
+#	else
+#		define DBG(...)    LOG(L_DBG, __VA_ARGS__)
+#	endif		
+
+/* obsolete, do not use */
+#	define DEBUG(...) DBG(__VA_ARGS__)
+
+#else /* ! __SUNPRO_C */
+#	define ALERT(fmt, args...)  LOG(L_ALERT,  fmt, ## args)
+#	define BUG(fmt, args...)    LOG(L_CRIT,   fmt, ## args)
+#	define ERR(fmt, args...)    LOG(L_ERR,    fmt, ## args)
+#	define WARN(fmt, args...)   LOG(L_WARN,   fmt, ## args)
+#	define NOTICE(fmt, args...) LOG(L_NOTICE, fmt, ## args)
+#	define INFO(fmt, args...)   LOG(L_INFO,   fmt, ## args)
+#	define CRIT(fmt, args...)   LOG(L_CRIT2,   fmt, ## args)
+
+#	ifdef NO_DEBUG
+#		define DBG(fmt, args...)
+#	else
+#		define DBG(fmt, args...)    LOG(L_DBG, fmt, ## args)
+#	endif		
+
+/* obsolete, do not use */
+#	define DEBUG(fmt, args...) DBG(fmt, ## args)
+		
+#endif /* __SUNPRO_C */
+
 
 /* kamailio/openser compatibility */
 
@@ -274,5 +247,4 @@ int log_facility_fixup(void *handle, str *name, void **val);
 #define LM_INFO INFO
 #define LM_DBG DEBUG
 
-
-#endif /* ifndef dprint_h */
+#endif /* !dprint_h */

+ 8 - 6
forward.c

@@ -342,6 +342,7 @@ int forward_request(struct sip_msg* msg, str* dst, unsigned short port,
 	struct socket_info* orig_send_sock; /* initial send_sock */
 	int ret;
 	struct ip_addr ip; /* debugging only */
+	char proto;
 #ifdef USE_DNS_FAILOVER
 	struct socket_info* prev_send_sock;
 	int err;
@@ -354,6 +355,7 @@ int forward_request(struct sip_msg* msg, str* dst, unsigned short port,
 	
 	buf=0;
 	orig_send_sock=send_info->send_sock;
+	proto=send_info->proto;
 	ret=0;
 
 	if(dst){
@@ -361,7 +363,7 @@ int forward_request(struct sip_msg* msg, str* dst, unsigned short port,
 		if (cfg_get(core, core_cfg, use_dns_failover)){
 			dns_srv_handle_init(&dns_srv_h);
 			err=dns_sip_resolve2su(&dns_srv_h, &send_info->to, dst, port,
-									&send_info->proto, dns_flags);
+									&proto, dns_flags);
 			if (err!=0){
 				LOG(L_ERR, "ERROR: forward_request: resolving \"%.*s\""
 						" failed: %s [%d]\n", dst->len, ZSW(dst->s),
@@ -371,7 +373,7 @@ int forward_request(struct sip_msg* msg, str* dst, unsigned short port,
 			}
 		}else
 #endif
-		if (sip_hostport2su(&send_info->to, dst, port, &send_info->proto)<0){
+		if (sip_hostport2su(&send_info->to, dst, port, &proto)<0){
 			LOG(L_ERR, "ERROR: forward_request: bad host name %.*s,"
 						" dropping packet\n", dst->len, ZSW(dst->s));
 			ret=E_BAD_ADDRESS;
@@ -410,12 +412,11 @@ int forward_request(struct sip_msg* msg, str* dst, unsigned short port,
 	do{
 #endif
 		if (orig_send_sock==0) /* no forced send_sock => find it **/
-			send_info->send_sock=get_send_socket(msg, &send_info->to,
-												send_info->proto);
+			send_info->send_sock=get_send_socket(msg, &send_info->to, proto);
 		if (send_info->send_sock==0){
 			LOG(L_ERR, "forward_req: ERROR: cannot forward to af %d, proto %d "
 						"no corresponding listening socket\n",
-						send_info->to.s.sa_family, send_info->proto);
+						send_info->to.s.sa_family, proto);
 			ret=ser_error=E_NO_SOCKET;
 #ifdef USE_DNS_FAILOVER
 			/* continue, maybe we find a socket for some other ip */
@@ -431,6 +432,7 @@ int forward_request(struct sip_msg* msg, str* dst, unsigned short port,
 			prev_send_sock=send_info->send_sock;
 #endif
 			if (buf) pkg_free(buf);
+			send_info->proto=proto;
 			buf = build_req_buf_from_sip_req(msg, &len, send_info);
 			if (!buf){
 				LOG(L_ERR, "ERROR: forward_request: building failed\n");
@@ -496,7 +498,7 @@ int forward_request(struct sip_msg* msg, str* dst, unsigned short port,
 	}while(dst && cfg_get(core, core_cfg, use_dns_failover) &&
 			dns_srv_handle_next(&dns_srv_h, err) && 
 			((err=dns_sip_resolve2su(&dns_srv_h, &send_info->to, dst, port,
-								  &send_info->proto, dns_flags))==0));
+										&proto, dns_flags))==0));
 	if ((err!=0) && (err!=-E_DNS_EOR)){
 		LOG(L_ERR, "ERROR:  resolving %.*s host name in uri"
 							" failed: %s [%d] (dropping packet)\n",

+ 8 - 8
main.c

@@ -642,7 +642,7 @@ void handle_sigs()
 				DBG("SIGTERM received, program terminates\n");
 			/* shutdown/kill all the children */
 			shutdown_children(SIGTERM, 1);
-			dprint("Thank you for flying " NAME "\n");
+			LOG(L_NOTICE, "Thank you for flying " NAME "\n");
 			exit(0);
 			break;
 
@@ -768,32 +768,32 @@ int install_sigs()
 {
 	/* added by jku: add exit handler */
 	if (set_sig_h(SIGINT, sig_usr) == SIG_ERR ) {
-		DPrint("ERROR: no SIGINT signal handler can be installed\n");
+		ERR("no SIGINT signal handler can be installed\n");
 		goto error;
 	}
 	/* if we debug and write to a pipe, we want to exit nicely too */
 	if (set_sig_h(SIGPIPE, sig_usr) == SIG_ERR ) {
-		DPrint("ERROR: no SIGINT signal handler can be installed\n");
+		ERR("no SIGINT signal handler can be installed\n");
 		goto error;
 	}
 	if (set_sig_h(SIGUSR1, sig_usr)  == SIG_ERR ) {
-		DPrint("ERROR: no SIGUSR1 signal handler can be installed\n");
+		ERR("no SIGUSR1 signal handler can be installed\n");
 		goto error;
 	}
 	if (set_sig_h(SIGCHLD , sig_usr)  == SIG_ERR ) {
-		DPrint("ERROR: no SIGCHLD signal handler can be installed\n");
+		ERR("no SIGCHLD signal handler can be installed\n");
 		goto error;
 	}
 	if (set_sig_h(SIGTERM , sig_usr)  == SIG_ERR ) {
-		DPrint("ERROR: no SIGTERM signal handler can be installed\n");
+		ERR("no SIGTERM signal handler can be installed\n");
 		goto error;
 	}
 	if (set_sig_h(SIGHUP , sig_usr)  == SIG_ERR ) {
-		DPrint("ERROR: no SIGHUP signal handler can be installed\n");
+		ERR("no SIGHUP signal handler can be installed\n");
 		goto error;
 	}
 	if (set_sig_h(SIGUSR2 , sig_usr)  == SIG_ERR ) {
-		DPrint("ERROR: no SIGUSR2 signal handler can be installed\n");
+		ERR("no SIGUSR2 signal handler can be installed\n");
 		goto error;
 	}
 	return 0;

+ 57 - 9
modules/tm/README

@@ -74,9 +74,11 @@ Jiri Kuthan
         1.4.23. t_any_replied() 
         1.4.24. t_grep_status("code") 
         1.4.25. t_is_canceled() 
-        1.4.26. t_relay_cancel() 
-        1.4.27. t_drop_replies() 
-        1.4.28. t_save_lumps() 
+        1.4.26. t_is_expired() 
+        1.4.27. t_relay_cancel() 
+        1.4.28. t_lookup_cancel(), t_lookup_cancel(1) 
+        1.4.29. t_drop_replies() 
+        1.4.30. t_save_lumps() 
 
    1.5. TM Module API
 
@@ -1154,7 +1156,21 @@ failure_route[0]{
         }
 }
 
-1.4.26.  t_relay_cancel()
+1.4.26.  t_is_expired()
+
+   Returns true if the current transaction has already been expired, i.e.
+   the max_inv_lifetime/max_noninv_lifetime interval has already elapsed.
+
+   Example 50. t_is_expired usage
+...
+failure_route[0]{
+        if (t_is_expired()){
+                log("transaction expired\n");
+                # There is no point in adding a new branch.
+        }
+}
+
+1.4.27.  t_relay_cancel()
 
    Forwards the CANCEL if the corresponding INVITE transaction exists.
    The function is supposed to be used at the very beginning of the
@@ -1166,7 +1182,7 @@ failure_route[0]{
    CANCELs were successfully sent to the pending branches, true if the
    INVITE was not found, and false in case of any error.
 
-   Example 50. t_relay_cancel usage
+   Example 51. t_relay_cancel usage
 if (method == CANCEL) {
         if (!t_relay_cancel()) {  # implicit drop if relaying was successful,
                                   # nothing to do
@@ -1179,14 +1195,46 @@ if (method == CANCEL) {
         # do the same as for INVITEs
 }
 
-1.4.27.  t_drop_replies()
+1.4.28.  t_lookup_cancel(), t_lookup_cancel(1)
+
+   Returns true if the corresponding INVITE transaction exists for a
+   CANCEL request. The function can be called at the beginning of the
+   script to check whether or not the CANCEL can be immediately forwarded
+   bypassing the rest of the script. Note however that t_relay_cancel
+   includes t_lookup_cancel as well, therefore it is not needed to
+   explicitly call this function unless something has to be logged for
+   example.
+
+   If the function parameter (optional) is set to 1, the message flags
+   are overwritten with the flags of the INVITE. isflagset() can be used
+   to check the flags of the previously forwarded INVITE in this case.
+
+   Example 52. t_lookup_cancel usage
+if (method == CANCEL) {
+        if (t_lookup_cancel()) {
+                log("INVITE transaction exists");
+                if (!t_relay_cancel()) {  # implicit drop if
+                                          # relaying was successful,
+                                          # nothing to do
+
+                        # corresponding INVITE transaction found
+                        # but error occurred
+                        sl_reply("500", "Internal Server Error");
+                        drop;
+                }
+        }
+        # bad luck, corresponding INVITE transaction is missing,
+        # do the same as for INVITEs
+}
+
+1.4.29.  t_drop_replies()
 
    Drops all the previously received replies in failure_route block to
    make sure that none of them is picked up again. Works only if a new
    branch is added to the transaction, or it is explicitly replied in the
    script!
 
-   Example 51. t_drop_replies() usage
+   Example 53. t_drop_replies() usage
 ...
 failure_route[0]{
         if (t_check_status("5[0-9][0-9]")){
@@ -1202,7 +1250,7 @@ failure_route[0]{
         }
 }
 
-1.4.28.  t_save_lumps()
+1.4.30.  t_save_lumps()
 
    Forces the modifications of the processed SIP message to be saved in
    shared memory before t_relay() is called. The new branches which are
@@ -1217,7 +1265,7 @@ failure_route[0]{
    The transaction must be created by t_newtran() before calling
    t_save_lumps().
 
-   Example 52. t_save_lumps() usage
+   Example 54. t_save_lumps() usage
 route {
         ...
         t_newtran();

+ 33 - 28
modules/tm/dlg.c

@@ -54,12 +54,6 @@
 #define NORMAL_ORDER 0  /* Create route set in normal order - UAS */
 #define REVERSE_ORDER 1 /* Create route set in reverse order - UAC */
 
-#define ROUTE_PREFIX "Route: "
-#define ROUTE_PREFIX_LEN (sizeof(ROUTE_PREFIX) - 1)
-
-#define ROUTE_SEPARATOR "," CRLF "       "
-#define ROUTE_SEPARATOR_LEN (sizeof(ROUTE_SEPARATOR) - 1)
-
 
 #ifdef DIALOG_CALLBACKS
 
@@ -221,11 +215,17 @@ static inline int str_duplicate(str* _d, str* _s)
 
 /*
  * Calculate dialog hooks
+ * @return:
+ *  negative : error
+ *  0 : no routes present
+ *  F_RB_NH_LOOSE : routes present, next hop is loose router
+ *  F_RB_NH_STRICT: next hop is strict.
  */
 static inline int calculate_hooks(dlg_t* _d)
 {
 	str* uri;
 	struct sip_uri puri;
+	int nhop;
 
 	/* we might re-calc. some existing hooks =>
 	 * reset all the hooks to 0 */
@@ -242,6 +242,7 @@ static inline int calculate_hooks(dlg_t* _d)
 			else _d->hooks.request_uri = &_d->rem_uri;
 			_d->hooks.next_hop = &_d->route_set->nameaddr.uri;
 			_d->hooks.first_route = _d->route_set;
+			nhop = F_RB_NH_LOOSE;
 		} else {
 			_d->hooks.request_uri = &_d->route_set->nameaddr.uri;
 			_d->hooks.next_hop = _d->hooks.request_uri;
@@ -250,6 +251,7 @@ static inline int calculate_hooks(dlg_t* _d)
 				_d->hooks.last_route = &_d->rem_target;
 			else 
 				_d->hooks.last_route = NULL; /* ? */
+			nhop = F_RB_NH_STRICT;
 		}
 	} else {
 		if (_d->rem_target.s) _d->hooks.request_uri = &_d->rem_target;
@@ -258,12 +260,14 @@ static inline int calculate_hooks(dlg_t* _d)
 		if (_d->dst_uri.s) _d->hooks.next_hop = &_d->dst_uri;
 		else _d->hooks.next_hop = _d->hooks.request_uri;
 
+		nhop = 0;
 		/*
-		 * the routes in the hooks need to be reset because if the route_set was dropped somewhere else
-		 * then these will remain set without the actual routes existing any more
+		 * the routes in the hooks need to be reset because if the route_set 
+		 * was dropped somewhere else then these will remain set without the
+		 * actual routes existing any more
 		 */
 		_d->hooks.first_route = 0;
-		_d->hooks.last_route = 0; 
+		_d->hooks.last_route = 0;
 	}
 
 	if ((_d->hooks.request_uri) && (_d->hooks.request_uri->s) && (_d->hooks.request_uri->len)) {
@@ -279,7 +283,7 @@ static inline int calculate_hooks(dlg_t* _d)
 		get_raw_uri(_d->hooks.next_hop);
 	}
 
-	return 0;
+	return nhop;
 }
 
 /*
@@ -744,7 +748,8 @@ static inline int dlg_confirmed_resp_uac(dlg_t* _d, struct sip_msg* _m,
 			if (str_duplicate(&_d->rem_target, &contact) < 0) return -4;
 		}
 
-		calculate_hooks(_d);
+		if (calculate_hooks(_d) < 0)
+			return -1;
 	}
 
 	return 0;
@@ -1084,7 +1089,8 @@ int dlg_request_uas(dlg_t* _d, struct sip_msg* _m, target_refresh_t is_target_re
 			if (str_duplicate(&_d->rem_target, &contact) < 0) return -6;
 		}
 
-		calculate_hooks(_d);
+		if (calculate_hooks(_d) < 0)
+			return -1;
 		
 	}
 
@@ -1098,31 +1104,30 @@ int dlg_request_uas(dlg_t* _d, struct sip_msg* _m, target_refresh_t is_target_re
 int calculate_routeset_length(dlg_t* _d)
 {
 	int len;
-	rr_t* ptr;
+	rr_t *ptr;
 
-	len = 0;
-	ptr = _d->hooks.first_route;
+	if (! _d->route_set)
+		return 0;
 
-	if (ptr) {
-		len = ROUTE_PREFIX_LEN;
-		len += CRLF_LEN;
-	}
+	len = ROUTE_PREFIX_LEN;
 
-	while(ptr) {
+	for (ptr = _d->hooks.first_route; ptr; ptr = ptr->next) {
 		len += ptr->len;
-		ptr = ptr->next;
-		if (ptr) len += ROUTE_SEPARATOR_LEN;
-	} 
-
+		len += ROUTE_SEPARATOR_LEN;
+	}
 	if (_d->hooks.last_route) {
-		len += ROUTE_SEPARATOR_LEN + 2; /* < > */
-		len += _d->hooks.last_route->len;
+		if (_d->hooks.first_route)
+			len += ROUTE_SEPARATOR_LEN;
+		len += _d->hooks.last_route->len + 2; /* < > */
+	} else {
+		len -= ROUTE_SEPARATOR_LEN;
 	}
 
+	len += CRLF_LEN;
+
 	return len;
 }
 
-
 /*
  *
  * Print the route set
@@ -1255,7 +1260,7 @@ int set_dlg_target(dlg_t* _d, str* _ruri, str* _duri) {
 		if (str_duplicate(&_d->dst_uri, _duri)) return -1;
 	}
 
-	if (calculate_hooks(_d)) {
+	if (calculate_hooks(_d) < 0) {
 		LOG(L_ERR, "set_dlg_target(): Error while calculating hooks\n");
 		return -1;
 	}

+ 67 - 0
modules/tm/doc/functions.xml

@@ -841,6 +841,29 @@ failure_route[0]{
 	</example>
 	</section>
 
+	<section id="t_is_expired">
+	<title>
+	    <function>t_is_expired()</function>
+	</title>
+	<para>
+		Returns true if the current transaction has already been expired,
+		i.e. the max_inv_lifetime/max_noninv_lifetime interval has already
+		elapsed.
+	</para>
+	<example>
+	    <title><function>t_is_expired</function> usage</title>
+	    <programlisting>
+...
+failure_route[0]{ 
+	if (t_is_expired()){
+		log("transaction expired\n");
+		# There is no point in adding a new branch.
+	}
+} 
+	    </programlisting>
+	</example>
+	</section>
+
     <section id="t_relay_cancel">
 	<title>
 	    <function>t_relay_cancel()</function>
@@ -877,6 +900,50 @@ if (method == CANCEL) {
 	</example>
     </section>
 
+    <section id="t_lookup_cancel">
+	<title>
+	    <function>t_lookup_cancel()</function>,
+	    <function>t_lookup_cancel(1)</function>
+	</title>
+	<para>
+		Returns true if the corresponding INVITE transaction exists
+		for a CANCEL request. The function can be called at the beginning
+		of the script to check whether or not the CANCEL can be immediately
+		forwarded bypassing the rest of the script. Note however that
+		<function>t_relay_cancel</function> includes
+		<function>t_lookup_cancel</function> as well, therefore it is not
+		needed to explicitly call this function unless something has to be
+		logged for example.
+	</para>
+	<para>
+		If the function parameter (optional) is set to 1, the message flags
+		are overwritten with the flags of the INVITE. isflagset() can be used
+		to check the flags of the previously forwarded INVITE in this case.
+	</para>
+	<example>
+	    <title><function>t_lookup_cancel</function> usage</title>
+	    <programlisting>
+
+if (method == CANCEL) {
+	if (t_lookup_cancel()) {
+		log("INVITE transaction exists");
+		if (!t_relay_cancel()) {  # implicit drop if
+                                          # relaying was successful,
+                                          # nothing to do
+
+			# corresponding INVITE transaction found
+			# but error occurred
+			sl_reply("500", "Internal Server Error");
+			drop;
+		}
+	}
+	# bad luck, corresponding INVITE transaction is missing,
+	# do the same as for INVITEs
+}
+	    </programlisting>
+	</example>
+    </section>
+
     <section id="t_drop_replies">
 	<title>
 	    <function>t_drop_replies()</function>

+ 6 - 0
modules/tm/h_table.c

@@ -65,6 +65,7 @@
 #include "h_table.h"
 #include "fix_lumps.h" /* free_via_clen_lump */
 #include "timer.h"
+#include "uac.h" /* free_local_ack */
 
 
 static enum kill_reason kr;
@@ -174,6 +175,11 @@ void free_cell( struct cell* dead_cell )
 #endif
 	}
 
+#ifdef WITH_AS_SUPPORT
+	if (dead_cell->uac[0].local_ack)
+		free_local_ack_unsafe(dead_cell->uac[0].local_ack);
+#endif
+
 	/* collected to tags */
 	tt=dead_cell->fwded_totags;
 	while(tt) {

+ 23 - 2
modules/tm/h_table.h

@@ -143,6 +143,10 @@ enum kill_reason { REQ_FWDED=1, REQ_RPLD=2, REQ_RLSD=4, REQ_EXIST=8,
 #define F_RB_REPLIED	0x20 /* reply received */
 #define F_RB_CANCELED	0x40 /* rb/branch canceled */
 #define F_RB_DEL_TIMER	0x80 /* timer should be deleted if active */
+#define F_RB_NH_LOOSE	0x100 /* next hop is a loose router */
+#define F_RB_NH_STRICT	0x200 /* next hop is a strict router */
+/* must detect when neither loose nor strict flag is set -> two flags.
+ * alternatively, 1x flag for strict/loose and 1x for loose|strict set/not */
 
 
 /* if canceled or intended to be canceled, return true */
@@ -154,10 +158,10 @@ typedef struct retr_buf
 	short activ_type;
 	/* set to status code if the buffer is a reply,
 	0 if request or -1 if local CANCEL */
-	volatile unsigned char flags; /* DISABLED, T2 */
+	volatile unsigned short flags; /* DISABLED, T2 */
 	volatile unsigned char t_active; /* timer active */
 	unsigned short branch; /* no more then 65k branches :-) */
-	short   buffer_len;
+	short buffer_len;
 	char *buffer;
 	/*the cell that contains this retrans_buff*/
 	struct cell* my_T;
@@ -207,6 +211,16 @@ typedef struct ua_client
 	str              uri;
 	/* if we don't store, we at least want to know the status */
 	int             last_received;
+#ifdef WITH_AS_SUPPORT
+	/**
+	 * Resent for every rcvd 2xx reply.
+	 * This member's as an alternative to passing the reply to the AS, 
+	 * every time a reply for local request is rcvd.
+	 * Member can not be union'ed with local_cancel, since CANCEL can happen
+	 * concurrently with a 2xx reply (to generate an ACK).
+	 */
+	struct retr_buf *local_ack;
+#endif
 }ua_client_type;
 
 
@@ -237,6 +251,10 @@ struct totag_elem {
 #define T_IN_AGONY (1<<5) /* set if waiting to die (delete timer)
                              TODO: replace it with del on unref */
 #define T_AUTO_INV_100 (1<<6) /* send an 100 reply automatically  to inv. */
+#ifdef WITH_AS_SUPPORT
+	/* don't generate automatically an ACK for local transaction */
+#	define T_NO_AUTO_ACK	(1<<7)
+#endif
 #define T_DONT_FORK   (T_CANCELED|T_6xx)
 
 /* unsigned short should be enough for a retr. timer: max. 65535 ticks =>
@@ -441,6 +459,9 @@ inline static void insert_into_hash_table_unsafe( struct cell * p_cell,
 													unsigned int hash )
 {
 	p_cell->label = _tm_table->entries[hash].next_label++;
+#ifdef EXTRA_DEBUG
+	DEBUG("cell label: %u\n", p_cell->label);
+#endif
 	p_cell->hash_index=hash;
 	/* insert at the beginning */
 	clist_insert(&_tm_table->entries[hash], p_cell, next_c, prev_c);

+ 18 - 4
modules/tm/t_hooks.h

@@ -76,12 +76,15 @@ struct cell;
 #define TMCB_DESTROY_N          15  /* called on transaction destroy */
 #define TMCB_E2ECANCEL_IN_N     16
 #define TMCB_E2EACK_RETR_IN_N   17
+#ifdef WITH_AS_SUPPORT
+#define TMCB_DONT_ACK_N         18 /* TM shoudn't ACK a local UAC  */
+#endif
 #ifdef TMCB_ONSEND
-#define TMCB_REQUEST_SENT_N     18
-#define TMCB_RESPONSE_SENT_N    19
-#define TMCB_MAX_N              19
+#define TMCB_REQUEST_SENT_N     19
+#define TMCB_RESPONSE_SENT_N    20
+#define TMCB_MAX_N              20
 #else
-#define TMCB_MAX_N              17
+#define TMCB_MAX_N              18
 #endif
 
 
@@ -103,6 +106,9 @@ struct cell;
 #define TMCB_DESTROY          (1<<TMCB_DESTROY_N)
 #define TMCB_E2ECANCEL_IN     (1<<TMCB_E2ECANCEL_IN_N)
 #define TMCB_E2EACK_RETR_IN   (1<<TMCB_E2EACK_RETR_IN_N)
+#ifdef WITH_AS_SUPPORT
+#define TMCB_DONT_ACK         (1<<TMCB_DONT_ACK_N)
+#endif
 #ifdef TMCB_ONSEND
 #define TMCB_REQUEST_SENT      (1<<TMCB_REQUEST_SENT_N)
 #define TMCB_RESPONSE_SENT     (1<<TMCB_RESPONSE_SENT_N)
@@ -313,6 +319,14 @@ struct cell;
  *  the cell* parameter (t) and the tmcb are set to 0. Only the param is
  *  is filled inside TMCB. For dialogs callbacks t is also 0.
  *
+ *
+ * TMCB_DONT_ACK (requires AS support) -- for localy generated INVITEs, TM 
+ * automatically generates an ACK for the received 2xx replies. But, if this 
+ * flag is passed to TM when creating the initial UAC request, this won't
+ * happen anymore: the ACK generation must be triggered from outside, using
+ * TM's interface.
+ * While this isn't exactly a callback type, it is used as part of the flags
+ * mask when registering callbacks.
 
 	the callback's param MUST be in shared memory and will
 	NOT be freed by TM; you must do it yourself from the

+ 51 - 3
modules/tm/t_lookup.c

@@ -605,19 +605,34 @@ int t_lookup_request( struct sip_msg* p_msg , int leave_new_locked,
 			/* CSeq only the number without method ! */
 			if (get_cseq(t_msg)->number.len!=get_cseq(p_msg)->number.len)
 				continue;
-			if (! EQ_LEN(from)) continue;
 			/* To only the uri -- to many UACs screw up tags  */
 			if (get_to(t_msg)->uri.len!=get_to(p_msg)->uri.len)
 				continue;
 			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 (memcmp(get_to(t_msg)->uri.s, get_to(p_msg)->uri.s,
 				get_to(t_msg)->uri.len)!=0) continue;
 			
 			/* it is e2e ACK/200 */
 			if (p_cell->uas.status<300) {
+				/* For e2e ACKs, From's tag 'MUST' equal INVITE's, while use
+				 * of the URI in this case is to be deprecated (Sec. 12.2.1.1).
+				 * Comparing entire From body is dangerous, since some UAs
+				 * screw the display name up. */
+				if (parse_from_header(p_msg) < 0) {
+					ERR("failed to parse From HF; ACK might not match.\n");
+					continue;
+				}
+				if (! STR_EQ(get_from(t_msg)->tag_value, 
+						get_from(p_msg)->tag_value))
+					continue;
+#ifdef TM_E2E_ACK_CHECK_FROM_URI
+				if (! STR_EQ(get_from(t_msg)->uri, 
+						get_from(p_msg)->uri))
+					continue;
+#endif
+
 				/* all criteria for proxied ACK are ok */
 				if (likely(p_cell->relayed_reply_branch!=-2)) {
 					if (unlikely(has_tran_tmcbs(p_cell, 
@@ -633,6 +648,10 @@ int t_lookup_request( struct sip_msg* p_msg , int leave_new_locked,
 				if (dlg_matching(p_cell, p_msg))
 					goto found;
 				continue;
+			} else {
+				/* for hbh ACKs, From HF 'MUST' equal INVITE's one */
+				if (! EQ_LEN(from)) continue;
+				if (! EQ_STR(from)) continue;
 			}
 			
 			/* it is not an e2e ACK/200 -- perhaps it is 
@@ -1481,6 +1500,31 @@ int t_get_trans_ident(struct sip_msg* p_msg, unsigned int* hash_index, unsigned
     return 1;
 }
 
+#ifdef WITH_AS_SUPPORT
+/**
+ * Returns the hash coordinates of the transaction current CANCEL is targeting.
+ */
+int t_get_canceled_ident(struct sip_msg* msg, unsigned int* hash_index, 
+		unsigned int* label)
+{
+	struct cell *orig;
+	if (msg->REQ_METHOD != METHOD_CANCEL) {
+		WARN("looking up original transaction for non-CANCEL method (%d).\n",
+				msg->REQ_METHOD);
+		return -1;
+	}
+	orig = t_lookupOriginalT(msg);
+	if ((orig == T_NULL_CELL) || (orig == T_UNDEFINED))
+		return -1;
+	*hash_index = orig->hash_index;
+	*label = orig->label;
+	DEBUG("original T found @%p, %d:%d.\n", orig, *hash_index, *label);
+	/* TODO: why's w_t_lookup_cancel setting T to 'undefined'?? */
+	UNREF(orig);
+	return 1;
+}
+#endif /* WITH_AS_SUPPORT */
+
 int t_lookup_ident(struct cell ** trans, unsigned int hash_index, 
 					unsigned int label)
 {
@@ -1493,7 +1537,11 @@ int t_lookup_ident(struct cell ** trans, unsigned int hash_index,
 	}
 	
 	LOCK_HASH(hash_index);
-	
+
+#ifndef E2E_CANCEL_HOP_BY_HOP
+#warning "t_lookup_ident() can only reliably match INVITE transactions in " \
+		"E2E_CANCEL_HOP_BY_HOP mode"
+#endif
 	hash_bucket=&(get_tm_table()->entries[hash_index]);
 	/* all the transactions from the entry are compared */
 	clist_foreach(hash_bucket, p_cell, next_c){

+ 512 - 85
modules/tm/t_msgbuilder.c

@@ -50,7 +50,9 @@
 
 #include "defs.h"
 
-
+#ifdef EXTRA_DEBUG
+#include <assert.h>
+#endif
 #include "../../comp_defs.h"
 #include "../../hash_func.h"
 #include "../../globals.h"
@@ -69,24 +71,24 @@
 #include "../../cfg_core.h" /* cfg_get(core, core_cfg, use_dns_failover) */
 #endif
 
-
-#define ROUTE_PREFIX "Route: "
-#define ROUTE_PREFIX_LEN (sizeof(ROUTE_PREFIX) - 1)
-
-#define ROUTE_SEPARATOR ", "
-#define ROUTE_SEPARATOR_LEN (sizeof(ROUTE_SEPARATOR) - 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);
+		}while(0)
 
 #define append_str(_p,_str) \
 	do{  \
 		memcpy((_p), (_str).s, (_str).len); \
 		(_p)+=(_str).len;  \
- 	} while(0);
+ 	} while(0)
 
 
 /* Build a local request based on a previous request; main
@@ -375,10 +377,13 @@ error:
 }
 
 
-struct rte {
+typedef struct rte {
 	rr_t* ptr;
+	/* 'ptr' above doesn't point to a mem chunk linked to a sip_msg, so it
+	 * won't be free'd along with it => it must be free'd "manually" */
+	int free_rr;
 	struct rte* next;
-};
+} rte_t;
 
   	 
 static inline void free_rte_list(struct rte* list)
@@ -388,74 +393,13 @@ static inline void free_rte_list(struct rte* list)
 	while(list) {
 		ptr = list;
 		list = list->next;
+		if (ptr->free_rr)
+			free_rr(&ptr->ptr);
 		pkg_free(ptr);
 	}
 }
 
 
-static inline int process_routeset(struct sip_msg* msg, str* contact, struct rte** list, str* ruri, str* next_hop)
-{
-	struct hdr_field* ptr;
-	rr_t* p;
-	struct rte* t, *head;
-	struct sip_uri puri;
-	
-	ptr = msg->record_route;
-	head = 0;
-	while(ptr) {
-		if (ptr->type == HDR_RECORDROUTE_T) {
-			if (parse_rr(ptr) < 0) {
-				LOG(L_ERR, "process_routeset: Error while parsing Record-Route header\n");
-				return -1;
-			}
-			
-			p = (rr_t*)ptr->parsed;
-			while(p) {
-				t = (struct rte*)pkg_malloc(sizeof(struct rte));
-				if (!t) {
-					LOG(L_ERR, "process_routeset: No memory left\n");
-					free_rte_list(head);
-					return -1;
-				}
-				t->ptr = p;
-				t->next = head;
-				head = t;
-				p = p->next;
-			}
-		}
-		ptr = ptr->next;
-	}
-	
-	if (head) {
-		if (parse_uri(head->ptr->nameaddr.uri.s, head->ptr->nameaddr.uri.len, &puri) == -1) {
-			LOG(L_ERR, "process_routeset: Error while parsing URI\n");
-			free_rte_list(head);
-			return -1;
-		}
-		
-		if (puri.lr.s) {
-			     /* Next hop is loose router */
-			*ruri = *contact;
-			*next_hop = head->ptr->nameaddr.uri;
-		} else {
-			     /* Next hop is strict router */
-			*ruri = head->ptr->nameaddr.uri;
-			*next_hop = *ruri;
-			t = head;
-			head = head->next;
-			pkg_free(t);
-		}
-	} else {
-		     /* No routes */
-		*ruri = *contact;
-		*next_hop = *contact;
-	}
-	
-	*list = head;
-	return 0;
-}
-
-
 static inline int calc_routeset_len(struct rte* list, str* contact)
 {
 	struct rte* ptr;
@@ -547,7 +491,429 @@ static inline int get_contact_uri(struct sip_msg* msg, str* uri)
 	return 0;
 }
 
+/**
+ * Extract route set from the message (out of Record-Route, if reply, OR
+ * Route, if request).
+ * The route set is returned into the "UAC-format" (keep order for Rs, reverse
+ * RRs).
+ */
+static inline int get_uac_rs(sip_msg_t *msg, int is_req, struct rte **rtset)
+{
+	struct hdr_field* ptr;
+	rr_t *p, *new_p;
+	struct rte *t, *head, *old_head;
 
+	head = 0;
+	for (ptr = is_req ? msg->route : msg->record_route; ptr; ptr = ptr->next) {
+		switch (ptr->type) {
+			case HDR_RECORDROUTE_T:
+				if (is_req)
+					continue;
+				break;
+			case HDR_ROUTE_T:
+				if (! is_req)
+					continue;
+				break;
+			default:
+				continue;
+		}
+		if (parse_rr(ptr) < 0) {
+			ERR("failed to parse Record-/Route HF (%d).\n", ptr->type);
+			goto err;
+		}
+			
+		p = (rr_t*)ptr->parsed;
+		while(p) {
+			if (! (t = (struct rte*)pkg_malloc(sizeof(struct rte)))) {
+				ERR("out of pkg mem (asked for: %zd).\n", sizeof(struct rte));
+				goto err;
+			}
+			if (is_req) {
+				/* in case of requests, the sip_msg structure is free'd before
+				 * rte list is evaluated => must do a copy of it */
+				if (duplicate_rr(&new_p, p) < 0) {
+					pkg_free(t);
+					ERR("failed to duplicate RR");
+					goto err;
+				}
+				t->ptr = new_p;
+			} else {
+				t->ptr = p;
+			}
+			t->free_rr = is_req;
+			t->next = head;
+			head = t;
+			p = p->next;
+		}
+	}
+
+	if (is_req) {
+		/* harvesting the R/RR HF above inserts at head, which suites RRs (as
+		 * they must be reversed, anyway), but not Rs => reverse once more */
+		old_head = head;
+		head = 0;
+		while (old_head) {
+			t = old_head;
+			old_head = old_head->next;
+			t->next = head;
+			head = t;
+		}
+	}
+
+	*rtset = head;
+	return 0;
+err:
+	free_rte_list(head);
+	return -1;
+}
+
+
+static inline unsigned short uri2port(const struct sip_uri *puri)
+{
+	if (puri->port.s) {
+		return puri->port_no;
+	} else switch (puri->type) {
+		case SIP_URI_T:
+		case TEL_URI_T:
+			if (puri->transport_val.len == sizeof("TLS") - 1) {
+				unsigned trans;
+				trans = puri->transport_val.s[0] | 0x20; trans <<= 8;
+				trans |= puri->transport_val.s[1] | 0x20; trans <<= 8;
+				trans |= puri->transport_val.s[2] | 0x20;
+				if (trans == 0x746C73) /* t l s */
+					return SIPS_PORT;
+			}
+			return SIP_PORT;
+		case SIPS_URI_T:
+		case TELS_URI_T:
+			return SIPS_PORT;
+		default:
+			BUG("unexpected URI type %d.\n", puri->type);
+	}
+	return 0;
+}
+
+/**
+ * Evaluate if next hop is a strict or loose router, by looking at the
+ * retr. buffer of the original INVITE.
+ * Assumes:
+ * 	orig_inv is a parsed SIP message;
+ * 	rtset is not NULL.
+ * @return:
+ * 	F_RB_NH_LOOSE : next hop was loose router;
+ * 	F_RB_NH_STRICT: nh is strict;
+ * 	0 on error.
+ */
+static unsigned long nhop_type(sip_msg_t *orig_inv, rte_t *rtset,
+		const struct dest_info *dst_inv, str *contact)
+{
+	struct sip_uri puri, topr_uri, lastr_uri, inv_ruri, cont_uri;
+	struct ip_addr *uri_ia;
+	union sockaddr_union uri_sau;
+	unsigned int uri_port, dst_port, inv_port, cont_port, lastr_port;
+	rte_t *last_r;
+#ifdef TM_LOC_ACK_DO_REV_DNS
+	struct ip_addr ia;
+	struct hostent *he;
+	char **alias;
+#endif
+
+#define PARSE_URI(_str_, _uri_) \
+	do { \
+		/* parse_uri() 0z the puri */ \
+		if (parse_uri((_str_)->s, \
+				(_str_)->len, _uri_) < 0) { \
+			ERR("failed to parse route body '%.*s'.\n", STR_FMT(_str_)); \
+			return 0; \
+		} \
+	} while (0)
+
+#define HAS_LR(_rte_) \
+	({ \
+		PARSE_URI(&(_rte_)->ptr->nameaddr.uri, &puri); \
+		puri.lr.s; \
+	})
+
+#define URI_PORT(_puri_, _port) \
+	do { \
+		if (! (_port = uri2port(_puri_))) \
+			return 0; \
+	} while (0)
+
+	/* examine the easy/fast & positive cases foremost */
+
+	/* [1] check if 1st route lacks ;lr */
+	DEBUG("checking lack of ';lr' in 1st route.\n");
+	if (! HAS_LR(rtset))
+		return F_RB_NH_STRICT;
+	topr_uri = puri; /* save 1st route's URI */
+
+	/* [2] check if last route shows ;lr */
+	DEBUG("checking presence of ';lr' in last route.\n");
+	for (last_r = rtset; last_r->next; last_r = last_r->next)
+		/* scroll down to last route */
+		;
+	if (HAS_LR(last_r))
+		return F_RB_NH_LOOSE;
+
+	/* [3] 1st route has ;lr -> check if the destination of original INV
+	 * equals the address provided by this route; if does -> loose */
+	DEBUG("checking INVITE's destination against its first route.\n");
+	URI_PORT(&topr_uri, uri_port);
+	if (! (dst_port = su_getport((void *)&dst_inv->to)))
+		return 0; /* not really expected */
+	if (dst_port != uri_port)
+		return F_RB_NH_STRICT;
+	/* if 1st route contains an IP address, comparing it against .dst */
+	if ((uri_ia = str2ip(&topr_uri.host))
+#ifdef USE_IPV6
+			|| (uri_ia = str2ip6(&topr_uri.host))
+#endif
+			) {
+		/* we have an IP address in route -> comparison can go swiftly */
+		if (init_su(&uri_sau, uri_ia, uri_port) < 0)
+			return 0; /* not really expected */
+		if (su_cmp(&uri_sau, (void *)&dst_inv->to))
+			/* ;lr and sent there */
+			return F_RB_NH_LOOSE;
+		else
+			/* ;lr and NOT sent there (probably sent to RURI address) */
+			return F_RB_NH_STRICT;
+	} else {
+		/*if 1st route contains a name, rev resolve the .dst and compare*/
+		INFO("Failed to decode string '%.*s' in route set element as IP "
+				"address. Trying name resolution.\n",STR_FMT(&topr_uri.host));
+
+	/* TODO: alternatively, rev name and compare against dest. IP.  */
+#ifdef TM_LOC_ACK_DO_REV_DNS
+		ia.af = 0;
+		su2ip_addr(&ia, (void *)&dst_inv->to);
+		if (! ia.af)
+			return 0; /* not really expected */
+		if ((he = rev_resolvehost(&ia))) {
+			if ((strlen(he->h_name) == topr_uri.host.len) &&
+					(memcmp(he->h_name, topr_uri.host.s, 
+							topr_uri.host.len) == 0))
+				return F_RB_NH_LOOSE;
+			for (alias = he->h_aliases; *alias; alias ++)
+				if ((strlen(*alias) == topr_uri.host.len) &&
+						(memcmp(*alias, topr_uri.host.s, 
+								topr_uri.host.len) == 0))
+					return F_RB_NH_LOOSE;
+			return F_RB_NH_STRICT;
+		} else {
+			INFO("failed to resolve address '%s' to a name.\n", 
+					ip_addr2a(&ia));
+		}
+#endif
+	}
+
+	WARN("failed to establish with certainty the type of next hop; trying an"
+			" educated guess.\n");
+
+	/* [4] compare (possibly updated) remote target to original RURI; if
+	 * equal, a strict router's address wasn't filled in as RURI -> loose */
+	DEBUG("checking remote target against INVITE's RURI.\n");
+	PARSE_URI(contact, &cont_uri);
+	PARSE_URI(GET_RURI(orig_inv), &inv_ruri);
+	URI_PORT(&cont_uri, cont_port);
+	URI_PORT(&inv_ruri, inv_port);
+	if ((cont_port == inv_port) && (cont_uri.host.len == inv_ruri.host.len) &&
+			(memcmp(cont_uri.host.s, inv_ruri.host.s, cont_uri.host.len) == 0))
+		return F_RB_NH_LOOSE;
+
+	/* [5] compare (possibly updated) remote target to last route; if equal, 
+	 * strict router's address might have been filled as RURI and remote
+	 * target appended to route set -> strict */
+	DEBUG("checking remote target against INVITE's last route.\n");
+	PARSE_URI(&last_r->ptr->nameaddr.uri, &lastr_uri);
+	URI_PORT(&lastr_uri, lastr_port);
+	if ((cont_port == lastr_port) && 
+			(cont_uri.host.len == lastr_uri.host.len) &&
+			(memcmp(cont_uri.host.s, lastr_uri.host.s, 
+					lastr_uri.host.len) == 0))
+		return F_RB_NH_STRICT;
+
+	WARN("failed to establish the type of next hop; assuming loose router.\n");
+	return F_RB_NH_LOOSE;
+
+#undef PARSE_URI
+#undef HAS_LR
+#undef URI_PORT
+}
+
+/**
+ * Evaluates the routing elements in locally originated request or reply to
+ * locally originated request.
+ * If original INVITE was in-dialog (had to-tag), it uses the
+ * routes present there (b/c the 2xx for it does not have a RR set, normally).
+ * Otherwise, use the reply (b/c the INVITE does not have yet the complete 
+ * route set).
+ *
+ * @return: negative for failure; out params:
+ *  - list: route set;
+ *  - ruri: RURI to be used in ACK;
+ *  - nexthop: where to first send the ACK.
+ *
+ *  NOTE: assumes rpl's parsed to EOF!
+ *
+ */
+static int eval_uac_routing(sip_msg_t *rpl, const struct retr_buf *inv_rb, 
+		str* contact, struct rte **list, str *ruri, str *next_hop)
+{
+	sip_msg_t orig_inv, *sipmsg; /* reparse original INVITE */
+	rte_t *t, *prev_t, *rtset = NULL;
+	int is_req;
+	struct sip_uri puri;
+	static size_t chklen;
+	int ret = -1;
+	
+	/* parse the retr. buffer */
+	memset(&orig_inv, 0, sizeof(struct sip_msg));
+	orig_inv.buf = inv_rb->buffer;
+	orig_inv.len = inv_rb->buffer_len;
+	DEBUG("reparsing retransmission buffer of original INVITE:\n%.*s\n",
+			orig_inv.len, orig_inv.buf);
+	if (parse_msg(orig_inv.buf, orig_inv.len, &orig_inv) != 0) {
+		ERR("failed to parse retr buffer (weird!): \n%.*s\n", orig_inv.len,
+				orig_inv.buf);
+		return -1;
+	}
+
+	/* check if we need to look at request or reply */
+	if ((parse_headers(&orig_inv, HDR_TO_F, 0) < 0) || (! orig_inv.to)) {
+		/* the bug is at message assembly */
+		BUG("failed to parse INVITE retr. buffer and/or extract 'To' HF:"
+				"\n%.*s\n", orig_inv.len, orig_inv.buf);
+		goto end;
+	}
+	if (((struct to_body *)orig_inv.to->parsed)->tag_value.len) {
+		DEBUG("building ACK for in-dialog INVITE (using RS in orig. INV.)\n");
+		if (parse_headers(&orig_inv, HDR_EOH_F, 0) < 0) {
+			BUG("failed to parse INVITE retr. buffer to EOH:"
+					"\n%.*s\n", orig_inv.len, orig_inv.buf);
+			goto end;
+		}
+		sipmsg = &orig_inv;
+		is_req = 1;
+	} else {
+		DEBUG("building ACK for out-of-dialog INVITE (using RS in RR set).\n");
+		sipmsg = rpl;
+		is_req = 0;
+	}
+
+	/* extract the route set */
+	if (get_uac_rs(sipmsg, is_req, &rtset) < 0) {
+		ERR("failed to extract route set.\n");
+		goto end;
+	}
+
+	if (! rtset) { /* No routes */
+		*ruri = *contact;
+		*next_hop = *contact;
+	} else if (! is_req) { /* out of dialog req. */
+		if (parse_uri(rtset->ptr->nameaddr.uri.s, rtset->ptr->nameaddr.uri.len,
+				&puri) < 0) {
+			ERR("failed to parse first route in set.\n");
+			goto end;
+		}
+		
+		if (puri.lr.s) { /* Next hop is loose router */
+			*ruri = *contact;
+			*next_hop = rtset->ptr->nameaddr.uri;
+		} else { /* Next hop is strict router */
+			*ruri = rtset->ptr->nameaddr.uri;
+			*next_hop = *ruri;
+			/* consume first route, b/c it will be put in RURI */
+			t = rtset;
+			rtset = rtset->next;
+			pkg_free(t);
+		}
+	} else {
+		unsigned long route_flags = inv_rb->flags;
+		DEBUG("UAC rb flags: 0x%x.\n", (unsigned int)route_flags);
+eval_flags:
+		switch (route_flags & (F_RB_NH_LOOSE|F_RB_NH_STRICT)) {
+		case 0:
+			WARN("calculate_hooks() not called when built the local UAC of "
+					"in-dialog request, or called with empty route set.\n");
+			/* try to figure out what kind of hop is the next one
+			 * (strict/loose) by reading the original invite */
+			if ((route_flags = nhop_type(&orig_inv, rtset, &inv_rb->dst, 
+					contact))) {
+				DEBUG("original request's next hop type evaluated to: 0x%x.\n",
+						(unsigned int)route_flags);
+				goto eval_flags;
+			} else {
+				ERR("failed to establish what kind of router the next "
+						"hop is.\n");
+				goto end;
+			}
+			break;
+		case F_RB_NH_LOOSE:
+			*ruri = *contact;
+			*next_hop = rtset->ptr->nameaddr.uri;
+			break;
+		case F_RB_NH_STRICT:
+			/* find ptr to last route body that contains the (possibly) old 
+			 * remote target 
+			 */
+			for (t = rtset, prev_t = t; t->next; prev_t = t, t = t->next)
+				;
+			if ((t->ptr->len == contact->len) && 
+					(memcmp(t->ptr->nameaddr.name.s, contact->s, 
+							contact->len) == 0)){
+				/* the remote target didn't update -> keep the whole route set,
+				 * including the last entry */
+				/* do nothing */
+			} else {
+				/* trash last entry and replace with new remote target */
+				free_rte_list(t);
+				/* compact the rr_t struct along with rte. this way, free'ing
+				 * it can be done along with rte chunk, independent of Route
+				 * header parser's allocator (using pkg/shm) */
+				chklen = sizeof(struct rte) + sizeof(rr_t);
+				if (! (t = (struct rte *)pkg_malloc(chklen))) {
+					ERR("out of pkg memory (%zd required)\n", chklen);
+					goto end;
+				}
+				/* this way, .free_rr is also set to 0 (!!!) */
+				memset(t, 0, chklen); 
+				((rr_t *)&t[1])->nameaddr.name = *contact;
+				((rr_t *)&t[1])->len = contact->len;
+				/* chain the new route elem in set */
+				if (prev_t == rtset)
+				 	/*there is only one elem in route set: the remote target*/
+					rtset = t;
+				else
+					prev_t->next = t;
+			}
+
+			*ruri = *GET_RURI(&orig_inv); /* reuse original RURI */
+			*next_hop = *ruri;
+			break;
+		default:
+			/* probably a mem corruption */
+			BUG("next hop of original request marked as both loose and strict"
+					" router (buffer: %.*s).\n", inv_rb->buffer_len, 
+					inv_rb->buffer);
+#ifdef EXTRA_DEBUG
+			abort();
+#else
+			goto end;
+#endif
+		}
+	}
+
+	*list = rtset;
+	/* all went well */
+	ret = 0;
+end:
+	free_sip_msg(&orig_inv);
+	if (ret < 0)
+		free_rte_list(rtset);
+	return ret;
+}
 
      /*
       * The function creates an ACK to 200 OK. Route set will be created
@@ -556,8 +922,8 @@ static inline int get_contact_uri(struct sip_msg* msg, str* uri)
 	  * generates local ACK to 200 OK (on behalf of applications using uac)
       */
 char *build_dlg_ack(struct sip_msg* rpl, struct cell *Trans, 
-					unsigned int branch, str* to, unsigned int *len,
-					struct dest_info* dst)
+					unsigned int branch, str *hdrs, str *body,
+					unsigned int *len, struct dest_info* dst)
 {
 	char *req_buf, *p, *via;
 	unsigned int via_len;
@@ -568,18 +934,47 @@ char *build_dlg_ack(struct sip_msg* rpl, struct cell *Trans,
 	struct rte* list;
 	str contact, ruri, *cont;
 	str next_hop;
+	str body_len;
+	str _to, *to = &_to;
 #ifdef USE_DNS_FAILOVER
 	struct dns_srv_handle dns_h;
 #endif
+#ifdef WITH_AS_SUPPORT
+	/* With AS support, TM allows for external modules to generate building of
+	 * the ACK; in this case, the ACK's retransmission buffer is built once
+	 * and kept in memory (to help when retransmitted 2xx are received and ACK
+	 * must be resent).
+	 * Allocation of the string raw buffer that holds the ACK is piggy-backed
+	 * with allocation of the retransmission buffer (since both have the same
+	 * life-cycle): both the string buffer and retransm. buffer are placed 
+	 * into the same allocated chunk of memory (retr. buffer first, string 
+	 * buffer follows).In this case, the 'len' param is used as in-out 
+	 * parameter: 'in' to give the extra space needed by the retr. buffer,
+	 * 'out' to return the lenght of the allocated string buffer.
+	 */
+	unsigned offset = *len;
+#endif
 	
-	if (get_contact_uri(rpl, &contact) < 0) {
+	if (parse_headers(rpl, HDR_EOH_F, 0) == -1 || !rpl->to) {
+		ERR("Error while parsing headers.\n");
 		return 0;
+	} else {
+		_to.s = rpl->to->name.s;
+		_to.len = rpl->to->len;
 	}
 	
-	if (process_routeset(rpl, &contact, &list, &ruri, &next_hop) < 0) {
+	if (get_contact_uri(rpl, &contact) < 0) {
 		return 0;
 	}
 	
+	if (eval_uac_routing(rpl, &Trans->uac[branch].request, &contact, 
+			&list, &ruri, &next_hop) < 0) {
+		ERR("failed to evaluate routing elements.\n");
+		return 0;
+	}
+	DEBUG("ACK RURI: `%.*s', NH: `%.*s'.\n", STR_FMT(&ruri), 
+			STR_FMT(&next_hop));
+
 	if ((contact.s != ruri.s) || (contact.len != ruri.len)) {
 		     /* contact != ruri means that the next
 		      * hop is a strict router, cont will be non-zero
@@ -643,12 +1038,29 @@ char *build_dlg_ack(struct sip_msg* rpl, struct cell *Trans,
 	
 	     /* User Agent */
 	if (server_signature) *len += USER_AGENT_LEN + CRLF_LEN;
+		/* extra headers */
+	if (hdrs)
+		*len += hdrs->len;
+		/* body */
+	if (body) {
+		body_len.s = int2str(body->len, &body_len.len);
+		*len += body->len;
+	} else {
+		body_len.len = 0;
+		body_len.s = NULL; /*4gcc*/
+		*len += 1; /* for the (Cont-Len:) `0' */
+	}
 	     /* Content Length, EoM */
-	*len += CONTENT_LENGTH_LEN + 1 + CRLF_LEN + CRLF_LEN;
-	
+	*len += CONTENT_LENGTH_LEN + body_len.len + CRLF_LEN + CRLF_LEN;
+
+#if WITH_AS_SUPPORT
+	req_buf = shm_malloc(offset + *len + 1);
+	req_buf += offset;
+#else
 	req_buf = shm_malloc(*len + 1);
+#endif
 	if (!req_buf) {
-		LOG(L_ERR, "build_dlg_ack: Cannot allocate memory\n");
+		ERR("Cannot allocate memory (%u+1)\n", *len);
 		goto error01;
 	}
 	p = req_buf;
@@ -679,8 +1091,23 @@ char *build_dlg_ack(struct sip_msg* rpl, struct cell *Trans,
 		append_mem_block(p, USER_AGENT CRLF, USER_AGENT_LEN + CRLF_LEN);
 	}
 	
-	     /* Content Length, EoM */
-	append_mem_block(p, CONTENT_LENGTH "0" CRLF CRLF, CONTENT_LENGTH_LEN + 1 + CRLF_LEN + CRLF_LEN);
+	/* extra headers */
+	if (hdrs)
+		append_mem_block(p, hdrs->s, hdrs->len);
+	
+	     /* Content Length, EoH, (body) */
+	if (body) {
+		append_mem_block(p, CONTENT_LENGTH, CONTENT_LENGTH_LEN);
+		append_mem_block(p, body_len.s, body_len.len);
+		append_mem_block(p, /*end crr. header*/CRLF /*EoH*/CRLF, CRLF_LEN + 
+				CRLF_LEN);
+		append_mem_block(p, body->s, body->len);
+	} else {
+		append_mem_block(p, CONTENT_LENGTH "0" CRLF CRLF, 
+				CONTENT_LENGTH_LEN + 1 + CRLF_LEN + CRLF_LEN);
+	}
+
+	/* EoM */
 	*p = 0;
 	
 	pkg_free(via);
@@ -692,7 +1119,7 @@ char *build_dlg_ack(struct sip_msg* rpl, struct cell *Trans,
  error:
 	free_rte_list(list);
 	return 0;
-  	 }
+}
 
 
 /*
@@ -959,7 +1386,7 @@ char* build_uac_req(str* method, str* headers, str* body, dlg_t* dialog, int bra
      	if (body) memapp(w, body->s, body->len);
 
 #ifdef EXTRA_DEBUG
-	if (w-buf != *len ) abort();
+	assert(w-buf == *len);
 #endif
 
 	pkg_free(via.s);

+ 2 - 15
modules/tm/t_msgbuilder.h

@@ -53,19 +53,6 @@
 #define TOTAG_LEN (sizeof(TOTAG)-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);
-
 char *build_local(struct cell *Trans, unsigned int branch,
 	unsigned int *len, char *method, int method_len, str *to);
 
@@ -85,8 +72,8 @@ char *build_uac_request(  str msg_type, str dst, str from,
  * local ACK to 200 OK (on behalf of applications using uac
  */
 char *build_dlg_ack(struct sip_msg* rpl, struct cell *Trans, 
-					unsigned int branch, str* to, unsigned int *len,
-					struct dest_info *dst);
+					unsigned int branch, str *hdrs, str *body,
+					unsigned int *len, struct dest_info* dst);
 
 
 /*

+ 78 - 9
modules/tm/t_reply.c

@@ -93,7 +93,9 @@
  */
 
 
-
+#ifdef EXTRA_DEBUG
+#include <assert.h>
+#endif
 #include "../../comp_defs.h"
 #include "../../hash_func.h"
 #include "../../dprint.h"
@@ -368,15 +370,43 @@ static char *build_local_ack(struct sip_msg* rpl, struct cell *trans,
 								int branch, unsigned int *ret_len,
 								struct dest_info*  dst)
 {
-	str to;
-	if (parse_headers(rpl, HDR_EOH_F, 0) == -1 || !rpl->to) {
-		LOG(L_ERR, "ERROR: build_local_ack: Error while parsing headers\n");
-		return 0;
+#ifdef WITH_AS_SUPPORT
+	struct retr_buf *local_ack, *old_lack;
+
+	/* do we have the ACK cache, previously build? */
+	if ((local_ack = trans->uac[0].local_ack) && local_ack->buffer_len) {
+		DEBUG("reusing ACK retr. buffer.\n");
+		*ret_len = local_ack->buffer_len;
+		*dst = local_ack->dst;
+		return local_ack->buffer;
 	}
 
-	to.s = rpl->to->name.s;
-	to.len = rpl->to->len;
-	return build_dlg_ack(rpl, trans, branch, &to, ret_len, dst);
+	/* the ACK will be built (and cached) by the AS (ack_local_uac()) */
+	if (trans->flags & T_NO_AUTO_ACK) 
+		return NULL;
+
+	if (! (local_ack = local_ack_rb(rpl, trans, branch, /*hdrs*/NULL, 
+			/*body*/NULL))) {
+		ERR("failed to build local ACK retransmission buffer (T@%p).\n",trans);
+		return NULL;
+	}
+
+	/* set the new buffer, but only if not already set (concurrent 2xx) */
+	if ((old_lack = (struct retr_buf *)atomic_cmpxchg_long(
+			(void *)&trans->uac[0].local_ack, 0, (long)local_ack))) {
+		/* buffer already set: trash current and use the winning one */
+		INFO("concurrent 2xx to local INVITE detected (T@%p).\n", trans);
+		free_local_ack(local_ack);
+		local_ack = old_lack;
+	}
+	
+	*ret_len = local_ack->buffer_len;
+	*dst = local_ack->dst;
+	return local_ack->buffer;
+#else /* ! WITH_AS_SUPPORT */
+	return build_dlg_ack(rpl, trans, branch, /*hdrs*/NULL, /*body*/NULL, 
+			ret_len, dst);
+#endif /* WITH_AS_SUPPORT */
 }
 
 
@@ -907,6 +937,36 @@ int t_pick_branch(int inc_branch, int inc_code, struct cell *t, int *res_code)
 	return best_b;
 }
 
+/* The same as t_pick_branch(), but allows also
+ * blind branches to be picked up.
+ * This function should be used only in failure_route
+ * to check which response has been 
+ * picked up by t_pick_branch().
+ * returns:
+ * 0..X ... branch number
+ * -1   ... error
+ * -2   ... can't decide yet -- incomplete branches present
+ */
+int t_pick_branch_blind(struct cell *t, int *res_code)
+{
+	int best_b, best_s, b;
+
+	best_b=-1; best_s=0;
+	for ( b=0; b<t->nr_of_outgoings ; b++ ) {
+		/* there is still an unfinished UAC transaction; wait now! */
+		if ( t->uac[b].last_received<200 )
+			return -2;
+		/* if reply is null => t_send_branch "faked" reply, skip over it */
+		if ( t->uac[b].reply && 
+				get_prio(t->uac[b].last_received)<get_prio(best_s) ) {
+			best_b = b;
+			best_s = t->uac[b].last_received;
+		}
+	} /* find lowest branch */
+	
+	*res_code=best_s;
+	return best_b;
+}
 
 /* flag indicating whether it is requested
  * to drop the already saved replies or not */
@@ -1125,9 +1185,16 @@ static enum rps t_should_relay_response( struct cell *Trans , int new_code,
 
 	/* not >=300 ... it must be 2xx or provisional 1xx */
 	if (new_code>=100) {
+#ifdef WITH_AS_SUPPORT
+			/* need a copy of the message for ACK generation */
+			*should_store = (inv_through && is_local(Trans) && 
+					(Trans->uac[branch].last_received < 200) &&
+					(Trans->flags & T_NO_AUTO_ACK)) ? 1 : 0;
+#else
+		*should_store=0;
+#endif
 		/* 1xx and 2xx except 100 will be relayed */
 		Trans->uac[branch].last_received=new_code;
-		*should_store=0;
 		*should_relay= new_code==100? -1 : branch;
 		if (new_code>=200 ) {
 			which_cancel( Trans, cancel_bitmap );
@@ -1818,7 +1885,9 @@ int reply_received( struct sip_msg  *p_msg )
 													&onsend_params);
 					}
 #endif
+#ifndef WITH_AS_SUPPORT
 					shm_free(ack);
+#endif
 				}
 			}
 		}

+ 2 - 0
modules/tm/t_reply.h

@@ -147,6 +147,8 @@ void tm_init_tags();
 
 /* selects the branch for fwd-ing the reply */
 int t_pick_branch(int inc_branch, int inc_code, struct cell *t, int *res_code);
+/* checks the selected branch from failure_route */
+int t_pick_branch_blind(struct cell *t, int *res_code);
 
 /* drops all the replies to make sure
  * that none of them is picked up again

+ 43 - 5
modules/tm/tm.c

@@ -112,6 +112,7 @@
 #include "../../route.h"
 #include "../../cfg/cfg.h"
 #include "../../globals.h"
+#include "../../timer_ticks.h"
 
 #include "config.h"
 #include "sip_msg.h"
@@ -229,6 +230,7 @@ static int t_branch_replied(struct sip_msg* msg, char*, char*);
 static int t_any_timeout(struct sip_msg* msg, char*, char*);
 static int t_any_replied(struct sip_msg* msg, char*, char*);
 static int t_is_canceled(struct sip_msg* msg, char*, char*);
+static int t_is_expired(struct sip_msg* msg, char*, char*);
 static int t_grep_status(struct sip_msg* msg, char*, char*);
 static int w_t_drop_replies(struct sip_msg* msg, char* foo, char* bar);
 static int w_t_save_lumps(struct sip_msg* msg, char* foo, char* bar);
@@ -248,7 +250,9 @@ static cmd_export_t cmds[]={
 			REQUEST_ROUTE},
 	{"t_lookup_request",   w_t_check,               0, 0,
 			REQUEST_ROUTE},
-	{"t_lookup_cancel",    w_t_lookup_cancel,     0, 0,
+	{"t_lookup_cancel",    w_t_lookup_cancel,       0, 0,
+			REQUEST_ROUTE},
+	{"t_lookup_cancel",    w_t_lookup_cancel,       1, fixup_int_1,
 			REQUEST_ROUTE},
 	{T_REPLY,              w_t_reply,               2, fixup_t_reply,
 			REQUEST_ROUTE | FAILURE_ROUTE },
@@ -360,6 +364,8 @@ static cmd_export_t cmds[]={
 			REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE },
 	{"t_is_canceled",     t_is_canceled,            0, 0,
 			REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE },
+	{"t_is_expired",      t_is_expired,             0, 0,
+			REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE },
 	{"t_grep_status",     t_grep_status,            1, fixup_var_int_1, 
 			REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE },
 	{"t_drop_replies",    w_t_drop_replies,         0, 0,
@@ -785,7 +791,7 @@ static int t_check_status(struct sip_msg* msg, char *p1, char *foo)
 	struct cell *t;
 	char *status, *s = NULL;
 	char backup;
-	int lowest_status, n;
+	int lowest_status, n, ret;
 	fparam_t* fp;
 	regex_t* re = NULL;
 	str tmp;
@@ -844,7 +850,16 @@ static int t_check_status(struct sip_msg* msg, char *p1, char *foo)
 
 	case MODE_ONFAILURE:
 		/* use the status of the winning reply */
-		if (t_pick_branch( -1, 0, t, &lowest_status)<0 ) {
+		ret = t_pick_branch( -1, 0, t, &lowest_status);
+		if (ret == -1) {
+			/* t_pick_branch() retuns error also when there are only
+			 * blind UACs. Let us give it another chance including the
+			 * blind branches. */
+			LOG(L_DBG, "DEBUG: t_check_status: t_pick_branch returned error, "
+				"trying t_pick_branch_blind\n");
+			ret = t_pick_branch_blind(t, &lowest_status);
+		}
+		if (ret < 0) {
 			LOG(L_CRIT,"BUG:t_check_status: t_pick_branch failed to get "
 				" a final response in MODE_ONFAILURE\n");
 			goto error;
@@ -889,17 +904,21 @@ inline static int w_t_check(struct sip_msg* msg, char* str, char* str2)
 inline static int w_t_lookup_cancel(struct sip_msg* msg, char* str, char* str2)
 {
 	struct cell *ret;
+	int i=0;
 	if (msg->REQ_METHOD==METHOD_CANCEL) {
 		ret = t_lookupOriginalT( msg );
 		DBG("lookup_original: t_lookupOriginalT returned: %p\n", ret);
 		if (ret != T_NULL_CELL) {
+			/* If the parameter is set to 1, overwrite the message flags of
+			 * the CANCEL with the flags of the INVITE */
+			if (str && (get_int_fparam(&i, msg, (fparam_t*)str)==0) && i)
+				msg->flags = ret->uas.request->flags;
+
 			/* The cell is reffed by t_lookupOriginalT, but T is not set.
 			So we must unref it before returning. */
 			UNREF(ret);
-			set_t(T_UNDEFINED);
 			return 1;
 		}
-		set_t(T_UNDEFINED);
 	} else {
 		LOG(L_WARN, "WARNING: script error t_lookup_cancel() called for non-CANCEL request\n");
 	}
@@ -1555,6 +1574,25 @@ int t_is_canceled(struct sip_msg* msg, char* foo, char* bar)
 	return ret;
 }
 
+/* script function, returns: 1 if the transaction lifetime interval has already elapsed, -1 if not */
+int t_is_expired(struct sip_msg* msg, char* foo, char* bar)
+{
+	struct cell *t;
+	int ret;
+	
+	
+	if (t_check( msg , 0 )==-1) return -1;
+	t=get_t();
+	if ((t==0) || (t==T_UNDEFINED)){
+		LOG(L_ERR, "ERROR: t_is_expired: cannot check a message "
+			"for which no T-state has been established\n");
+		ret=-1;
+	}else{
+		ret=(TICKS_GT(t->end_of_life, get_ticks_raw()))?-1:1;
+	}
+	return ret;
+}
+
 /* script function, returns: 1 if any of the branches did timeout, -1 if not */
 int t_any_timeout(struct sip_msg* msg, char* foo, char* bar)
 {

+ 143 - 14
modules/tm/uac.c

@@ -194,6 +194,7 @@ static inline int t_uac_prepare(uac_req_t *uac_r,
 #ifdef USE_DNS_FAILOVER
 	struct dns_srv_handle dns_h;
 #endif
+	long nhtype;
 
 	ret=-1;
 	hi=0; /* make gcc happy */
@@ -203,7 +204,8 @@ static inline int t_uac_prepare(uac_req_t *uac_r,
 	/*** added by dcm 
 	 * - needed by external ua to send a request within a dlg
 	 */
-	if (w_calculate_hooks(uac_r->dialog)<0 && !uac_r->dialog->hooks.next_hop)
+	if ((nhtype = w_calculate_hooks(uac_r->dialog)) < 0)
+		/* if err's returned, the message is incorrect */
 		goto error2;
 
 	if (!uac_r->dialog->loc_seq.is_set) {
@@ -257,6 +259,10 @@ static inline int t_uac_prepare(uac_req_t *uac_r,
 		new_cell->flags |= T_IS_INVITE_FLAG;
 		new_cell->flags|=T_AUTO_INV_100 &
 				(!cfg_get(tm, tm_cfg, tm_auto_inv_100) -1);
+#ifdef WITH_AS_SUPPORT
+		if (uac_r->cb_flags & TMCB_DONT_ACK)
+			new_cell->flags |= T_NO_AUTO_ACK;
+#endif
 		lifetime=cfg_get(tm, tm_cfg, tm_max_inv_lifetime);
 	}else
 		lifetime=cfg_get(tm, tm_cfg, tm_max_noninv_lifetime);
@@ -282,8 +288,8 @@ static inline int t_uac_prepare(uac_req_t *uac_r,
 	set_kr(REQ_FWDED);
 
 	request = &new_cell->uac[0].request;
-	
 	request->dst = dst;
+	request->flags |= nhtype;
 
 	if (!is_ack) {
 #ifdef TM_DEL_UNREF
@@ -402,18 +408,7 @@ void send_prepared_request(struct retr_buf *request)
  */
 int t_uac(uac_req_t *uac_r)
 {
-	struct retr_buf *request;
-	struct cell *cell;
-	int ret;
-	int is_ack;
-
-	ret = t_uac_prepare(uac_r, &request, &cell);
-	if (ret < 0) return ret;
-	is_ack = (uac_r->method->len == 3) && (memcmp("ACK", uac_r->method->s, 3)==0) ? 1 : 0;
-	send_prepared_request_impl(request, !is_ack /* retransmit */);
-	if (cell && is_ack)
-		free_cell(cell);
-	return ret;
+	return t_uac_with_ids(uac_r, NULL, NULL);
 }
 
 /*
@@ -445,6 +440,140 @@ int t_uac_with_ids(uac_req_t *uac_r,
 	return ret;
 }
 
+#ifdef WITH_AS_SUPPORT
+struct retr_buf *local_ack_rb(sip_msg_t *rpl_2xx, struct cell *trans,
+					unsigned int branch, str *hdrs, str *body)
+{
+	struct retr_buf *lack;
+	unsigned int buf_len;
+	char *buffer;
+	struct dest_info dst;
+
+	buf_len = (unsigned)sizeof(struct retr_buf);
+	if (! (buffer = build_dlg_ack(rpl_2xx, trans, branch, hdrs, body, 
+			&buf_len, &dst))) {
+		return 0;
+	} else {
+		/* 'buffer' now points into a contiguous chunk of memory with enough
+		 * room to hold both the retr. buffer and the string raw buffer: it
+		 * points to the begining of the string buffer; we iterate back to get
+		 * the begining of the space for the retr. buffer. */
+		lack = &((struct retr_buf *)buffer)[-1];
+		lack->buffer = buffer;
+		lack->buffer_len = buf_len;
+		lack->dst = dst;
+	}
+
+	/* TODO: need next 2? */
+	lack->activ_type = TYPE_LOCAL_ACK;
+	lack->my_T = trans;
+
+	return lack;
+}
+
+void free_local_ack(struct retr_buf *lack)
+{
+	shm_free(lack);
+}
+
+void free_local_ack_unsafe(struct retr_buf *lack)
+{
+	shm_free_unsafe(lack);
+}
+
+/**
+ * @return: 
+ * 	0: success
+ * 	-1: internal error
+ * 	-2: insane call :)
+ */
+int ack_local_uac(struct cell *trans, str *hdrs, str *body)
+{
+	struct retr_buf *local_ack, *old_lack;
+	int ret;
+
+	/* sanity checks */
+
+#ifdef EXTRA_DEBUG
+	if (! trans) {
+		BUG("no transaction to ACK.\n");
+		abort();
+	}
+#endif
+
+#define RET_INVALID \
+		ret = -2; \
+		goto fin
+
+	if (! is_local(trans)) {
+		ERR("trying to ACK non local transaction (T@%p).\n", trans);
+		RET_INVALID;
+	}
+	if (! is_invite(trans)) {
+		ERR("trying to ACK non INVITE local transaction (T@%p).\n", trans);
+		RET_INVALID;
+	}
+	if (! trans->uac[0].reply) {
+		ERR("trying to ACK un-completed INVITE transaction (T@%p).\n", trans);
+		RET_INVALID;
+	}
+
+	if (! (trans->flags & T_NO_AUTO_ACK)) {
+		ERR("trying to ACK an auto-ACK transaction (T@%p).\n", trans);
+		RET_INVALID;
+	}
+	if (trans->uac[0].local_ack) {
+		ERR("trying to rebuild ACK retransmission buffer (T@%p).\n", trans);
+		RET_INVALID;
+	}
+
+	/* looks sane: build the retransmission buffer */
+
+	if (! (local_ack = local_ack_rb(trans->uac[0].reply, trans, /*branch*/0, 
+			hdrs, body))) {
+		ERR("failed to build ACK retransmission buffer");
+		RET_INVALID;
+	} else {
+		/* set the new buffer, but only if not already set (conc. invok.) */
+		if ((old_lack = (struct retr_buf *)atomic_cmpxchg_long(
+				(void *)&trans->uac[0].local_ack, 0, (long)local_ack))) {
+			/* buffer already set: deny current attempt */
+			ERR("concurrent ACKing for local INVITE detected (T@%p).\n",trans);
+			free_local_ack(local_ack);
+			RET_INVALID;
+		}
+	}
+
+	if (msg_send(&local_ack->dst, local_ack->buffer, local_ack->buffer_len)<0){
+		/* hopefully will succeed on next 2xx retransmission */
+		ERR("failed to send local ACK (T@%p).\n", trans);
+		ret = -1;
+		goto fin;
+	}
+#ifdef	TMCB_ONSEND
+	else {
+		run_onsend_callbacks2(TMCB_REQUEST_SENT, &trans->uac[0]->request, 
+				local_ack->buffer, local_ack->buffer_len, &local_ack->dst,
+				TYPE_LOCAL_ACK);
+	}
+#endif
+
+	ret = 0;
+fin:
+	/* TODO: ugly! */
+	/* FIXME: the T had been obtain by t_lookup_ident()'ing for it, so, it is
+	 * ref-counted. The t_unref() can not be used, as it requests a valid SIP
+	 * message (all available might be the reply, but if AS goes wrong and
+	 * tries to ACK before the final reply is received, we still have to
+	 * lookup the T to find this out). */
+	UNREF( trans );
+	return ret;
+
+#undef RET_INVALID
+}
+#endif /* WITH_AS_SUPPORT */
+
+
 /*
  * Send a message within a dialog
  */

+ 16 - 0
modules/tm/uac.h

@@ -82,6 +82,9 @@ typedef int (*req_t)(uac_req_t *uac_r, str* ruri, str* to, str* from, str *next_
 typedef int (*t_uac_t)(uac_req_t *uac_r);
 typedef int (*t_uac_with_ids_t)(uac_req_t *uac_r,
 		unsigned int *ret_index, unsigned int *ret_label);
+#ifdef WITH_AS_SUPPORT
+typedef int (*ack_local_uac_f)(struct cell *trans, str *hdrs, str *body);
+#endif
 typedef int (*prepare_request_within_f)(uac_req_t *uac_r,
 		struct retr_buf **dst_req);
 typedef void (*send_prepared_request_f)(struct retr_buf *request_dst);
@@ -121,6 +124,19 @@ int req_within(uac_req_t *uac_r);
  */
 int req_outside(uac_req_t *uac_r, str* to, str* from);
 
+
+#ifdef WITH_AS_SUPPORT
+struct retr_buf *local_ack_rb(sip_msg_t *rpl_2xx, struct cell *trans,
+					unsigned int branch, str *hdrs, str *body);
+void free_local_ack(struct retr_buf *lack);
+void free_local_ack_unsafe(struct retr_buf *lack);
+
+/**
+ * ACK an existing local INVITE transaction...
+ */
+int ack_local_uac(struct cell *trans, str *hdrs, str *body);
+#endif
+
 /*
  * Send a transactional request, no dialogs involved
  */

+ 134 - 19
msg_translator.c

@@ -61,6 +61,10 @@
  *              (rfc3486) (andrei)
  * 2007-08-31  id_builder() and via_builder() are grouped into one function:
  *             create_via_hf() -- tm module needs them as well (Miklos)
+ * 2008-12-17  build_req_from_sip_req() will now fallback to tcp, tls or sctp
+ *              if packet size > udp_mtu and fallback is enabled 
+ *             build_req_from_sip_req() uses now global_req_flags along
+ *               msg->msg_flags  (andrei)
  *
  */
 /* Via special params:
@@ -134,6 +138,8 @@
 #include "resolve.h"
 #include "ut.h"
 #include "pt.h"
+#include "cfg/cfg.h"
+#include "forward.h"
 
 
 #define append_str(_dest,_src,_len) \
@@ -148,9 +154,35 @@
 extern char version[];
 extern int version_len;
 
+/* global flags for build_req_from_sip_req */
+static unsigned int global_req_flags=0;
 
 
 
+/** per process fixup function for global_req_flags.
+  * It should be called from the configuration framework.
+  */
+void fix_global_req_flags( str* name)
+{
+	global_req_flags=0;
+	switch(cfg_get(core, core_cfg, udp_mtu_try_proto)){
+		case PROTO_NONE:
+		case PROTO_UDP:
+			/* do nothing */
+			break;
+		case PROTO_TCP:
+			global_req_flags|=FL_MTU_TCP_FB;
+			break;
+		case PROTO_TLS:
+			global_req_flags|=FL_MTU_TLS_FB;
+			break;
+		case PROTO_SCTP:
+			global_req_flags|=FL_MTU_SCTP_FB;
+			break;
+	}
+	if (cfg_get(core, core_cfg, force_rport))
+		global_req_flags|=FL_FORCE_RPORT;
+}
 
 
 
@@ -1419,20 +1451,54 @@ error:
 
 
 
+/** builds a request in memory from another sip request.
+  *
+  * Side-effects: - it adds lumps to the msg which are _not_ cleaned.
+  * All the added lumps are HDR_VIA_T.
+  *               - it might change send_info->proto and send_info->send_socket
+  *                 if proto fallback is enabled (see below).
+  *
+  * Uses also global_req_flags ( OR'ed with msg->msg_flags, see send_info
+  * below).
+  *
+  * @param msg  - sip message structure, complete with lumps
+  * @param returned_len - result length (filled in)
+  * @param send_info  - dest_info structure (value/result), contains where the
+  *                     packet will be sent to (it's needed for building a 
+  *                     correct via, fill RR lumps a.s.o.). If MTU based
+  *                     protocol fall-back is enabled (see flags below),
+  *                     send_info->proto might be updated with the new
+  *                     protocol.
+  *                     msg->msg_flags used:
+  *                     - FL_TCP_MTU_FB, FL_TLS_MTU_FB and FL_SCTP_MTU_FB -
+  *                       fallback to the corresp. proto if the built 
+  *                       message > mtu and send_info->proto==PROTO_UDP. 
+  *                       It will also update send_info->proto.
+  *                     - FL_FORCE_RPORT: add rport to via
+  *
+  * @return pointer to the new request (pkg_malloc'ed, needs freeing when
+  *   done) and sets returned_len or 0 on error.
+  */
 char * build_req_buf_from_sip_req( struct sip_msg* msg,
 								unsigned int *returned_len,
-								struct dest_info* send_info)
+								struct dest_info* send_info
+								)
 {
-	unsigned int len, new_len, received_len, rport_len, uri_len, via_len, body_delta;
+	unsigned int len, new_len, received_len, rport_len, uri_len, via_len,
+				 body_delta;
 	char* line_buf;
 	char* received_buf;
 	char* rport_buf;
 	char* new_buf;
 	char* buf;
 	unsigned int offset, s_offset, size;
-	struct lump* anchor;
+	struct lump* via_anchor;
+	struct lump* via_lump;
 	struct lump* via_insert_param;
 	str branch;
+	unsigned int flags;
+	unsigned int udp_mtu;
+	struct dest_info di;
 
 	via_insert_param=0;
 	uri_len=0;
@@ -1445,9 +1511,8 @@ char * build_req_buf_from_sip_req( struct sip_msg* msg,
 	rport_buf=0;
 	line_buf=0;
 
-	     /* Calculate message body difference and adjust
-	      * Content-Length
-	      */
+	flags=msg->msg_flags|global_req_flags;
+	/* Calculate message body difference and adjust Content-Length */
 	body_delta = lumps_len(msg, msg->body_lumps, send_info);
 	if (adjust_clen(msg, body_delta, send_info->proto) < 0) {
 		LOG(L_ERR, "ERROR: build_req_buf_from_sip_req: Error while adjusting"
@@ -1455,13 +1520,14 @@ char * build_req_buf_from_sip_req( struct sip_msg* msg,
 		goto error00;
 	}
 
-	/* create a the via header */
+	/* create the via header */
 	branch.s=msg->add_to_branch_s;
 	branch.len=msg->add_to_branch_len;
 
 	line_buf = create_via_hf( &via_len, msg, send_info, &branch);
 	if (!line_buf){
-		LOG(L_ERR,"ERROR: build_req_buf_from_sip_req: no via received!\n");
+		LOG(L_ERR,"ERROR: build_req_buf_from_sip_req: "
+					"memory allocation failure\n");
 		goto error00;
 	}
 	/* check if received needs to be added */
@@ -1478,7 +1544,7 @@ char * build_req_buf_from_sip_req( struct sip_msg* msg,
 	 *  - if via already contains an rport add it and overwrite the previous
 	 *  rport value if present (if you don't want to overwrite the previous
 	 *  version remove the comments) */
-	if ((msg->msg_flags&FL_FORCE_RPORT)||
+	if ((flags&FL_FORCE_RPORT)||
 			(msg->via1->rport /*&& msg->via1->rport->value.s==0*/)){
 		if ((rport_buf=rport_builder(msg, &rport_len))==0){
 			LOG(L_ERR, "ERROR: build_req_buf_from_sip_req:"
@@ -1487,13 +1553,6 @@ char * build_req_buf_from_sip_req( struct sip_msg* msg,
 		}
 	}
 
-	/* add via header to the list */
-	/* try to add it before msg. 1st via */
-	/* add first via, as an anchor for second via*/
-	anchor=anchor_lump(msg, msg->via1->hdr.s-buf, 0, HDR_VIA_T);
-	if (anchor==0) goto error01;
-	if (insert_new_lump_before(anchor, line_buf, via_len, HDR_VIA_T)==0)
-		goto error01;
 	/* find out where the offset of the first parameter that should be added
 	 * (after host:port), needed by add receive & maybe rport */
 	if (msg->via1->params.s){
@@ -1546,10 +1605,59 @@ char * build_req_buf_from_sip_req( struct sip_msg* msg,
 	}
 
 	/* compute new msg len and fix overlapping zones*/
-	new_len=len+body_delta+lumps_len(msg, msg->add_rm, send_info);
+	new_len=len+body_delta+lumps_len(msg, msg->add_rm, send_info)+via_len;
 #ifdef XL_DEBUG
 	LOG(L_ERR, "DEBUG: new_len(%d)=len(%d)+lumps_len\n", new_len, len);
 #endif
+	udp_mtu=cfg_get(core, core_cfg, udp_mtu);
+	di.proto=PROTO_NONE;
+	if (unlikely((send_info->proto==PROTO_UDP) && udp_mtu && 
+					(flags & FL_MTU_FB_MASK) && (new_len>udp_mtu))){
+
+		di=*send_info; /* copy whole struct - will be used in the Via builder */
+		di.proto=PROTO_NONE; /* except the proto */
+#ifdef USE_TCP
+		if (!tcp_disable && (flags & FL_MTU_TCP_FB) &&
+				(di.send_sock=get_send_socket(msg, &send_info->to, PROTO_TCP))){
+			di.proto=PROTO_TCP;
+		}
+	#ifdef USE_TLS
+		else if (!tls_disable && (flags & FL_MTU_TLS_FB) &&
+				(di.send_sock=get_send_socket(msg, &send_info->to, PROTO_TLS))){
+			di.proto=PROTO_TLS;
+		}
+	#endif /* USE_TLS */
+#endif /* USE_TCP */
+#ifdef USE_SCTP
+	#ifdef USE_TCP
+		else
+	#endif /* USE_TCP */
+		 if (!sctp_disable && (flags & FL_MTU_SCTP_FB) &&
+				(di.send_sock=get_send_socket(msg, &send_info->to, PROTO_SCTP))){
+			di.proto=PROTO_SCTP;
+		 }
+#endif /* USE_SCTP */
+		
+		if (di.proto!=PROTO_NONE){
+			new_len-=via_len;
+			pkg_free(line_buf);
+			line_buf = create_via_hf( &via_len, msg, &di, &branch);
+			if (!line_buf){
+				LOG(L_ERR,"ERROR: build_req_buf_from_sip_req: "
+							"memory allocation failure!\n");
+				goto error00;
+			}
+			new_len+=via_len;
+		}
+	}
+	/* add via header to the list */
+	/* try to add it before msg. 1st via */
+	/* add first via, as an anchor for second via*/
+	via_anchor=anchor_lump(msg, msg->via1->hdr.s-buf, 0, HDR_VIA_T);
+	if (via_anchor==0) goto error04;
+	if ((via_lump=insert_new_lump_before(via_anchor, line_buf, via_len,
+											HDR_VIA_T))==0)
+		goto error04;
 
 	if (msg->new_uri.s){
 		uri_len=msg->new_uri.len;
@@ -1582,6 +1690,12 @@ char * build_req_buf_from_sip_req( struct sip_msg* msg,
 	memcpy(new_buf+offset, buf+s_offset, len-s_offset);
 	new_buf[new_len]=0;
 
+	/* update the send_info if udp_mtu affected */
+	if (di.proto!=PROTO_NONE) { 
+		send_info->proto=di.proto;
+		send_info->send_sock=di.send_sock;
+	}
+
 #ifdef DBG_MSG_QA
 	if (new_buf[new_len-1]==0) {
 		LOG(L_ERR, "ERROR: build_req_buf_from_sip_req: 0 in the end\n");
@@ -1593,11 +1707,12 @@ char * build_req_buf_from_sip_req( struct sip_msg* msg,
 	return new_buf;
 
 error01:
-	if (line_buf) pkg_free(line_buf);
 error02:
 	if (received_buf) pkg_free(received_buf);
 error03:
 	if (rport_buf) pkg_free(rport_buf);
+error04:
+	if (line_buf) pkg_free(line_buf);
 error00:
 	*returned_len=0;
 	return 0;
@@ -1729,7 +1844,7 @@ char * build_res_buf_from_sip_req( unsigned int code, char *text ,str *new_tag,
 		}
 	}
 	/* check if rport needs to be updated */
-	if ( (msg->msg_flags&FL_FORCE_RPORT)||
+	if ( ((msg->msg_flags|global_req_flags)&FL_FORCE_RPORT)||
 		(msg->via1->rport /*&& msg->via1->rport->value.s==0*/)){
 		if ((rport_buf=rport_builder(msg, &rport_len))==0){
 			LOG(L_ERR, "ERROR: build_res_buf_from_sip_req:"

+ 2 - 0
msg_translator.h

@@ -149,5 +149,7 @@ char * build_all( struct sip_msg* msg, int adjust_clen,
 			int *error,
 			struct dest_info* send_info);
 
+/** cfg framework fixup */
+void fix_global_req_flags( str* name);
 
 #endif

+ 5 - 0
parser/msg_parser.h

@@ -92,6 +92,11 @@ enum request_method { METHOD_UNDEF=0, METHOD_INVITE=1, METHOD_CANCEL=2, METHOD_A
                                 (for failure route use) */
 #define FL_HASH_INDEX  128 /* msg->hash_index contains a valid value (tm use)*/
 
+#define FL_MTU_TCP_FB   256
+#define FL_MTU_TLS_FB   512
+#define FL_MTU_SCTP_FB 1024
+#define FL_MTU_FB_MASK  (FL_MTU_TCP_FB|FL_MTU_TLS_FB|FL_MTU_SCTP_FB)
+
 
 #define IFISMETHOD(methodname,firstchar)                                  \
 if (  (*tmp==(firstchar) || *tmp==((firstchar) | 32)) &&                  \

+ 26 - 0
route.c

@@ -876,6 +876,32 @@ int fix_actions(struct action* a)
 				t->val[0].u.data=si;
 				t->val[0].type=SOCKETINFO_ST;
 				break;
+			case UDP_MTU_TRY_PROTO_T:
+				if (t->val[0].type!=NUMBER_ST){
+					LOG(L_CRIT, "BUG: fix_actions: invalid subtype"
+								"%d for udp_mtu_try_proto\n",
+								t->val[0].type);
+					return E_BUG;
+				}
+				switch(t->val[0].u.number){
+					case PROTO_UDP:
+						t->val[0].u.number=0;
+						break;
+					case PROTO_TCP:
+						t->val[0].u.number=FL_MTU_TCP_FB;
+						break;
+					case PROTO_TLS:
+						t->val[0].u.number=FL_MTU_TLS_FB;
+						break;
+					case PROTO_SCTP:
+						t->val[0].u.number=FL_MTU_SCTP_FB;
+						break;
+					default:
+						LOG(L_CRIT, "BUG: fix actions: invalid argument for"
+									" udp_mtu_try_proto (%d)\n", 
+									(unsigned int)t->val[0].u.number);
+				}
+				break;
 		}
 	}
 	return 0;

+ 3 - 1
route_struct.h

@@ -35,6 +35,7 @@
  *  2004-02-24  added LOAD_AVP_T and AVP_TO_URI_T (bogdan)
  *  2005-12-11  added SND{IP,PORT,PROTO,AF}_O & TO{IP,PORT}_O (andrei)
  *  2005-12-19  select framework added SELECT_O and SELECT_ST (mma)
+ *  2008-12-17  added UDP_MTU_TRY_PROTO_T (andrei)
  */
 
 
@@ -91,7 +92,8 @@ enum { FORWARD_T=1, SEND_T, DROP_T, LOG_T, ERROR_T, ROUTE_T, EXEC_T,
 		AVP_TO_URI_T,
 		FORCE_SEND_SOCKET_T,
 		ASSIGN_T,
-		ADD_T
+		ADD_T,
+		UDP_MTU_TRY_PROTO_T
 };
 enum { NOSUBTYPE=0, STRING_ST, NET_ST, NUMBER_ST, IP_ST, RE_ST, PROXY_ST,
 		EXPR_ST, ACTIONS_ST, MODEXP_ST, MODFIXUP_ST, URIHOST_ST, URIPORT_ST,

+ 155 - 0
scripts/logging/fix-logs

@@ -0,0 +1,155 @@
+#!/usr/bin/perl -w
+#
+# Usage: fix-log [MODULE-NAME] < INFILE > OUTFILE
+#
+# Fixes logging macros and messages in the SER source file INFILE
+# to match the recent updates in the logging API.
+#
+# Specify MODULE_NAME if INFILE source file is a part of a SER module.
+#
+# See doc/logging-api.txt for details.
+#
+# $Id$
+
+#
+# What *exactly* does this script do?
+#
+#   - replaces LOG(L_*, ...) with the short macro corresponding to L_* level
+#
+#   - replaces DEBUG() with DBG() macro
+#
+#   - removes MODULE and the level string prefixes from FMT arguments of macros
+#     where FMT looks like "X:...", "X:Y:..." or "X:Y:Z:...", white spaces are
+#     ignored and preserved, string matching is case-insensitive
+#
+#     In addition, if the level string found in FMT argument doesn't match the actual
+#     level of the macro, the macro level is fixed.
+#
+#     Examples:
+#        ERR("ERROR:tm: blah\n")           becomes ERR("blah\n")
+#        DBG("Debug: blah\n")              becomes DBG("blah\n")
+#        LOG(L_ERR, "tm: INFO: blah\n")    becomes INFO("blah\n")
+#
+#   - removes 'MODULE_NAME ":' string from the beggining of FMT arguments of macros
+#     in module source files (a common special case)
+#
+#     Example:
+#        LOG(L_ERR, MODULE_NAME ": tm:Info:blah\n")   becomes INFO("blah\n")
+#
+
+# Map a text string to L_* log level macro
+my %text2level = (
+    "BUG"         => "L_CRIT",
+    "CRIT"        => "L_CRIT",
+    "CRITICAL"    => "L_CRIT",
+
+    "ALERT"       => "L_ALERT",
+
+    "ERR"         => "L_ERR",
+    "ERROR"       => "L_ERR",
+
+    "WARN"        => "L_WARN",
+    "WARNING"     => "L_WARN",
+
+    "NOTICE"      => "L_NOTICE",
+
+    "INFO"        => "L_INFO",
+
+    "DBG"         => "L_DBG",
+    "DEBUG"       => "L_DBG",
+);
+
+#
+short2level
+
+
+# Strip the leading and trailing whitespaces and upper-case a text
+sub norm {
+    my $text = ($_[0] || "");
+    $text =~ s/^\s*//;
+    $text =~ s/\s*$//;
+    uc($text);
+}
+
+my $module_name = norm($ARGV[0]);
+
+sub fix_log_prefix {
+    my ($prefix, $level) = ($_[0], $_[1]);
+
+    # delete prefix if it contains module name
+    if ($module_name) {
+        if (!$text || (norm($text) eq $module_name)) {
+            return ("", $level);
+        }
+    }
+
+    # delete prefix if it contains text level
+    my $prefix_level = $text2level{norm($prefix)};
+    if ($prefix_level) {
+	$prefix = "";
+	
+	# change level if does not match prefix level
+	if ($level =~ /^L_(DBG|INFO|NOTICE|WARN|ERR|CRIT|ALERT)$/ && 
+	    $level ne $prefix_level) {
+    	    return ("", $prefix_level);
+	}
+    }
+
+    return ($prefix . ":", $level);
+}
+
+sub fix_log {
+    my $level = $_[0];
+    my $prefix1 = $_[1];
+    my $prefix2 = $_[2];
+    my $prefix3 = $_[3];
+    my $space = $_[4];
+
+    ($prefix1, $level) = fix_log_prefix($prefix1, $level) if $prefix1;
+    ($prefix2, $level) = fix_log_prefix($prefix2, $level) if $prefix2;
+    ($prefix3, $level) = fix_log_prefix($prefix3, $level) if $prefix3;
+
+    my $prefix = $prefix1 . $prefix2 . $prefix3 . $space;
+    $prefix =~ s/^\s*//;
+    
+    "LOG($level, \"$prefix";
+}
+
+while (<STDIN>) {
+AGAIN:
+    # replace DEBUG() by DBG()
+    s/DEBUG\(/DBG\(/g;
+
+    # ...in case the statement spans more lines
+    if (/(DBG|INFO|NOTICE|WARN|ERR|BUG|ALERT)\(\s*$/ || /LOG\(([^,]*,)?\s*$/) {
+        $_ .= <STDIN>;
+        goto AGAIN;
+    }
+
+    # one common special case used in several modules
+    if ($module_name) {
+	s/LOG\(\s*([^,]+)\s*,\s*MODULE_NAME\s*"\s*:\s*/LOG($1, "/g;
+	s/(DBG|INFO|NOTICE|WARN|ERR|BUG|ALERT)\(\s*MODULE_NAME\s*"\s*:\s*/$1("/g;
+    }
+
+    # module name and level prefix removing magic, may change macro level
+    # if different one found in the message
+    $id='\s*[a-zA-Z0-9_]+\s*';
+    
+    s/LOG\(\s*(([A-Z_]+)|[^,]+)\s*,\s*"($id):(($id):(($id):)?)?(\s*)/
+        fix_log($1, $3, $5, $7, $8);
+    /eg;
+
+    s/(DBG|INFO|NOTICE|WARN|ERR|BUG|ALERT)\(\s*"($id):(($id):(($id):)?)?(\s*)/
+        $1 = "CRIT" if $1 eq "BUG";
+        fix_log("L_$1", $2, $4, $6, $7);
+    /eg;
+
+    # prefer shorter static-level macros
+    s/LOG\(\s*L_(DBG|INFO|NOTICE|WARN|ERR|CRIT|ALERT)\s*,\s*/
+	$1 = "BUG" if $1 eq "CRIT";
+	"$1\(";
+    /eg;
+
+    print;
+}

+ 37 - 0
scripts/logging/fix-logs-all

@@ -0,0 +1,37 @@
+#!/bin/sh
+#
+# Usage: fix-logs-all [DIR]
+#
+# Fixes logging macros and messages in SER source files in DIR
+# directory (recursively).
+#
+# See doc/logging-api.txt and fix-logs script for details.
+#
+# $Id$
+
+# <filename>.ORIG backup files is created for each processed file.
+# If TEST is set, it only prints a "patch" file.
+#
+
+find ${1:-.} -type f \( -name "*.[chy]" -o -name "*.lex" -o -name "*.cc" \) | \
+    grep -v "/dprint\.[hc]$" | \
+	while read file; do
+	    echo "=== $file"
+
+	    if ! test "$TEST"; then
+		mv "$file" "$file.ORIG"
+	    fi
+
+	    if expr match "$file" ".*/modules/" >/dev/null; then
+		module=$(basename $(dirname $file))
+	    fi
+
+	    if ! test "$TEST"; then
+		fix-logs "$module" < "$file.ORIG" > "$file"
+	    else
+		fix-logs "$module" < "$file" > "$file.NEW"
+
+		diff "$file.NEW" "$file"
+		rm "$file.NEW"
+	    fi
+	done

+ 5 - 0
tcp_main.c

@@ -115,6 +115,11 @@
 #include <sys/types.h>
 #include <sys/select.h>
 #include <sys/socket.h>
+#ifdef HAVE_FILIO_H
+#include <sys/filio.h> /* needed on solaris 2.x for FIONREAD */
+#elif defined __OS_solaris
+#define BSD_COMP  /* needed on older solaris for FIONREAD */
+#endif /* HAVE_FILIO_H / __OS_solaris */
 #include <sys/ioctl.h>  /* ioctl() used on write error */
 #include <netinet/in.h>
 #include <netinet/in_systm.h>

+ 14 - 0
usr_avp.c

@@ -1015,6 +1015,20 @@ error:
 	return -1;
 }
 
+void free_avp_ident(avp_ident_t* attr)
+{
+	if (attr->flags & AVP_NAME_RE) {
+		if (! attr->name.re) {
+			BUG("attr ident @%p has the regexp flag set, but no regexp.\n",
+					attr);
+#ifdef EXTRA_DEBUG
+			abort();
+#endif
+		} else {
+			pkg_free(attr->name.re);
+		}
+	}
+}
 
 int parse_avp_spec( str *name, int *type, int_str *avp_name, int *index)
 {

+ 2 - 0
usr_avp.h

@@ -204,6 +204,8 @@ int parse_avp_ident( str *name, avp_ident_t* attr);
 int parse_avp_name( str *name, int *type, int_str *avp_name, int *index);
 int parse_avp_spec( str *name, int *type, int_str *avp_name, int *index);
 void free_avp_name( avp_flags_t *type, int_str *avp_name);
+/* Free an ident obtained with parse_avp_ident() */
+void free_avp_ident(avp_ident_t* attr);
 
 /* AVP flags functions */
 #define MAX_AVPFLAG  ((unsigned int)( sizeof(avp_flags_t) * CHAR_BIT - 1 - AVP_CUSTOM_FLAGS))