Selaa lähdekoodia

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 vuotta sitten
vanhempi
commit
a07e7447c3
39 muutettua tiedostoa jossa 1889 lisäystä ja 455 poistoa
  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
 #		core that the DNS servers are down. No DNS query is performed
 #		when the servers are unreachable, and even expired resource
 #		when the servers are unreachable, and even expired resource
 #		records are used from the cache. (requires external watchdog)
 #		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
 # Sometimes is needes correct non-quoted $OS. HACK: gcc translates known OS to number ('linux'), so there is added underscore
 
 
 DEFS= $(extra_defs) \
 DEFS= $(extra_defs) \
@@ -1438,6 +1440,16 @@ ifeq  ($(OS), solaris)
 	ifeq ($(NO_SELECT),)
 	ifeq ($(NO_SELECT),)
 		DEFS+=-DHAVE_SELECT
 		DEFS+=-DHAVE_SELECT
 	endif
 	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)
 	ifeq ($(mode), release)
 		#use these only if you're using gcc with Solaris ld
 		#use these only if you're using gcc with Solaris ld
 		#LDFLAGS=-O2 $(PROFILE)
 		#LDFLAGS=-O2 $(PROFILE)

+ 2 - 0
Makefile.modules

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

+ 32 - 3
NEWS

@@ -97,7 +97,16 @@ modules:
  - blst      - new module containing script blacklist manipulations functions
  - blst      - new module containing script blacklist manipulations functions
                (the source of a message can be blacklisted, removed from the
                (the source of a message can be blacklisted, removed from the
                 blacklist or checked for presence in the blacklist).
                 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_to renamed to t_relay_to_avp (undocumented function)
              - t_relay() can now also take host and port parameters (e.g.
              - t_relay() can now also take host and port parameters (e.g.
                t_relay(host, port)), behaving like a statefull 
                t_relay(host, port)), behaving like a statefull 
@@ -220,6 +229,10 @@ modules:
                         - t_set_retr(t1, t2) - changes the retransmissions
                         - t_set_retr(t1, t2) - changes the retransmissions
                            intervals on the fly, on a per transaction basis.
                            intervals on the fly, on a per transaction basis.
 core:
 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
              - sctp support (one-to-many, work in progress, for now linux
                only with no fallback to one-to-one on full send buffers)
                only with no fallback to one-to-one on full send buffers)
              - partial cygwin (windows) support revived: core+static modules, 
              - 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
                between the short name and long name in cache as CNAME record
 
 
 new config variables:
 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)
   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, 
   enable_sctp = 0/1/2  - disable (0)/enable (1)/auto (2) sctp support, 
                          default auto (2)
                          default auto (2)
@@ -260,8 +284,8 @@ new config variables:
                       hosts (default: 0).
                       hosts (default: 0).
   server_id = number - A configurable unique server id that can be used to
   server_id = number - A configurable unique server id that can be used to
                        discriminate server instances within a cluster of
                        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
   loadpath = <modules path> - directory where to load the modules from (-L
      equivalent); modules can be loaded simply by specifying their name
      equivalent); modules can be loaded simply by specifying their name
      (loadmodule "maxfwd")
      (loadmodule "maxfwd")
@@ -356,6 +380,11 @@ new config variables:
     is not initialized at startup and cannot be enabled runtime,
     is not initialized at startup and cannot be enabled runtime,
     that saves some memory.
     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:
 build system:
  - check defines and includes used at compile time and if different 
  - check defines and includes used at compile time and if different 
    force rebuilding everything in the current dir (creates a new file: 
    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-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
  *  2006-12-06  on popular request last_retcode set also by module functions
  *              (andrei)
  *              (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)
  *               static vars (andrei)
  *  2008-11-18  support for variable parameter module functions (andrei)
  *  2008-11-18  support for variable parameter module functions (andrei)
  *  2008-12-03  use lvalues/rvalues for assignments (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
 #endif
 				}
 				}
 
 
-#ifdef HONOR_MADDR				
+#ifdef HONOR_MADDR
 				if (u->maddr_val.s && u->maddr_val.len)
 				if (u->maddr_val.s && u->maddr_val.len)
 					dst_host=&u->maddr_val;
 					dst_host=&u->maddr_val;
 				else
 				else
@@ -310,7 +311,7 @@ int do_action(struct run_act_ctx* h, struct action* a, struct sip_msg* msg)
 				ret=E_BUG;
 				ret=E_BUG;
 				break;
 				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;
 			ret=1;
 			break;
 			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 */
 					ret=1;  /*default is continue */
 					if (v>0) {
 					if (v>0) {
 						if ((a->val[1].type==ACTIONS_ST)&&a->val[1].u.data){
 						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);
 										(struct action*)a->val[1].u.data, msg);
 						}
 						}
 					}else if ((a->val[2].type==ACTIONS_ST)&&a->val[2].u.data){
 					}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);
 										(struct action*)a->val[2].u.data, msg);
 					}
 					}
 				}
 				}
@@ -935,6 +936,10 @@ sw_jt_def:
 			msg->msg_flags|=FL_FORCE_RPORT;
 			msg->msg_flags|=FL_FORCE_RPORT;
 			ret=1; /* continue processing */
 			ret=1; /* continue processing */
 			break;
 			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:
 		case SET_ADV_ADDR_T:
 			if (a->val[0].type!=STR_ST){
 			if (a->val[0].type!=STR_ST){
 				LOG(L_CRIT, "BUG: do_action: bad set_advertised_address() "
 				LOG(L_CRIT, "BUG: do_action: bad set_advertised_address() "
@@ -993,11 +998,11 @@ sw_jt_def:
 			ret=1; /* continue processing */
 			ret=1; /* continue processing */
 			break;
 			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,
 			v=lval_assign(h, msg, (struct lvalue*)a->val[0].u.data,
 								  (struct rval_expr*)a->val[1].u.data);
 								  (struct rval_expr*)a->val[1].u.data);
-			if (likely(v>=0)) 
+			if (likely(v>=0))
 				ret = 1;
 				ret = 1;
 			else if (unlikely (v == EXPR_DROP)) /* hack to quit on DROP*/
 			else if (unlikely (v == EXPR_DROP)) /* hack to quit on DROP*/
 				ret=0;
 				ret=0;
@@ -1090,6 +1095,3 @@ error:
 	h->rec_lev--;
 	h->rec_lev--;
 	return ret;
 	return ret;
 }
 }
-
-
-

+ 5 - 0
cfg.lex

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

+ 16 - 0
cfg.y

@@ -129,6 +129,7 @@
 #include "lvalue.h"
 #include "lvalue.h"
 #include "rvalue.h"
 #include "rvalue.h"
 #include "sr_compat.h"
 #include "sr_compat.h"
+#include "msg_translator.h"
 
 
 #include "config.h"
 #include "config.h"
 #include "cfg_core.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 REVERT_URI
 %token FORCE_RPORT
 %token FORCE_RPORT
 %token FORCE_TCP_ALIAS
 %token FORCE_TCP_ALIAS
+%token UDP_MTU
+%token UDP_MTU_TRY_PROTO
 %token IF
 %token IF
 %token ELSE
 %token ELSE
 %token SET_ADV_ADDRESS
 %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 NUMBER { IF_STUN(stun_allow_fp=$3) ; }
 	| STUN_ALLOW_FP EQUAL error{ yyerror("number expected"); }
 	| STUN_ALLOW_FP EQUAL error{ yyerror("number expected"); }
     | SERVER_ID EQUAL NUMBER { server_id=$3; }
     | 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
 	| cfg_var
 	| error EQUAL { yyerror("unknown config variable"); }
 	| error EQUAL { yyerror("unknown config variable"); }
 	;
 	;
@@ -2556,6 +2568,10 @@ cmd:
 		#endif
 		#endif
 	}
 	}
 	| FORCE_TCP_ALIAS LPAREN error RPAREN	{$$=0; yyerror("bad argument, number expected"); }
 	| 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 {
 	| SET_ADV_ADDRESS LPAREN listen_id RPAREN {
 		$$=0;
 		$$=0;
 		if ((str_tmp=pkg_malloc(sizeof(str)))==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
 #if defined PKG_MALLOC || defined SHM_MEM
 #include "pt.h"
 #include "pt.h"
 #endif
 #endif
+#include "msg_translator.h" /* fix_global_req_flags() */
 #include "cfg/cfg.h"
 #include "cfg/cfg.h"
 #include "cfg_core.h"
 #include "cfg_core.h"
 
 
 struct cfg_group_core default_core_cfg = {
 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) */
 	LOG_DAEMON,	/* log_facility -- see syslog(3) */
 #ifdef USE_DST_BLACKLIST
 #ifdef USE_DST_BLACKLIST
 	/* blacklist */
 	/* blacklist */
@@ -89,6 +90,9 @@ struct cfg_group_core default_core_cfg = {
 	0, /* mem_dump_shm */
 	0, /* mem_dump_shm */
 #endif
 #endif
 	DEFAULT_MAX_WHILE_LOOPS, /* max_while_loops */
 	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;
 void	*core_cfg = &default_core_cfg;
@@ -180,5 +184,12 @@ cfg_def_t core_cfg_def[] = {
 #endif
 #endif
 	{"max_while_loops",	CFG_VAR_INT|CFG_ATOMIC,	0, 0, 0, 0,
 	{"max_while_loops",	CFG_VAR_INT|CFG_ATOMIC,	0, 0, 0, 0,
 		"maximum iterations allowed for a while loop" },
 		"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}
 	{0, 0, 0, 0, 0, 0}
 };
 };

+ 3 - 0
cfg_core.h

@@ -86,6 +86,9 @@ struct cfg_group_core {
 	int mem_dump_shm;
 	int mem_dump_shm;
 #endif
 #endif
 	int max_while_loops;
 	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;
 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 "Via: SIP/2.0/UDP "
 #define MY_VIA_LEN (sizeof(MY_VIA) - 1)
 #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 "Content-Length: "
 #define CONTENT_LENGTH_LEN (sizeof(CONTENT_LENGTH)-1)
 #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
 #ifdef DNS_SRV_LB
 		dns_flags|=DNS_SRV_RR_LB;
 		dns_flags|=DNS_SRV_RR_LB;
 #else
 #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");
 					" support for it is not compiled -- ignoring\n");
 #endif
 #endif
 	}
 	}
 	if (cfg_get(core, core_cfg, dns_try_naptr)) {
 	if (cfg_get(core, core_cfg, dns_try_naptr)) {
 #ifndef USE_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");
 				" support for it is not compiled -- ignoring\n");
 #endif
 #endif
 		dns_flags|=DNS_TRY_NAPTR;
 		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 "globals.h"
-#include "pt.h"
+#include "dprint.h"
  
  
 #include <stdarg.h>
 #include <stdarg.h>
 #include <stdio.h>
 #include <stdio.h>
 #include <strings.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",
 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
 #ifndef __OS_solaris
-					"LOG_AUTHPRIV","LOG_FTP","LOG_SYSLOG",
+			"LOG_AUTHPRIV","LOG_FTP","LOG_SYSLOG",
 #endif
 #endif
-					0};
+			0};
+			
 static int int_fac[]={LOG_AUTH ,  LOG_CRON , LOG_DAEMON ,
 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
 #ifndef __OS_solaris
-					,LOG_AUTHPRIV,LOG_FTP,LOG_SYSLOG
+		      LOG_AUTHPRIV,LOG_FTP,LOG_SYSLOG,
 #endif
 #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)
 int str2facility(char *s)
 {
 {

+ 174 - 202
dprint.h

@@ -25,242 +25,215 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
  */
 
 
-
-
 #ifndef dprint_h
 #ifndef dprint_h
 #define dprint_h
 #define dprint_h
 
 
+#include <assert.h>
 #include <syslog.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__ */
 /* C >= 99 has __func__, older gcc versions have __FUNCTION__ */
 #if __STDC_VERSION__ < 199901L
 #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
 #else
-# define _FUNC_NAME_ __func__
+#	define _FUNC_NAME_ __func__
 #endif
 #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
 #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 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
 #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 { \
 			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; \
 					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; \
 					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 { \
 			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; \
 					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; \
 					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
 #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 */
 /* kamailio/openser compatibility */
 
 
@@ -274,5 +247,4 @@ int log_facility_fixup(void *handle, str *name, void **val);
 #define LM_INFO INFO
 #define LM_INFO INFO
 #define LM_DBG DEBUG
 #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 */
 	struct socket_info* orig_send_sock; /* initial send_sock */
 	int ret;
 	int ret;
 	struct ip_addr ip; /* debugging only */
 	struct ip_addr ip; /* debugging only */
+	char proto;
 #ifdef USE_DNS_FAILOVER
 #ifdef USE_DNS_FAILOVER
 	struct socket_info* prev_send_sock;
 	struct socket_info* prev_send_sock;
 	int err;
 	int err;
@@ -354,6 +355,7 @@ int forward_request(struct sip_msg* msg, str* dst, unsigned short port,
 	
 	
 	buf=0;
 	buf=0;
 	orig_send_sock=send_info->send_sock;
 	orig_send_sock=send_info->send_sock;
+	proto=send_info->proto;
 	ret=0;
 	ret=0;
 
 
 	if(dst){
 	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)){
 		if (cfg_get(core, core_cfg, use_dns_failover)){
 			dns_srv_handle_init(&dns_srv_h);
 			dns_srv_handle_init(&dns_srv_h);
 			err=dns_sip_resolve2su(&dns_srv_h, &send_info->to, dst, port,
 			err=dns_sip_resolve2su(&dns_srv_h, &send_info->to, dst, port,
-									&send_info->proto, dns_flags);
+									&proto, dns_flags);
 			if (err!=0){
 			if (err!=0){
 				LOG(L_ERR, "ERROR: forward_request: resolving \"%.*s\""
 				LOG(L_ERR, "ERROR: forward_request: resolving \"%.*s\""
 						" failed: %s [%d]\n", dst->len, ZSW(dst->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
 		}else
 #endif
 #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,"
 			LOG(L_ERR, "ERROR: forward_request: bad host name %.*s,"
 						" dropping packet\n", dst->len, ZSW(dst->s));
 						" dropping packet\n", dst->len, ZSW(dst->s));
 			ret=E_BAD_ADDRESS;
 			ret=E_BAD_ADDRESS;
@@ -410,12 +412,11 @@ int forward_request(struct sip_msg* msg, str* dst, unsigned short port,
 	do{
 	do{
 #endif
 #endif
 		if (orig_send_sock==0) /* no forced send_sock => find it **/
 		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){
 		if (send_info->send_sock==0){
 			LOG(L_ERR, "forward_req: ERROR: cannot forward to af %d, proto %d "
 			LOG(L_ERR, "forward_req: ERROR: cannot forward to af %d, proto %d "
 						"no corresponding listening socket\n",
 						"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;
 			ret=ser_error=E_NO_SOCKET;
 #ifdef USE_DNS_FAILOVER
 #ifdef USE_DNS_FAILOVER
 			/* continue, maybe we find a socket for some other ip */
 			/* 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;
 			prev_send_sock=send_info->send_sock;
 #endif
 #endif
 			if (buf) pkg_free(buf);
 			if (buf) pkg_free(buf);
+			send_info->proto=proto;
 			buf = build_req_buf_from_sip_req(msg, &len, send_info);
 			buf = build_req_buf_from_sip_req(msg, &len, send_info);
 			if (!buf){
 			if (!buf){
 				LOG(L_ERR, "ERROR: forward_request: building failed\n");
 				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) &&
 	}while(dst && cfg_get(core, core_cfg, use_dns_failover) &&
 			dns_srv_handle_next(&dns_srv_h, err) && 
 			dns_srv_handle_next(&dns_srv_h, err) && 
 			((err=dns_sip_resolve2su(&dns_srv_h, &send_info->to, dst, port,
 			((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)){
 	if ((err!=0) && (err!=-E_DNS_EOR)){
 		LOG(L_ERR, "ERROR:  resolving %.*s host name in uri"
 		LOG(L_ERR, "ERROR:  resolving %.*s host name in uri"
 							" failed: %s [%d] (dropping packet)\n",
 							" failed: %s [%d] (dropping packet)\n",

+ 8 - 8
main.c

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

+ 57 - 9
modules/tm/README

@@ -74,9 +74,11 @@ Jiri Kuthan
         1.4.23. t_any_replied() 
         1.4.23. t_any_replied() 
         1.4.24. t_grep_status("code") 
         1.4.24. t_grep_status("code") 
         1.4.25. t_is_canceled() 
         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
    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.
    Forwards the CANCEL if the corresponding INVITE transaction exists.
    The function is supposed to be used at the very beginning of the
    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
    CANCELs were successfully sent to the pending branches, true if the
    INVITE was not found, and false in case of any error.
    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 (method == CANCEL) {
         if (!t_relay_cancel()) {  # implicit drop if relaying was successful,
         if (!t_relay_cancel()) {  # implicit drop if relaying was successful,
                                   # nothing to do
                                   # nothing to do
@@ -1179,14 +1195,46 @@ if (method == CANCEL) {
         # do the same as for INVITEs
         # 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
    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
    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
    branch is added to the transaction, or it is explicitly replied in the
    script!
    script!
 
 
-   Example 51. t_drop_replies() usage
+   Example 53. t_drop_replies() usage
 ...
 ...
 failure_route[0]{
 failure_route[0]{
         if (t_check_status("5[0-9][0-9]")){
         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
    Forces the modifications of the processed SIP message to be saved in
    shared memory before t_relay() is called. The new branches which are
    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
    The transaction must be created by t_newtran() before calling
    t_save_lumps().
    t_save_lumps().
 
 
-   Example 52. t_save_lumps() usage
+   Example 54. t_save_lumps() usage
 route {
 route {
         ...
         ...
         t_newtran();
         t_newtran();

+ 33 - 28
modules/tm/dlg.c

@@ -54,12 +54,6 @@
 #define NORMAL_ORDER 0  /* Create route set in normal order - UAS */
 #define NORMAL_ORDER 0  /* Create route set in normal order - UAS */
 #define REVERSE_ORDER 1 /* Create route set in reverse order - UAC */
 #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
 #ifdef DIALOG_CALLBACKS
 
 
@@ -221,11 +215,17 @@ static inline int str_duplicate(str* _d, str* _s)
 
 
 /*
 /*
  * Calculate dialog hooks
  * 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)
 static inline int calculate_hooks(dlg_t* _d)
 {
 {
 	str* uri;
 	str* uri;
 	struct sip_uri puri;
 	struct sip_uri puri;
+	int nhop;
 
 
 	/* we might re-calc. some existing hooks =>
 	/* we might re-calc. some existing hooks =>
 	 * reset all the hooks to 0 */
 	 * 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;
 			else _d->hooks.request_uri = &_d->rem_uri;
 			_d->hooks.next_hop = &_d->route_set->nameaddr.uri;
 			_d->hooks.next_hop = &_d->route_set->nameaddr.uri;
 			_d->hooks.first_route = _d->route_set;
 			_d->hooks.first_route = _d->route_set;
+			nhop = F_RB_NH_LOOSE;
 		} else {
 		} else {
 			_d->hooks.request_uri = &_d->route_set->nameaddr.uri;
 			_d->hooks.request_uri = &_d->route_set->nameaddr.uri;
 			_d->hooks.next_hop = _d->hooks.request_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;
 				_d->hooks.last_route = &_d->rem_target;
 			else 
 			else 
 				_d->hooks.last_route = NULL; /* ? */
 				_d->hooks.last_route = NULL; /* ? */
+			nhop = F_RB_NH_STRICT;
 		}
 		}
 	} else {
 	} else {
 		if (_d->rem_target.s) _d->hooks.request_uri = &_d->rem_target;
 		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;
 		if (_d->dst_uri.s) _d->hooks.next_hop = &_d->dst_uri;
 		else _d->hooks.next_hop = _d->hooks.request_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.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)) {
 	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);
 		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;
 			if (str_duplicate(&_d->rem_target, &contact) < 0) return -4;
 		}
 		}
 
 
-		calculate_hooks(_d);
+		if (calculate_hooks(_d) < 0)
+			return -1;
 	}
 	}
 
 
 	return 0;
 	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;
 			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 calculate_routeset_length(dlg_t* _d)
 {
 {
 	int len;
 	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;
 		len += ptr->len;
-		ptr = ptr->next;
-		if (ptr) len += ROUTE_SEPARATOR_LEN;
-	} 
-
+		len += ROUTE_SEPARATOR_LEN;
+	}
 	if (_d->hooks.last_route) {
 	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;
 	return len;
 }
 }
 
 
-
 /*
 /*
  *
  *
  * Print the route set
  * 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 (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");
 		LOG(L_ERR, "set_dlg_target(): Error while calculating hooks\n");
 		return -1;
 		return -1;
 	}
 	}

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

@@ -841,6 +841,29 @@ failure_route[0]{
 	</example>
 	</example>
 	</section>
 	</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">
     <section id="t_relay_cancel">
 	<title>
 	<title>
 	    <function>t_relay_cancel()</function>
 	    <function>t_relay_cancel()</function>
@@ -877,6 +900,50 @@ if (method == CANCEL) {
 	</example>
 	</example>
     </section>
     </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">
     <section id="t_drop_replies">
 	<title>
 	<title>
 	    <function>t_drop_replies()</function>
 	    <function>t_drop_replies()</function>

+ 6 - 0
modules/tm/h_table.c

@@ -65,6 +65,7 @@
 #include "h_table.h"
 #include "h_table.h"
 #include "fix_lumps.h" /* free_via_clen_lump */
 #include "fix_lumps.h" /* free_via_clen_lump */
 #include "timer.h"
 #include "timer.h"
+#include "uac.h" /* free_local_ack */
 
 
 
 
 static enum kill_reason kr;
 static enum kill_reason kr;
@@ -174,6 +175,11 @@ void free_cell( struct cell* dead_cell )
 #endif
 #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 */
 	/* collected to tags */
 	tt=dead_cell->fwded_totags;
 	tt=dead_cell->fwded_totags;
 	while(tt) {
 	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_REPLIED	0x20 /* reply received */
 #define F_RB_CANCELED	0x40 /* rb/branch canceled */
 #define F_RB_CANCELED	0x40 /* rb/branch canceled */
 #define F_RB_DEL_TIMER	0x80 /* timer should be deleted if active */
 #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 */
 /* if canceled or intended to be canceled, return true */
@@ -154,10 +158,10 @@ typedef struct retr_buf
 	short activ_type;
 	short activ_type;
 	/* set to status code if the buffer is a reply,
 	/* set to status code if the buffer is a reply,
 	0 if request or -1 if local CANCEL */
 	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 */
 	volatile unsigned char t_active; /* timer active */
 	unsigned short branch; /* no more then 65k branches :-) */
 	unsigned short branch; /* no more then 65k branches :-) */
-	short   buffer_len;
+	short buffer_len;
 	char *buffer;
 	char *buffer;
 	/*the cell that contains this retrans_buff*/
 	/*the cell that contains this retrans_buff*/
 	struct cell* my_T;
 	struct cell* my_T;
@@ -207,6 +211,16 @@ typedef struct ua_client
 	str              uri;
 	str              uri;
 	/* if we don't store, we at least want to know the status */
 	/* if we don't store, we at least want to know the status */
 	int             last_received;
 	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;
 }ua_client_type;
 
 
 
 
@@ -237,6 +251,10 @@ struct totag_elem {
 #define T_IN_AGONY (1<<5) /* set if waiting to die (delete timer)
 #define T_IN_AGONY (1<<5) /* set if waiting to die (delete timer)
                              TODO: replace it with del on unref */
                              TODO: replace it with del on unref */
 #define T_AUTO_INV_100 (1<<6) /* send an 100 reply automatically  to inv. */
 #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)
 #define T_DONT_FORK   (T_CANCELED|T_6xx)
 
 
 /* unsigned short should be enough for a retr. timer: max. 65535 ticks =>
 /* 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 )
 													unsigned int hash )
 {
 {
 	p_cell->label = _tm_table->entries[hash].next_label++;
 	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;
 	p_cell->hash_index=hash;
 	/* insert at the beginning */
 	/* insert at the beginning */
 	clist_insert(&_tm_table->entries[hash], p_cell, next_c, prev_c);
 	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_DESTROY_N          15  /* called on transaction destroy */
 #define TMCB_E2ECANCEL_IN_N     16
 #define TMCB_E2ECANCEL_IN_N     16
 #define TMCB_E2EACK_RETR_IN_N   17
 #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
 #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
 #else
-#define TMCB_MAX_N              17
+#define TMCB_MAX_N              18
 #endif
 #endif
 
 
 
 
@@ -103,6 +106,9 @@ struct cell;
 #define TMCB_DESTROY          (1<<TMCB_DESTROY_N)
 #define TMCB_DESTROY          (1<<TMCB_DESTROY_N)
 #define TMCB_E2ECANCEL_IN     (1<<TMCB_E2ECANCEL_IN_N)
 #define TMCB_E2ECANCEL_IN     (1<<TMCB_E2ECANCEL_IN_N)
 #define TMCB_E2EACK_RETR_IN   (1<<TMCB_E2EACK_RETR_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
 #ifdef TMCB_ONSEND
 #define TMCB_REQUEST_SENT      (1<<TMCB_REQUEST_SENT_N)
 #define TMCB_REQUEST_SENT      (1<<TMCB_REQUEST_SENT_N)
 #define TMCB_RESPONSE_SENT     (1<<TMCB_RESPONSE_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
  *  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.
  *  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
 	the callback's param MUST be in shared memory and will
 	NOT be freed by TM; you must do it yourself from the
 	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 ! */
 			/* CSeq only the number without method ! */
 			if (get_cseq(t_msg)->number.len!=get_cseq(p_msg)->number.len)
 			if (get_cseq(t_msg)->number.len!=get_cseq(p_msg)->number.len)
 				continue;
 				continue;
-			if (! EQ_LEN(from)) continue;
 			/* To only the uri -- to many UACs screw up tags  */
 			/* To only the uri -- to many UACs screw up tags  */
 			if (get_to(t_msg)->uri.len!=get_to(p_msg)->uri.len)
 			if (get_to(t_msg)->uri.len!=get_to(p_msg)->uri.len)
 				continue;
 				continue;
 			if (!EQ_STR(callid)) continue;
 			if (!EQ_STR(callid)) continue;
 			if (memcmp(get_cseq(t_msg)->number.s, get_cseq(p_msg)->number.s,
 			if (memcmp(get_cseq(t_msg)->number.s, get_cseq(p_msg)->number.s,
 				get_cseq(p_msg)->number.len)!=0) continue;
 				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,
 			if (memcmp(get_to(t_msg)->uri.s, get_to(p_msg)->uri.s,
 				get_to(t_msg)->uri.len)!=0) continue;
 				get_to(t_msg)->uri.len)!=0) continue;
 			
 			
 			/* it is e2e ACK/200 */
 			/* it is e2e ACK/200 */
 			if (p_cell->uas.status<300) {
 			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 */
 				/* all criteria for proxied ACK are ok */
 				if (likely(p_cell->relayed_reply_branch!=-2)) {
 				if (likely(p_cell->relayed_reply_branch!=-2)) {
 					if (unlikely(has_tran_tmcbs(p_cell, 
 					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))
 				if (dlg_matching(p_cell, p_msg))
 					goto found;
 					goto found;
 				continue;
 				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 
 			/* 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;
     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, 
 int t_lookup_ident(struct cell ** trans, unsigned int hash_index, 
 					unsigned int label)
 					unsigned int label)
 {
 {
@@ -1493,7 +1537,11 @@ int t_lookup_ident(struct cell ** trans, unsigned int hash_index,
 	}
 	}
 	
 	
 	LOCK_HASH(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]);
 	hash_bucket=&(get_tm_table()->entries[hash_index]);
 	/* all the transactions from the entry are compared */
 	/* all the transactions from the entry are compared */
 	clist_foreach(hash_bucket, p_cell, next_c){
 	clist_foreach(hash_bucket, p_cell, next_c){

+ 512 - 85
modules/tm/t_msgbuilder.c

@@ -50,7 +50,9 @@
 
 
 #include "defs.h"
 #include "defs.h"
 
 
-
+#ifdef EXTRA_DEBUG
+#include <assert.h>
+#endif
 #include "../../comp_defs.h"
 #include "../../comp_defs.h"
 #include "../../hash_func.h"
 #include "../../hash_func.h"
 #include "../../globals.h"
 #include "../../globals.h"
@@ -69,24 +71,24 @@
 #include "../../cfg_core.h" /* cfg_get(core, core_cfg, use_dns_failover) */
 #include "../../cfg_core.h" /* cfg_get(core, core_cfg, use_dns_failover) */
 #endif
 #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) \
 #define  append_mem_block(_d,_s,_len) \
 		do{\
 		do{\
 			memcpy((_d),(_s),(_len));\
 			memcpy((_d),(_s),(_len));\
 			(_d) += (_len);\
 			(_d) += (_len);\
-		}while(0);
+		}while(0)
 
 
 #define append_str(_p,_str) \
 #define append_str(_p,_str) \
 	do{  \
 	do{  \
 		memcpy((_p), (_str).s, (_str).len); \
 		memcpy((_p), (_str).s, (_str).len); \
 		(_p)+=(_str).len;  \
 		(_p)+=(_str).len;  \
- 	} while(0);
+ 	} while(0)
 
 
 
 
 /* Build a local request based on a previous request; main
 /* Build a local request based on a previous request; main
@@ -375,10 +377,13 @@ error:
 }
 }
 
 
 
 
-struct rte {
+typedef struct rte {
 	rr_t* ptr;
 	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;
 	struct rte* next;
-};
+} rte_t;
 
 
   	 
   	 
 static inline void free_rte_list(struct rte* list)
 static inline void free_rte_list(struct rte* list)
@@ -388,74 +393,13 @@ static inline void free_rte_list(struct rte* list)
 	while(list) {
 	while(list) {
 		ptr = list;
 		ptr = list;
 		list = list->next;
 		list = list->next;
+		if (ptr->free_rr)
+			free_rr(&ptr->ptr);
 		pkg_free(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)
 static inline int calc_routeset_len(struct rte* list, str* contact)
 {
 {
 	struct rte* ptr;
 	struct rte* ptr;
@@ -547,7 +491,429 @@ static inline int get_contact_uri(struct sip_msg* msg, str* uri)
 	return 0;
 	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
       * 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)
 	  * generates local ACK to 200 OK (on behalf of applications using uac)
       */
       */
 char *build_dlg_ack(struct sip_msg* rpl, struct cell *Trans, 
 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;
 	char *req_buf, *p, *via;
 	unsigned int via_len;
 	unsigned int via_len;
@@ -568,18 +934,47 @@ char *build_dlg_ack(struct sip_msg* rpl, struct cell *Trans,
 	struct rte* list;
 	struct rte* list;
 	str contact, ruri, *cont;
 	str contact, ruri, *cont;
 	str next_hop;
 	str next_hop;
+	str body_len;
+	str _to, *to = &_to;
 #ifdef USE_DNS_FAILOVER
 #ifdef USE_DNS_FAILOVER
 	struct dns_srv_handle dns_h;
 	struct dns_srv_handle dns_h;
 #endif
 #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;
 		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;
 		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)) {
 	if ((contact.s != ruri.s) || (contact.len != ruri.len)) {
 		     /* contact != ruri means that the next
 		     /* contact != ruri means that the next
 		      * hop is a strict router, cont will be non-zero
 		      * 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 */
 	     /* User Agent */
 	if (server_signature) *len += USER_AGENT_LEN + CRLF_LEN;
 	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 */
 	     /* 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);
 	req_buf = shm_malloc(*len + 1);
+#endif
 	if (!req_buf) {
 	if (!req_buf) {
-		LOG(L_ERR, "build_dlg_ack: Cannot allocate memory\n");
+		ERR("Cannot allocate memory (%u+1)\n", *len);
 		goto error01;
 		goto error01;
 	}
 	}
 	p = req_buf;
 	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);
 		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;
 	*p = 0;
 	
 	
 	pkg_free(via);
 	pkg_free(via);
@@ -692,7 +1119,7 @@ char *build_dlg_ack(struct sip_msg* rpl, struct cell *Trans,
  error:
  error:
 	free_rte_list(list);
 	free_rte_list(list);
 	return 0;
 	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);
      	if (body) memapp(w, body->s, body->len);
 
 
 #ifdef EXTRA_DEBUG
 #ifdef EXTRA_DEBUG
-	if (w-buf != *len ) abort();
+	assert(w-buf == *len);
 #endif
 #endif
 
 
 	pkg_free(via.s);
 	pkg_free(via.s);

+ 2 - 15
modules/tm/t_msgbuilder.h

@@ -53,19 +53,6 @@
 #define TOTAG_LEN (sizeof(TOTAG)-1)
 #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,
 char *build_local(struct cell *Trans, unsigned int branch,
 	unsigned int *len, char *method, int method_len, str *to);
 	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
  * local ACK to 200 OK (on behalf of applications using uac
  */
  */
 char *build_dlg_ack(struct sip_msg* rpl, struct cell *Trans, 
 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 "../../comp_defs.h"
 #include "../../hash_func.h"
 #include "../../hash_func.h"
 #include "../../dprint.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,
 								int branch, unsigned int *ret_len,
 								struct dest_info*  dst)
 								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;
 	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
 /* flag indicating whether it is requested
  * to drop the already saved replies or not */
  * 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 */
 	/* not >=300 ... it must be 2xx or provisional 1xx */
 	if (new_code>=100) {
 	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 */
 		/* 1xx and 2xx except 100 will be relayed */
 		Trans->uac[branch].last_received=new_code;
 		Trans->uac[branch].last_received=new_code;
-		*should_store=0;
 		*should_relay= new_code==100? -1 : branch;
 		*should_relay= new_code==100? -1 : branch;
 		if (new_code>=200 ) {
 		if (new_code>=200 ) {
 			which_cancel( Trans, cancel_bitmap );
 			which_cancel( Trans, cancel_bitmap );
@@ -1818,7 +1885,9 @@ int reply_received( struct sip_msg  *p_msg )
 													&onsend_params);
 													&onsend_params);
 					}
 					}
 #endif
 #endif
+#ifndef WITH_AS_SUPPORT
 					shm_free(ack);
 					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 */
 /* selects the branch for fwd-ing the reply */
 int t_pick_branch(int inc_branch, int inc_code, struct cell *t, int *res_code);
 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
 /* drops all the replies to make sure
  * that none of them is picked up again
  * that none of them is picked up again

+ 43 - 5
modules/tm/tm.c

@@ -112,6 +112,7 @@
 #include "../../route.h"
 #include "../../route.h"
 #include "../../cfg/cfg.h"
 #include "../../cfg/cfg.h"
 #include "../../globals.h"
 #include "../../globals.h"
+#include "../../timer_ticks.h"
 
 
 #include "config.h"
 #include "config.h"
 #include "sip_msg.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_timeout(struct sip_msg* msg, char*, char*);
 static int t_any_replied(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_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 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_drop_replies(struct sip_msg* msg, char* foo, char* bar);
 static int w_t_save_lumps(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},
 			REQUEST_ROUTE},
 	{"t_lookup_request",   w_t_check,               0, 0,
 	{"t_lookup_request",   w_t_check,               0, 0,
 			REQUEST_ROUTE},
 			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},
 			REQUEST_ROUTE},
 	{T_REPLY,              w_t_reply,               2, fixup_t_reply,
 	{T_REPLY,              w_t_reply,               2, fixup_t_reply,
 			REQUEST_ROUTE | FAILURE_ROUTE },
 			REQUEST_ROUTE | FAILURE_ROUTE },
@@ -360,6 +364,8 @@ static cmd_export_t cmds[]={
 			REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE },
 			REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE },
 	{"t_is_canceled",     t_is_canceled,            0, 0,
 	{"t_is_canceled",     t_is_canceled,            0, 0,
 			REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE },
 			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, 
 	{"t_grep_status",     t_grep_status,            1, fixup_var_int_1, 
 			REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE },
 			REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE },
 	{"t_drop_replies",    w_t_drop_replies,         0, 0,
 	{"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;
 	struct cell *t;
 	char *status, *s = NULL;
 	char *status, *s = NULL;
 	char backup;
 	char backup;
-	int lowest_status, n;
+	int lowest_status, n, ret;
 	fparam_t* fp;
 	fparam_t* fp;
 	regex_t* re = NULL;
 	regex_t* re = NULL;
 	str tmp;
 	str tmp;
@@ -844,7 +850,16 @@ static int t_check_status(struct sip_msg* msg, char *p1, char *foo)
 
 
 	case MODE_ONFAILURE:
 	case MODE_ONFAILURE:
 		/* use the status of the winning reply */
 		/* 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 "
 			LOG(L_CRIT,"BUG:t_check_status: t_pick_branch failed to get "
 				" a final response in MODE_ONFAILURE\n");
 				" a final response in MODE_ONFAILURE\n");
 			goto error;
 			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)
 inline static int w_t_lookup_cancel(struct sip_msg* msg, char* str, char* str2)
 {
 {
 	struct cell *ret;
 	struct cell *ret;
+	int i=0;
 	if (msg->REQ_METHOD==METHOD_CANCEL) {
 	if (msg->REQ_METHOD==METHOD_CANCEL) {
 		ret = t_lookupOriginalT( msg );
 		ret = t_lookupOriginalT( msg );
 		DBG("lookup_original: t_lookupOriginalT returned: %p\n", ret);
 		DBG("lookup_original: t_lookupOriginalT returned: %p\n", ret);
 		if (ret != T_NULL_CELL) {
 		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.
 			/* The cell is reffed by t_lookupOriginalT, but T is not set.
 			So we must unref it before returning. */
 			So we must unref it before returning. */
 			UNREF(ret);
 			UNREF(ret);
-			set_t(T_UNDEFINED);
 			return 1;
 			return 1;
 		}
 		}
-		set_t(T_UNDEFINED);
 	} else {
 	} else {
 		LOG(L_WARN, "WARNING: script error t_lookup_cancel() called for non-CANCEL request\n");
 		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;
 	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 */
 /* 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)
 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
 #ifdef USE_DNS_FAILOVER
 	struct dns_srv_handle dns_h;
 	struct dns_srv_handle dns_h;
 #endif
 #endif
+	long nhtype;
 
 
 	ret=-1;
 	ret=-1;
 	hi=0; /* make gcc happy */
 	hi=0; /* make gcc happy */
@@ -203,7 +204,8 @@ static inline int t_uac_prepare(uac_req_t *uac_r,
 	/*** added by dcm 
 	/*** added by dcm 
 	 * - needed by external ua to send a request within a dlg
 	 * - 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;
 		goto error2;
 
 
 	if (!uac_r->dialog->loc_seq.is_set) {
 	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_IS_INVITE_FLAG;
 		new_cell->flags|=T_AUTO_INV_100 &
 		new_cell->flags|=T_AUTO_INV_100 &
 				(!cfg_get(tm, tm_cfg, tm_auto_inv_100) -1);
 				(!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);
 		lifetime=cfg_get(tm, tm_cfg, tm_max_inv_lifetime);
 	}else
 	}else
 		lifetime=cfg_get(tm, tm_cfg, tm_max_noninv_lifetime);
 		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);
 	set_kr(REQ_FWDED);
 
 
 	request = &new_cell->uac[0].request;
 	request = &new_cell->uac[0].request;
-	
 	request->dst = dst;
 	request->dst = dst;
+	request->flags |= nhtype;
 
 
 	if (!is_ack) {
 	if (!is_ack) {
 #ifdef TM_DEL_UNREF
 #ifdef TM_DEL_UNREF
@@ -402,18 +408,7 @@ void send_prepared_request(struct retr_buf *request)
  */
  */
 int t_uac(uac_req_t *uac_r)
 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;
 	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
  * 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_t)(uac_req_t *uac_r);
 typedef int (*t_uac_with_ids_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);
 		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,
 typedef int (*prepare_request_within_f)(uac_req_t *uac_r,
 		struct retr_buf **dst_req);
 		struct retr_buf **dst_req);
 typedef void (*send_prepared_request_f)(struct retr_buf *request_dst);
 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);
 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
  * Send a transactional request, no dialogs involved
  */
  */

+ 134 - 19
msg_translator.c

@@ -61,6 +61,10 @@
  *              (rfc3486) (andrei)
  *              (rfc3486) (andrei)
  * 2007-08-31  id_builder() and via_builder() are grouped into one function:
  * 2007-08-31  id_builder() and via_builder() are grouped into one function:
  *             create_via_hf() -- tm module needs them as well (Miklos)
  *             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:
 /* Via special params:
@@ -134,6 +138,8 @@
 #include "resolve.h"
 #include "resolve.h"
 #include "ut.h"
 #include "ut.h"
 #include "pt.h"
 #include "pt.h"
+#include "cfg/cfg.h"
+#include "forward.h"
 
 
 
 
 #define append_str(_dest,_src,_len) \
 #define append_str(_dest,_src,_len) \
@@ -148,9 +154,35 @@
 extern char version[];
 extern char version[];
 extern int version_len;
 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,
 char * build_req_buf_from_sip_req( struct sip_msg* msg,
 								unsigned int *returned_len,
 								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* line_buf;
 	char* received_buf;
 	char* received_buf;
 	char* rport_buf;
 	char* rport_buf;
 	char* new_buf;
 	char* new_buf;
 	char* buf;
 	char* buf;
 	unsigned int offset, s_offset, size;
 	unsigned int offset, s_offset, size;
-	struct lump* anchor;
+	struct lump* via_anchor;
+	struct lump* via_lump;
 	struct lump* via_insert_param;
 	struct lump* via_insert_param;
 	str branch;
 	str branch;
+	unsigned int flags;
+	unsigned int udp_mtu;
+	struct dest_info di;
 
 
 	via_insert_param=0;
 	via_insert_param=0;
 	uri_len=0;
 	uri_len=0;
@@ -1445,9 +1511,8 @@ char * build_req_buf_from_sip_req( struct sip_msg* msg,
 	rport_buf=0;
 	rport_buf=0;
 	line_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);
 	body_delta = lumps_len(msg, msg->body_lumps, send_info);
 	if (adjust_clen(msg, body_delta, send_info->proto) < 0) {
 	if (adjust_clen(msg, body_delta, send_info->proto) < 0) {
 		LOG(L_ERR, "ERROR: build_req_buf_from_sip_req: Error while adjusting"
 		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;
 		goto error00;
 	}
 	}
 
 
-	/* create a the via header */
+	/* create the via header */
 	branch.s=msg->add_to_branch_s;
 	branch.s=msg->add_to_branch_s;
 	branch.len=msg->add_to_branch_len;
 	branch.len=msg->add_to_branch_len;
 
 
 	line_buf = create_via_hf( &via_len, msg, send_info, &branch);
 	line_buf = create_via_hf( &via_len, msg, send_info, &branch);
 	if (!line_buf){
 	if (!line_buf){
-		LOG(L_ERR,"ERROR: build_req_buf_from_sip_req: no via received!\n");
+		LOG(L_ERR,"ERROR: build_req_buf_from_sip_req: "
+					"memory allocation failure\n");
 		goto error00;
 		goto error00;
 	}
 	}
 	/* check if received needs to be added */
 	/* 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
 	 *  - 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
 	 *  rport value if present (if you don't want to overwrite the previous
 	 *  version remove the comments) */
 	 *  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*/)){
 			(msg->via1->rport /*&& msg->via1->rport->value.s==0*/)){
 		if ((rport_buf=rport_builder(msg, &rport_len))==0){
 		if ((rport_buf=rport_builder(msg, &rport_len))==0){
 			LOG(L_ERR, "ERROR: build_req_buf_from_sip_req:"
 			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
 	/* find out where the offset of the first parameter that should be added
 	 * (after host:port), needed by add receive & maybe rport */
 	 * (after host:port), needed by add receive & maybe rport */
 	if (msg->via1->params.s){
 	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*/
 	/* 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
 #ifdef XL_DEBUG
 	LOG(L_ERR, "DEBUG: new_len(%d)=len(%d)+lumps_len\n", new_len, len);
 	LOG(L_ERR, "DEBUG: new_len(%d)=len(%d)+lumps_len\n", new_len, len);
 #endif
 #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){
 	if (msg->new_uri.s){
 		uri_len=msg->new_uri.len;
 		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);
 	memcpy(new_buf+offset, buf+s_offset, len-s_offset);
 	new_buf[new_len]=0;
 	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
 #ifdef DBG_MSG_QA
 	if (new_buf[new_len-1]==0) {
 	if (new_buf[new_len-1]==0) {
 		LOG(L_ERR, "ERROR: build_req_buf_from_sip_req: 0 in the end\n");
 		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;
 	return new_buf;
 
 
 error01:
 error01:
-	if (line_buf) pkg_free(line_buf);
 error02:
 error02:
 	if (received_buf) pkg_free(received_buf);
 	if (received_buf) pkg_free(received_buf);
 error03:
 error03:
 	if (rport_buf) pkg_free(rport_buf);
 	if (rport_buf) pkg_free(rport_buf);
+error04:
+	if (line_buf) pkg_free(line_buf);
 error00:
 error00:
 	*returned_len=0;
 	*returned_len=0;
 	return 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 */
 	/* 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*/)){
 		(msg->via1->rport /*&& msg->via1->rport->value.s==0*/)){
 		if ((rport_buf=rport_builder(msg, &rport_len))==0){
 		if ((rport_buf=rport_builder(msg, &rport_len))==0){
 			LOG(L_ERR, "ERROR: build_res_buf_from_sip_req:"
 			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,
 			int *error,
 			struct dest_info* send_info);
 			struct dest_info* send_info);
 
 
+/** cfg framework fixup */
+void fix_global_req_flags( str* name);
 
 
 #endif
 #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) */
                                 (for failure route use) */
 #define FL_HASH_INDEX  128 /* msg->hash_index contains a valid value (tm 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)                                  \
 #define IFISMETHOD(methodname,firstchar)                                  \
 if (  (*tmp==(firstchar) || *tmp==((firstchar) | 32)) &&                  \
 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].u.data=si;
 				t->val[0].type=SOCKETINFO_ST;
 				t->val[0].type=SOCKETINFO_ST;
 				break;
 				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;
 	return 0;

+ 3 - 1
route_struct.h

@@ -35,6 +35,7 @@
  *  2004-02-24  added LOAD_AVP_T and AVP_TO_URI_T (bogdan)
  *  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-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)
  *  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,
 		AVP_TO_URI_T,
 		FORCE_SEND_SOCKET_T,
 		FORCE_SEND_SOCKET_T,
 		ASSIGN_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,
 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,
 		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/types.h>
 #include <sys/select.h>
 #include <sys/select.h>
 #include <sys/socket.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 <sys/ioctl.h>  /* ioctl() used on write error */
 #include <netinet/in.h>
 #include <netinet/in.h>
 #include <netinet/in_systm.h>
 #include <netinet/in_systm.h>

+ 14 - 0
usr_avp.c

@@ -1015,6 +1015,20 @@ error:
 	return -1;
 	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)
 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_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);
 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);
 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 */
 /* AVP flags functions */
 #define MAX_AVPFLAG  ((unsigned int)( sizeof(avp_flags_t) * CHAR_BIT - 1 - AVP_CUSTOM_FLAGS))
 #define MAX_AVPFLAG  ((unsigned int)( sizeof(avp_flags_t) * CHAR_BIT - 1 - AVP_CUSTOM_FLAGS))