everything: shotgun attempt to put PROTO_WS and PROTO_WSS across core and in modules...
[sip-router] / modules / tm / tm.c
index e769b75..a251826 100644 (file)
  *
  * Copyright (C) 2001-2003 FhG Fokus
  *
- * This file is part of ser, a free SIP server.
+ * This file is part of SIP-router, a free SIP server.
  *
- * ser is free software; you can redistribute it and/or modify
+ * SIP-router is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version
  *
- * For a license to use the ser software under conditions
- * other than those described here, or to purchase support for this
- * software, please contact iptel.org by e-mail at the following addresses:
- *    info@iptel.org
- *
- * ser is distributed in the hope that it will be useful,
+ * SIP-router is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
  *  2008-08-11  sctp support: t_relay_to_sctp, t_replicate_sctp,
  *               t_forward_nonack_sctp (andrei)
  *  2009-03-18  added a new param: auto_inv_100_reason (aheise) 
+ *  2010-03-03  added new params: local_cancel_reason and e2e_cancel_reason
+ *              (andrei)
+ */
+
+/** TM :: Module API (core).
+ * @file 
+ * @ingroup tm
+ */
+
+/**
+ * @defgroup tm TM :: Transaction stateful proxy support
+ *
+   The TM module enables stateful processing of SIP transactions. The main use
+   of stateful logic, which is costly in terms of memory and CPU, is some
+   services that inherently need state. 
+
+   For example, transaction-based accounting (module acc) needs to process 
+   transaction state as opposed to individual messages, and any kinds of 
+   forking must be implemented statefully. Other use of stateful processing 
+   is it trading CPU caused by retransmission processing for memory. 
+   That however only makes sense if CPU consumption per request is huge. 
+   For example, if you want to avoid costly DNS resolution for every 
+   retransmission of a request to an unresolvable destination, use stateful 
+   mode. Then, only the initial message burdens server by DNS queries, 
+   subsequent retransmissions will be dropped and will not result in more 
+   processes blocked by DNS resolution. The price is more memory consumption 
+   and higher processing latency.
+
+ *
+\code
+ * ***************************************************
+ * * Jiri's Source Memorial                          *
+ * *                                                 *
+ * * Welcome, pilgrim ! This is the greatest place   *
+ * * where dramatic changes happend. There are not   *
+ * * many places with a history like this, as there  *
+ * * are not so many people like Jiri, one of the    *
+ * * ser's fathers, who brought everywhere the wind  *
+ * * of change, the flood of clean-up. We all felt   *
+ * * his fatherly eye watching over us day and night.*
+ * *                                                 *
+ * * Please, preserve this codework heritage, as     *
+ * * it's unlikely for fresh, juicy pieces of code to  *
+ * * arise to give him the again the chance to       *
+ * * demonstrate his clean-up and improvement skills.*
+ * *                                                 *
+ * * Hereby, we solicit you to adopt this historical *
+ * * piece of code. For $100, your name will be      *
+ * * be printed in this banner and we will use       *
+ * * collected funds to create and display an ASCII  *
+ * * statue of Jiri  .                               *
+ * ***************************************************
+\endcode
  */
 
 
 #include "../../cfg/cfg.h"
 #include "../../globals.h"
 #include "../../timer_ticks.h"
+#include "../../mod_fix.h"
 
 #include "config.h"
 #include "sip_msg.h"
 #include "t_msgbuilder.h"
 #include "select.h"
 #include "t_serial.h"
+#include "rpc_uac.h"
 
 MODULE_VERSION
 
@@ -144,6 +194,7 @@ static int fixup_on_reply(void** param, int param_no);
 static int fixup_on_branch(void** param, int param_no);
 static int fixup_t_reply(void** param, int param_no);
 static int fixup_on_sl_reply(modparam_t type, void* val);
+static int fixup_t_relay_to(void** param, int param_no);
 
 /* init functions */
 static int mod_init(void);
@@ -179,6 +230,10 @@ inline static int w_t_relay_to_sctp( struct sip_msg  *p_msg , char *proxy,
 inline static int w_t_relay_to_sctp_uri( struct sip_msg*, char*, char*);
 #endif
 inline static int w_t_relay_to_avp(struct sip_msg* msg, char* str,char*);
+inline static int w_t_relay_to(struct sip_msg* msg, char* str,char*);
+inline static int w_t_replicate_uri( struct sip_msg  *p_msg ,
+                               char *uri,       /* sip uri as string or variable */
+                               char *_foo       /* nothing expected */ );
 inline static int w_t_replicate( struct sip_msg  *p_msg ,
                                char *proxy, /* struct proxy_l *proxy expected */
                                char *_foo       /* nothing expected */ );
@@ -227,16 +282,22 @@ static int w_t_reset_retr(struct sip_msg* msg, char* foo, char* bar);
 static int w_t_set_max_lifetime(struct sip_msg* msg, char* inv, char* noninv);
 static int w_t_reset_max_lifetime(struct sip_msg* msg, char* foo, char* bar);
 static int t_set_auto_inv_100(struct sip_msg* msg, char* on_off, char* foo);
+static int t_set_disable_6xx(struct sip_msg* msg, char* on_off, char* foo);
+static int t_set_disable_failover(struct sip_msg* msg, char* on_off, char* f);
+#ifdef CANCEL_REASON_SUPPORT
+static int t_set_no_e2e_cancel_reason(struct sip_msg* msg, char* on_off,
+                                                                               char* f);
+#endif /* CANCEL_REASON_SUPPORT */
 static int t_branch_timeout(struct sip_msg* msg, char*, char*);
 static int t_branch_replied(struct sip_msg* msg, char*, char*);
 static int t_any_timeout(struct sip_msg* msg, char*, char*);
 static int t_any_replied(struct sip_msg* msg, char*, char*);
-static int t_is_canceled(struct sip_msg* msg, char*, char*);
+static int w_t_is_canceled(struct sip_msg* msg, char*, char*);
 static int t_is_expired(struct sip_msg* msg, char*, char*);
 static int t_grep_status(struct sip_msg* msg, char*, char*);
 static int w_t_drop_replies(struct sip_msg* msg, char* foo, char* bar);
 static int w_t_save_lumps(struct sip_msg* msg, char* foo, char* bar);
-static int t_check_trans(struct sip_msg* msg, char* foo, char* bar);
+static int w_t_check_trans(struct sip_msg* msg, char* foo, char* bar);
 
 
 /* by default the fr timers avps are not set, so that the avps won't be
@@ -258,34 +319,36 @@ static cmd_export_t cmds[]={
                        REQUEST_ROUTE},
        {"t_lookup_cancel",    w_t_lookup_cancel,       1, fixup_int_1,
                        REQUEST_ROUTE},
-       {T_REPLY,              w_t_reply,               2, fixup_t_reply,
-                       REQUEST_ROUTE | FAILURE_ROUTE },
+       {"t_reply",              w_t_reply,               2, fixup_t_reply,
+                       REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE },
        {"t_retransmit_reply", w_t_retransmit_reply,    0, 0,
                        REQUEST_ROUTE},
        {"t_release",          w_t_release,             0, 0,
                        REQUEST_ROUTE},
-       {T_RELAY_TO_UDP,       w_t_relay_to_udp,        2, fixup_hostport2proxy,
+       {"t_relay_to_udp",       w_t_relay_to_udp,        2, fixup_hostport2proxy,
                        REQUEST_ROUTE|FAILURE_ROUTE},
-       {T_RELAY_TO_UDP,       w_t_relay_to_udp_uri,    0, 0,
+       {"t_relay_to_udp",       w_t_relay_to_udp_uri,    0, 0,
                        REQUEST_ROUTE|FAILURE_ROUTE},
 #ifdef USE_TCP
-       {T_RELAY_TO_TCP,       w_t_relay_to_tcp,        2, fixup_hostport2proxy,
+       {"t_relay_to_tcp",       w_t_relay_to_tcp,        2, fixup_hostport2proxy,
                        REQUEST_ROUTE|FAILURE_ROUTE},
-       {T_RELAY_TO_TCP,       w_t_relay_to_tcp_uri,    0, 0,
+       {"t_relay_to_tcp",       w_t_relay_to_tcp_uri,    0, 0,
                        REQUEST_ROUTE|FAILURE_ROUTE},
 #endif
 #ifdef USE_TLS
-       {T_RELAY_TO_TLS,       w_t_relay_to_tls,        2, fixup_hostport2proxy,
+       {"t_relay_to_tls",       w_t_relay_to_tls,        2, fixup_hostport2proxy,
                        REQUEST_ROUTE|FAILURE_ROUTE},
-       {T_RELAY_TO_TLS,       w_t_relay_to_tls_uri,    0, 0,
+       {"t_relay_to_tls",       w_t_relay_to_tls_uri,    0, 0,
                        REQUEST_ROUTE|FAILURE_ROUTE},
 #endif
 #ifdef USE_SCTP
-       {T_RELAY_TO_SCTP,       w_t_relay_to_sctp,       2, fixup_hostport2proxy,
+       {"t_relay_to_sctp",       w_t_relay_to_sctp,       2, fixup_hostport2proxy,
                        REQUEST_ROUTE|FAILURE_ROUTE},
-       {T_RELAY_TO_SCTP,       w_t_relay_to_sctp_uri,    0, 0,
+       {"t_relay_to_sctp",       w_t_relay_to_sctp_uri,    0, 0,
                        REQUEST_ROUTE|FAILURE_ROUTE},
 #endif
+       {"t_replicate",        w_t_replicate_uri,       1, fixup_var_str_1,
+                       REQUEST_ROUTE},
        {"t_replicate",        w_t_replicate,           2, fixup_hostport2proxy,
                        REQUEST_ROUTE},
        {"t_replicate_udp",    w_t_replicate_udp,       2, fixup_hostport2proxy,
@@ -304,28 +367,34 @@ static cmd_export_t cmds[]={
 #endif
        {"t_replicate_to", w_t_replicate_to,            2, fixup_proto_hostport2proxy,
                        REQUEST_ROUTE},
-       {T_RELAY,              w_t_relay,               0, 0,
+       {"t_relay",              w_t_relay,               0, 0,
                        REQUEST_ROUTE | FAILURE_ROUTE },
-       {T_RELAY,              w_t_relay2,              2, fixup_hostport2proxy,
+       {"t_relay",              w_t_relay2,              2, fixup_hostport2proxy,
                        REQUEST_ROUTE | FAILURE_ROUTE },
        {"t_relay_to_avp", w_t_relay_to_avp,            2, fixup_proto_hostport2proxy,
                        REQUEST_ROUTE},
-       {T_FORWARD_NONACK,     w_t_forward_nonack,      2, fixup_hostport2proxy,
+       {"t_relay_to",                  w_t_relay_to,           0, 0,
+                       REQUEST_ROUTE | FAILURE_ROUTE },
+       {"t_relay_to",                  w_t_relay_to,           1, fixup_t_relay_to,
+                       REQUEST_ROUTE | FAILURE_ROUTE },
+       {"t_relay_to",                  w_t_relay_to,           2, fixup_t_relay_to,
+                       REQUEST_ROUTE | FAILURE_ROUTE },
+       {"t_forward_nonack",     w_t_forward_nonack,      2, fixup_hostport2proxy,
                        REQUEST_ROUTE},
-       {T_FORWARD_NONACK_URI, w_t_forward_nonack_uri,  0, 0,
+       {"t_forward_nonack_uri", w_t_forward_nonack_uri,  0, 0,
                        REQUEST_ROUTE},
-       {T_FORWARD_NONACK_UDP, w_t_forward_nonack_udp,  2, fixup_hostport2proxy,
+       {"t_forward_nonack_udp", w_t_forward_nonack_udp,  2, fixup_hostport2proxy,
                        REQUEST_ROUTE},
 #ifdef USE_TCP
-       {T_FORWARD_NONACK_TCP, w_t_forward_nonack_tcp,  2, fixup_hostport2proxy,
+       {"t_forward_nonack_tcp", w_t_forward_nonack_tcp,  2, fixup_hostport2proxy,
                        REQUEST_ROUTE},
 #endif
 #ifdef USE_TLS
-       {T_FORWARD_NONACK_TLS, w_t_forward_nonack_tls,  2, fixup_hostport2proxy,
+       {"t_forward_nonack_tls", w_t_forward_nonack_tls,  2, fixup_hostport2proxy,
                        REQUEST_ROUTE},
 #endif
 #ifdef USE_SCTP
-       {T_FORWARD_NONACK_SCTP, w_t_forward_nonack_sctp, 2, fixup_hostport2proxy,
+       {"t_forward_nonack_sctp", w_t_forward_nonack_sctp, 2, fixup_hostport2proxy,
                        REQUEST_ROUTE},
 #endif
        {"t_forward_nonack_to", w_t_forward_nonack_to,  2, fixup_proto_hostport2proxy,
@@ -333,9 +402,9 @@ static cmd_export_t cmds[]={
        {"t_relay_cancel",     w_t_relay_cancel,        0, 0,
                        REQUEST_ROUTE},
        {"t_on_failure",       w_t_on_negative,         1, fixup_on_failure,
-                       REQUEST_ROUTE | FAILURE_ROUTE | ONREPLY_ROUTE },
+                       REQUEST_ROUTE | FAILURE_ROUTE | TM_ONREPLY_ROUTE | BRANCH_ROUTE },
        {"t_on_reply",         w_t_on_reply,            1, fixup_on_reply,
-                       REQUEST_ROUTE | FAILURE_ROUTE | ONREPLY_ROUTE },
+                       REQUEST_ROUTE | FAILURE_ROUTE | TM_ONREPLY_ROUTE | BRANCH_ROUTE },
        {"t_on_branch",       w_t_on_branch,         1, fixup_on_branch,
                        REQUEST_ROUTE | FAILURE_ROUTE },
        {"t_check_status",     t_check_status,          1, fixup_t_check_status,
@@ -345,38 +414,53 @@ static cmd_export_t cmds[]={
        {"t_write_unix",      t_write_unix,             2, fixup_t_write,
                        REQUEST_ROUTE | FAILURE_ROUTE },
        {"t_set_fr",          t_set_fr_inv,             1, fixup_var_int_1,
-                       REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE },
+                       REQUEST_ROUTE|TM_ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE },
        {"t_set_fr",          t_set_fr_all,             2, fixup_var_int_12,
-                       REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE },
+                       REQUEST_ROUTE|TM_ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE },
        {"t_reset_fr",        w_t_reset_fr,             0, 0,
-                       REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE },
+                       REQUEST_ROUTE|TM_ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE },
        {"t_set_retr",        w_t_set_retr,               2, fixup_var_int_12,
-                       REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE },
+                       REQUEST_ROUTE|TM_ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE },
        {"t_reset_retr",      w_t_reset_retr,           0, 0,
-                       REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE },
+                       REQUEST_ROUTE|TM_ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE },
        {"t_set_max_lifetime", w_t_set_max_lifetime,      2, fixup_var_int_12,
-                       REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE },
+                       REQUEST_ROUTE|TM_ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE },
        {"t_reset_max_lifetime", w_t_reset_max_lifetime, 0, 0,
-                       REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE },
+                       REQUEST_ROUTE|TM_ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE },
        {"t_set_auto_inv_100", t_set_auto_inv_100,       1, fixup_var_int_1,
                                                                                                          REQUEST_ROUTE},
+       {"t_set_disable_6xx", t_set_disable_6xx,         1, fixup_var_int_1,
+                       REQUEST_ROUTE|TM_ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE },
+       {"t_set_disable_failover", t_set_disable_failover, 1, fixup_var_int_1,
+                       REQUEST_ROUTE|TM_ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE },
+#ifdef CANCEL_REASON_SUPPORT
+       {"t_set_no_e2e_cancel_reason", t_set_no_e2e_cancel_reason, 1,
+               fixup_var_int_1,
+                       REQUEST_ROUTE|TM_ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE },
+       /* alias for t_set_no_e2e_cancel_reason */
+       {"t_disable_e2e_cancel_reason", t_set_no_e2e_cancel_reason, 1,
+               fixup_var_int_1,
+                       REQUEST_ROUTE|TM_ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE },
+#endif /* CANCEL_REASON_SUPPORT */
        {"t_branch_timeout",  t_branch_timeout,         0, 0,  FAILURE_ROUTE},
        {"t_branch_replied",  t_branch_replied,         0, 0,  FAILURE_ROUTE},
        {"t_any_timeout",     t_any_timeout,            0, 0, 
-                       REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE },
+                       REQUEST_ROUTE|TM_ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE },
        {"t_any_replied",     t_any_replied,            0, 0, 
-                       REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE },
-       {"t_is_canceled",     t_is_canceled,            0, 0,
-                       REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE },
+                       REQUEST_ROUTE|TM_ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE },
+       {"t_is_canceled",     w_t_is_canceled,          0, 0,
+                       REQUEST_ROUTE|TM_ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE },
        {"t_is_expired",      t_is_expired,             0, 0,
-                       REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE },
+                       REQUEST_ROUTE|TM_ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE },
        {"t_grep_status",     t_grep_status,            1, fixup_var_int_1, 
-                       REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE },
+                       REQUEST_ROUTE|TM_ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE },
        {"t_drop_replies",    w_t_drop_replies,         0, 0,
                        FAILURE_ROUTE},
+       {"t_drop_replies",    w_t_drop_replies,         1, 0,
+                       FAILURE_ROUTE},
        {"t_save_lumps",      w_t_save_lumps,           0, 0,
                        REQUEST_ROUTE},
-       {"t_check_trans",       t_check_trans,                          0, 0,
+       {"t_check_trans",         w_t_check_trans,                      0, 0,
                        REQUEST_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE },
 
        {"t_load_contacts", t_load_contacts,            0, 0,
@@ -385,39 +469,8 @@ static cmd_export_t cmds[]={
                        REQUEST_ROUTE | FAILURE_ROUTE},
 
        /* not applicable from the script */
-       {"register_tmcb",      (cmd_function)register_tmcb,     NO_SCRIPT,   0, 0},
        {"load_tm",            (cmd_function)load_tm,           NO_SCRIPT,   0, 0},
-       {T_REPLY_WB,           (cmd_function)t_reply_with_body, NO_SCRIPT,   0, 0},
-       {T_IS_LOCAL,           (cmd_function)t_is_local,        NO_SCRIPT,   0, 0},
-       {T_GET_TI,             (cmd_function)t_get_trans_ident, NO_SCRIPT,   0, 0},
-       {T_LOOKUP_IDENT,       (cmd_function)t_lookup_ident,    NO_SCRIPT,   0, 0},
-       {T_ADDBLIND,           (cmd_function)add_blind_uac,     NO_SCRIPT,   0, 0},
-       {"t_request_within",   (cmd_function)req_within,        NO_SCRIPT,   0, 0},
-       {"t_request_outside",  (cmd_function)req_outside,       NO_SCRIPT,   0, 0},
-       {"t_request",          (cmd_function)request,           NO_SCRIPT,   0, 0},
-       {"new_dlg_uac",        (cmd_function)new_dlg_uac,       NO_SCRIPT,   0, 0},
-       {"dlg_response_uac",   (cmd_function)dlg_response_uac,  NO_SCRIPT,   0, 0},
-       {"new_dlg_uas",        (cmd_function)new_dlg_uas,       NO_SCRIPT,   0, 0},
-       {"update_dlg_uas",     (cmd_function)update_dlg_uas,    NO_SCRIPT,   0, 0},
-       {"dlg_request_uas",    (cmd_function)dlg_request_uas,   NO_SCRIPT,   0, 0},
-       {"set_dlg_target",     (cmd_function)set_dlg_target,    NO_SCRIPT,   0, 0},
-       {"free_dlg",           (cmd_function)free_dlg,          NO_SCRIPT,   0, 0},
-       {"print_dlg",          (cmd_function)print_dlg,         NO_SCRIPT,   0, 0},
-       {T_GETT,               (cmd_function)get_t,             NO_SCRIPT,   0, 0},
-       {"calculate_hooks",    (cmd_function)w_calculate_hooks, NO_SCRIPT,   0, 0},
-       {"t_uac",              (cmd_function)t_uac,             NO_SCRIPT,   0, 0},
-       {"t_uac_with_ids",     (cmd_function)t_uac_with_ids,    NO_SCRIPT,   0, 0},
-       {"t_unref",            (cmd_function)t_unref,           NO_SCRIPT,   0, 0},
-       {"run_failure_handlers", (cmd_function)run_failure_handlers, NO_SCRIPT,   0, 0},
-       {"cancel_uacs",        (cmd_function)cancel_uacs,       NO_SCRIPT,   0, 0},
-       {"cancel_all_uacs",    (cmd_function)cancel_all_uacs,        NO_SCRIPT,   0, 0},
-#ifdef WITH_AS_SUPPORT
-       {"ack_local_uac",      (cmd_function)ack_local_uac,     NO_SCRIPT,   0, 0},
-       {"t_get_canceled_ident",   (cmd_function)t_get_canceled_ident,  NO_SCRIPT,
-                       0, 0},
-#endif
-       {"t_suspend",          (cmd_function)t_suspend,         NO_SCRIPT,   0, 0},
-       {"t_continue",         (cmd_function)t_continue,        NO_SCRIPT,   0, 0},
+       {"load_xtm",           (cmd_function)load_xtm,          NO_SCRIPT,   0, 0},
        {0,0,0,0,0}
 };
 
@@ -429,13 +482,13 @@ static param_export_t params[]={
        {"fr_inv_timer",        PARAM_INT, &default_tm_cfg.fr_inv_timeout        },
        {"wt_timer",            PARAM_INT, &default_tm_cfg.wait_timeout          },
        {"delete_timer",        PARAM_INT, &default_tm_cfg.delete_timeout        },
-       {"retr_timer1",         PARAM_INT, &default_tm_cfg.rt_t1_timeout         },
-       {"retr_timer2"  ,       PARAM_INT, &default_tm_cfg.rt_t2_timeout         },
+       {"retr_timer1",         PARAM_INT, &default_tm_cfg.rt_t1_timeout_ms      },
+       {"retr_timer2"  ,       PARAM_INT, &default_tm_cfg.rt_t2_timeout_ms      },
        {"max_inv_lifetime",    PARAM_INT, &default_tm_cfg.tm_max_inv_lifetime   },
        {"max_noninv_lifetime", PARAM_INT, &default_tm_cfg.tm_max_noninv_lifetime},
        {"noisy_ctimer",        PARAM_INT, &default_tm_cfg.noisy_ctimer          },
        {"auto_inv_100",        PARAM_INT, &default_tm_cfg.tm_auto_inv_100       },
-       {"auto_inv_100_reason", PARAM_STRING, &default_tm_cfg.tm_auto_inv_100_r  },    
+       {"auto_inv_100_reason", PARAM_STRING, &default_tm_cfg.tm_auto_inv_100_r  },
        {"unix_tx_timeout",     PARAM_INT, &default_tm_cfg.tm_unix_tx_timeout    },
        {"restart_fr_on_each_reply", PARAM_INT,
                                                                        &default_tm_cfg.restart_fr_on_each_reply},
@@ -460,10 +513,15 @@ static param_export_t params[]={
        {"cancel_b_method",     PARAM_INT, &default_tm_cfg.cancel_b_flags},
        {"reparse_on_dns_failover", PARAM_INT, &default_tm_cfg.reparse_on_dns_failover},
        {"on_sl_reply",         PARAM_STRING|PARAM_USE_FUNC, fixup_on_sl_reply   },
-
-       {"fr_inv_timer_next",   PARAM_INT, &default_tm_cfg.fr_inv_timeout_next   },
        {"contacts_avp",        PARAM_STRING, &contacts_avp_param                },
-
+       {"disable_6xx_block",   PARAM_INT, &default_tm_cfg.disable_6xx           },
+       {"local_ack_mode",      PARAM_INT, &default_tm_cfg.local_ack_mode        },
+       {"failure_reply_mode",  PARAM_INT, &failure_reply_mode                   },
+       {"faked_reply_prio",    PARAM_INT, &faked_reply_prio                     },
+#ifdef CANCEL_REASON_SUPPORT
+       {"local_cancel_reason", PARAM_INT, &default_tm_cfg.local_cancel_reason   },
+       {"e2e_cancel_reason",   PARAM_INT, &default_tm_cfg.e2e_cancel_reason     },
+#endif /* CANCEL_REASON_SUPPORT */
        {0,0,0}
 };
 
@@ -498,7 +556,7 @@ static int fixup_routes(char* r_type, struct route_list* rt, void** param)
                LOG(L_ERR, "ERROR: tm: fixup_routes: route_get failed\n");
                return E_UNSPEC;
        }
-       if (rt->rlist[i]==0){
+       if (r_type && rt->rlist[i]==0){
                LOG(L_WARN, "WARNING: %s(\"%s\"): empty/non existing route\n",
                                r_type, (char*)*param);
        }
@@ -508,12 +566,8 @@ static int fixup_routes(char* r_type, struct route_list* rt, void** param)
 
 static int fixup_t_reply(void** param, int param_no)
 {
-       int ret;
-
        if (param_no == 1) {
-               ret = fix_param(FPARAM_AVP, param);
-               if (ret <= 0) return ret;
-           if (fix_param(FPARAM_INT, param) != 0) return -1;
+               if (fixup_var_int_12(param, 1) != 0) return -1;
        } else if (param_no == 2) {
                return fixup_var_str_12(param, 2);
        }
@@ -523,6 +577,11 @@ static int fixup_t_reply(void** param, int param_no)
 static int fixup_on_failure(void** param, int param_no)
 {
        if (param_no==1){
+               if(strlen((char*)*param)<=1
+                               && (*(char*)(*param)==0 || *(char*)(*param)=='0')) {
+                       *param = (void*)0;
+                       return 0;
+               }
                return fixup_routes("t_on_failure", &failure_rt, param);
        }
        return 0;
@@ -533,6 +592,11 @@ static int fixup_on_failure(void** param, int param_no)
 static int fixup_on_reply(void** param, int param_no)
 {
        if (param_no==1){
+               if(strlen((char*)*param)<=1
+                               && (*(char*)(*param)==0 || *(char*)(*param)=='0')) {
+                       *param = (void*)0;
+                       return 0;
+               }
                return fixup_routes("t_on_reply", &onreply_rt, param);
        }
        return 0;
@@ -543,6 +607,11 @@ static int fixup_on_reply(void** param, int param_no)
 static int fixup_on_branch(void** param, int param_no)
 {
        if (param_no==1){
+               if(strlen((char*)*param)<=1
+                               && (*(char*)(*param)==0 || *(char*)(*param)=='0')) {
+                       *param = (void*)0;
+                       return 0;
+               }
                return fixup_routes("t_on_branch", &branch_rt, param);
        }
        return 0;
@@ -555,7 +624,7 @@ static int fixup_on_sl_reply(modparam_t type, void* val)
                return -1;
        }
 
-       if (fixup_routes("on_sl_reply", &onreply_rt, &val))
+       if (fixup_routes(0, &onreply_rt, &val))
                return -1;
 
        goto_on_sl_reply = (int)(long)val;
@@ -654,14 +723,11 @@ static int script_init( struct sip_msg *foo, unsigned int flags, void *bar)
        t_on_reply(0);
        t_on_branch(0);
        /* reset the kr status */
-       reset_kr(0);
+       reset_kr();
        /* set request mode so that multiple-mode actions know
         * how to behave */
        set_route_type(REQUEST_ROUTE);
-
-#ifdef POSTPONE_MSG_CLONING
        lumps_are_cloned = 0;
-#endif
        return 1;
 }
 
@@ -772,6 +838,15 @@ static int mod_init(void)
                LOG(L_ERR,"ERROR:tm:mod_init: failed to process AVP params\n");
                return -1;
        }
+#ifdef WITH_EVENT_LOCAL_REQUEST
+       goto_on_local_req=route_lookup(&event_rt, "tm:local-request");
+       if (goto_on_local_req>=0 && event_rt.rlist[goto_on_local_req]==0)
+               goto_on_local_req=-1; /* disable */
+       if (goto_on_local_req>=0)
+               set_child_rpc_sip_mode();
+#endif /* WITH_EVENT_LOCAL_REQUEST */
+       if (goto_on_sl_reply && onreply_rt.rlist[goto_on_sl_reply]==0)
+               WARN("empty/non existing on_sl_reply route\n");
        tm_init = 1;
        return 0;
 }
@@ -813,7 +888,7 @@ static int t_check_status(struct sip_msg* msg, char *p1, char *foo)
        str tmp;
        
        fp = (fparam_t*)p1;
-       
+       t = 0;
        /* first get the transaction */
        if (t_check(msg, 0 ) == -1) return -1;
        if ((t = get_t()) == 0) {
@@ -851,13 +926,14 @@ static int t_check_status(struct sip_msg* msg, char *p1, char *foo)
                break;
        }
        
-       switch(route_type) {
+       switch(get_route_type()) {
        case REQUEST_ROUTE:
                /* use the status of the last sent reply */
                status = int2str( t->uas.status, 0);
                break;
                
-       case ONREPLY_ROUTE:
+       case TM_ONREPLY_ROUTE:
+       case CORE_ONREPLY_ROUTE:
                /* use the status of the current reply */
                status = msg->first_line.u.reply.status.s;
                backup = status[msg->first_line.u.reply.status.len];
@@ -884,7 +960,8 @@ static int t_check_status(struct sip_msg* msg, char *p1, char *foo)
                break;
 
        default:
-               LOG(L_ERR,"ERROR:t_check_status: unsupported route type %d\n",route_type);
+               LOG(L_ERR,"ERROR:t_check_status: unsupported route type %d\n",
+                               get_route_type());
                goto error;
        }
 
@@ -898,11 +975,23 @@ static int t_check_status(struct sip_msg* msg, char *p1, char *foo)
                regfree(re);
                pkg_free(re);
        }
-
+       
+       if (unlikely(t && is_route_type(CORE_ONREPLY_ROUTE))){
+               /* t_check() above has the side effect of setting T and
+                  REFerencing T => we must unref and unset it.  */
+               UNREF( t );
+               set_t(T_UNDEFINED, T_BR_UNDEFINED);
+       }
        if (n!=0) return -1;
        return 1;
 
  error:
+       if (unlikely(t && is_route_type(CORE_ONREPLY_ROUTE))){
+               /* t_check() above has the side effect of setting T and
+                  REFerencing T => we must unref and unset it.  */
+               UNREF( t );
+               set_t(T_UNDEFINED, T_BR_UNDEFINED);
+       }
        if (s) pkg_free(s);
        if ((fp->type != FPARAM_REGEX) && re) {
                regfree(re);
@@ -950,7 +1039,12 @@ inline static int str2proto(char *s, int len) {
                return PROTO_TLS;       
        else if (len == 4 && !strncasecmp(s, "sctp", 4))
                return PROTO_SCTP;
-       else
+       else if (len == 2 && !strncasecmp(s, "ws", 2))
+               return PROTO_WS;
+       else if (len == 3 && !strncasecmp(s, "wss", 3)) {
+               LM_WARN("\"wss\" used somewhere...\n");
+               return PROTO_WS;
+       } else
                return PROTO_NONE;
 }
 
@@ -1119,6 +1213,7 @@ inline static int w_t_forward_nonack_to( struct sip_msg  *p_msg ,
        if (proxy) {
                r = _w_t_forward_nonack(p_msg, proxy, proxy->proto);            
                free_proxy(proxy);
+               pkg_free(proxy);
        }
        return r;
 }
@@ -1164,6 +1259,23 @@ inline static int w_t_reply(struct sip_msg* msg, char* p1, char* p2)
                ret = t_reply_unsafe(t, msg, code, r);
        } else if (is_route_type(REQUEST_ROUTE)) {
                ret = t_reply( t, msg, code, r);
+       } else if (is_route_type(ONREPLY_ROUTE)) {
+               if (likely(t->uas.request)){
+                       if (is_route_type(CORE_ONREPLY_ROUTE))
+                               ret=t_reply(t, t->uas.request, code, r);
+                       else
+                               ret=t_reply_unsafe(t, t->uas.request, code, r);
+               }else
+                       ret=-1;
+               /* t_check() above has the side effect of setting T and
+                  REFerencing T => we must unref and unset it.
+                  Note: this is needed only in the CORE_ONREPLY_ROUTE and not also in
+                  the TM_ONREPLY_ROUTE.
+                */
+               if (is_route_type(CORE_ONREPLY_ROUTE)) {
+                       UNREF( t );
+                       set_t(T_UNDEFINED, T_BR_UNDEFINED);
+               }
        } else {
                LOG(L_CRIT, "BUG: w_t_reply entered in unsupported mode\n");
                ret = -1;
@@ -1179,6 +1291,12 @@ inline static int w_t_release(struct sip_msg* msg, char* str, char* str2)
        struct cell *t;
        int ret;
        
+       if(get_route_type()!=REQUEST_ROUTE)
+       {
+               LM_INFO("invalid usage - not in request route\n");
+               return -1;
+       }
+
        if (t_check( msg  , 0  )==-1) return -1;
        t=get_t();
        if ( t && t!=T_UNDEFINED ) {
@@ -1248,6 +1366,7 @@ inline static int _w_t_relay_to(struct sip_msg  *p_msg ,
                                                                        struct proxy_l *proxy, int force_proto)
 {
        struct cell *t;
+       int res;
 
        if (is_route_type(FAILURE_ROUTE)) {
                t=get_t();
@@ -1255,10 +1374,13 @@ inline static int _w_t_relay_to(struct sip_msg  *p_msg ,
                        LOG(L_CRIT, "BUG: w_t_relay_to: undefined T\n");
                        return -1;
                }
-               if (t_forward_nonack(t, p_msg, proxy, force_proto)<=0 ) {
-                       LOG(L_ERR, "ERROR: w_t_relay_to: t_relay_to failed\n");
-                       /* let us save the error code, we might need it later
-                       when the failure_route has finished (Miklos) */
+               res = t_forward_nonack(t, p_msg, proxy, force_proto);
+               if (res <= 0) {
+                       if (res != E_CFG) {
+                           LOG(L_ERR, "ERROR: w_t_relay_to: t_relay_to failed\n");
+                           /* let us save the error code, we might need it later
+                              when the failure_route has finished (Miklos) */
+                       }
                        tm_error=ser_error;
                        return -1;
                }
@@ -1266,8 +1388,9 @@ inline static int _w_t_relay_to(struct sip_msg  *p_msg ,
        }
        if (is_route_type(REQUEST_ROUTE))
                return t_relay_to( p_msg, proxy, force_proto,
-                       0 /* no replication */ );
-       LOG(L_CRIT, "ERROR: w_t_relay_to: unsupported route type: %d\n", route_type);
+                                                       0 /* no replication */ );
+       LOG(L_CRIT, "ERROR: w_t_relay_to: unsupported route type: %d\n",
+                       get_route_type());
        return 0;
 }
 
@@ -1349,10 +1472,50 @@ inline static int w_t_relay_to_avp( struct sip_msg  *p_msg ,
        if (proxy) {
                r = _w_t_relay_to(p_msg, proxy, PROTO_NONE);
                free_proxy(proxy);
+               pkg_free(proxy);
+       }
+       return r;
+}
+
+int t_replicate_uri(struct sip_msg *msg, str *suri)
+{
+       struct proxy_l *proxy;
+       struct sip_uri turi;
+       int r = -1;
+
+       memset(&turi, 0, sizeof(struct sip_uri));
+       if(parse_uri(suri->s, suri->len, &turi)!=0)
+       {
+               LM_ERR("bad replicate SIP address!\n");
+               return -1;
        }
+
+       proxy=mk_proxy(&turi.host, turi.port_no, turi.proto);
+       if (proxy==0) {
+               LM_ERR("cannot create proxy from URI <%.*s>\n",
+                       suri->len, suri->s );
+               return -1;
+       }
+
+       r = t_replicate(msg, proxy, proxy->proto);
+       free_proxy(proxy);
+       pkg_free(proxy);
        return r;
 }
 
+inline static int w_t_replicate_uri(struct sip_msg  *msg ,
+                               char *uri,       /* sip uri as string or variable */
+                               char *_foo       /* nothing expected */ )
+{
+       str suri;
+
+       if(fixup_get_svalue(msg, (gparam_p)uri, &suri)!=0)
+       {
+               LM_ERR("invalid replicate uri parameter");
+               return -1;
+       }
+       return t_replicate_uri(msg, &suri);
+}
 
 inline static int w_t_replicate( struct sip_msg  *p_msg ,
        char *proxy, /* struct proxy_l *proxy expected */
@@ -1407,8 +1570,9 @@ inline static int w_t_replicate_to( struct sip_msg  *p_msg ,
        int r = -1;
        proxy = t_protoaddr2proxy(proto_par, addr_par);
        if (proxy) {
-               r = t_replicate(p_msg, proxy, proxy->proto);            
+               r = t_replicate(p_msg, proxy, proxy->proto);
                free_proxy(proxy);
+               pkg_free(proxy);
        }
        return r;
 }
@@ -1524,32 +1688,68 @@ int w_t_reset_max_lifetime(struct sip_msg* msg, char* foo, char* bar)
        return t_reset_max_lifetime();
 }
 
+
+
+/* helper macro, builds a function for setting a cell flag from the script.
+   e.g. T_SET_FLAG_GEN_FUNC(t_set_foo, T_FOO) =>
+   static int t_set_foo(struct sip_msg* msg, char*, char* )
+   that will expect fparam as first param and will set or reset T_FOO
+   in the current or next to be created transaction. */
+#define T_SET_FLAG_GEN_FUNC(fname, T_FLAG_NAME) \
+static int fname(struct sip_msg* msg, char* p1, char* p2) \
+{ \
+       int state; \
+       struct cell* t; \
+       unsigned int set_flags; \
+       unsigned int reset_flags; \
+        \
+       if (get_int_fparam(&state, msg, (fparam_t*)p1) < 0) return -1; \
+       t=get_t(); \
+       /* in REPLY_ROUTE and FAILURE_ROUTE T will be set to current transaction; \
+        * in REQUEST_ROUTE T will be set only if the transaction was already  \
+        * created; if not -> use the static variables */ \
+       if (!t || t==T_UNDEFINED ){ \
+               set_flags=get_msgid_val(user_cell_set_flags, msg->id, int); \
+               reset_flags=get_msgid_val(user_cell_reset_flags, msg->id, int); \
+               if (state){ \
+                       /* set */ \
+                       set_flags|= T_FLAG_NAME; \
+                       reset_flags&=~T_FLAG_NAME; \
+               }else{ \
+                       /* reset */ \
+                       set_flags&=~T_FLAG_NAME; \
+                       reset_flags|=T_FLAG_NAME; \
+               } \
+               set_msgid_val(user_cell_set_flags, msg->id, int, set_flags); \
+               set_msgid_val(user_cell_reset_flags, msg->id, int, reset_flags); \
+       }else{ \
+               if (state) \
+                       t->flags|=T_FLAG_NAME; \
+               else \
+                       t->flags&=~T_FLAG_NAME; \
+       } \
+       return 1; \
+}
+
+
+
 /* set automatically sending 100 replies on/off for the current or
  * next to be created transaction */
-static int t_set_auto_inv_100(struct sip_msg* msg, char* p1, char* p2)
-{
-       int state;
-       struct cell* t;
-       
-       if (get_int_fparam(&state, msg, (fparam_t*)p1) < 0) return -1;
-       t=get_t();
-       /* in REPLY_ROUTE and FAILURE_ROUTE T will be set to current transaction;
-        * in REQUEST_ROUTE T will be set only if the transaction was already
-        * created; if not -> use the static variables */
-       if (!t || t==T_UNDEFINED ){
-               if (state)
-                       set_msgid_val(user_auto_inv_100, msg->id, int, 1); /* set */
-               else
-                       set_msgid_val(user_auto_inv_100, msg->id, int, -1); /* reset */
-       }else{
-               if (state)
-                       t->flags|=T_AUTO_INV_100;
-               else
-                       t->flags&=~T_AUTO_INV_100;
-       }
-       return 1;
-}
+T_SET_FLAG_GEN_FUNC(t_set_auto_inv_100, T_AUTO_INV_100)
+
+
+/* set 6xx handling for the current or next to be created transaction */
+T_SET_FLAG_GEN_FUNC(t_set_disable_6xx, T_DISABLE_6xx)
+
 
+/* disable dns failover for the current transaction */
+T_SET_FLAG_GEN_FUNC(t_set_disable_failover, T_DISABLE_FAILOVER)
+
+
+#ifdef CANCEL_REASON_SUPPORT
+/* disable/enable e2e cancel reason copy for the current transaction */
+T_SET_FLAG_GEN_FUNC(t_set_no_e2e_cancel_reason, T_NO_E2E_CANCEL_REASON)
+#endif /* CANCEL_REASON_SUPPORT */
 
 
 /* script function, FAILURE_ROUTE only, returns true if the 
@@ -1572,7 +1772,7 @@ int t_branch_replied(struct sip_msg* msg, char* foo, char* bar)
 
 
 /* script function, returns: 1 if the transaction was canceled, -1 if not */
-int t_is_canceled(struct sip_msg* msg, char* foo, char* bar)
+int t_is_canceled(struct sip_msg* msg)
 {
        struct cell *t;
        int ret;
@@ -1590,6 +1790,11 @@ int t_is_canceled(struct sip_msg* msg, char* foo, char* bar)
        return ret;
 }
 
+static int w_t_is_canceled(struct sip_msg* msg, char* foo, char* bar)
+{
+       return t_is_canceled(msg);
+}
+
 /* 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)
 {
@@ -1685,42 +1890,39 @@ int t_grep_status(struct sip_msg* msg, char* status, char* bar)
  * that none of them is picked up again */
 static int w_t_drop_replies(struct sip_msg* msg, char* foo, char* bar)
 {
-       t_drop_replies();
+       if(foo==NULL)
+               t_drop_replies(1);
+       else if(*foo=='n')
+               t_drop_replies(0);
+       else if(*foo=='l')
+               t_drop_replies(2);
+       else
+               t_drop_replies(1);
        return 1;
 }
 
 /* save the message lumps after t_newtran() but before t_relay() */
 static int w_t_save_lumps(struct sip_msg* msg, char* foo, char* bar)
 {
-#ifdef POSTPONE_MSG_CLONING
        struct cell *t;
 
-       t=get_t();
-       if (!t || t==T_UNDEFINED) {
-               LOG(L_ERR, "ERROR: w_t_save_lumps: transaction has not been created yet\n");
-               return -1;
-       }
+       if (is_route_type(REQUEST_ROUTE)) {
+               t=get_t();
+               if (!t || t==T_UNDEFINED) {
+                       LOG(L_ERR, "ERROR: w_t_save_lumps: transaction has not been created yet\n");
+                       return -1;
+               }
 
-       if (save_msg_lumps(t->uas.request, msg)) {
-               LOG(L_ERR, "ERROR: w_t_save_lumps: "
-                       "failed to save the message lumps\n");
-               return -1;
-       }
+               if (save_msg_lumps(t->uas.request, msg)) {
+                       LOG(L_ERR, "ERROR: w_t_save_lumps: "
+                               "failed to save the message lumps\n");
+                       return -1;
+               }
+       } /* else nothing to do, the lumps have already been saved */
        return 1;
-#else
-       LOG(L_ERR, "ERROR: w_t_save_lumps: POSTPONE_MSG_CLONING is not defined,"
-                       " thus, the functionality is not supported\n");
-       return -1;
-#endif
 }
 
-static rpc_export_t tm_rpc[] = {
-       {"tm.cancel", rpc_cancel,   rpc_cancel_doc,   0},
-       {"tm.reply",  rpc_reply,    rpc_reply_doc,    0},
-       {"tm.stats",  tm_rpc_stats, tm_rpc_stats_doc, 0},
-       {"tm.hash_stats",  tm_rpc_hash_stats, tm_rpc_hash_stats_doc, 0},
-       {0, 0, 0, 0}
-};
+
 
 /* wrapper function needed after changes in w_t_reply */
 int w_t_reply_wrp(struct sip_msg *m, unsigned int code, char *txt)
@@ -1751,15 +1953,20 @@ int w_t_reply_wrp(struct sip_msg *m, unsigned int code, char *txt)
  *       reliable: if the ACK  is delayed the proxied transaction might
  *       be already deleted when it reaches the proxy (wait_timeout))
  */
-static int t_check_trans(struct sip_msg* msg, char* foo, char* bar)
+int t_check_trans(struct sip_msg* msg)
 {
        struct cell* t;
+       int branch;
+       int ret;
        
-       if (msg->first_line.type==SIP_REPLY)
-               return w_t_check(msg, 0 ,0);
-       else if (msg->REQ_METHOD==METHOD_CANCEL)
+       if (msg->first_line.type==SIP_REPLY) {
+               branch = 0;
+               ret = (t_check_msg( msg , &branch)==1) ? 1 : -1;
+               tm_ctx_set_branch_index(branch);
+               return ret;
+       } else if (msg->REQ_METHOD==METHOD_CANCEL) {
                return w_t_lookup_cancel(msg, 0, 0);
-       else{
+       } else {
                switch(t_check_msg(msg, 0)){
                        case -2: /* possible e2e ack */
                                return 1;
@@ -1789,3 +1996,218 @@ static int t_check_trans(struct sip_msg* msg, char* foo, char* bar)
        }
        return -1;
 }
+
+static int w_t_check_trans(struct sip_msg* msg, char* foo, char* bar)
+{
+       return t_check_trans(msg);
+}
+
+static int hexatoi(str *s, unsigned int* result)
+{
+       int i, xv, fact;
+
+       /* more than 32bit hexa? */
+       if (s->len>8)
+               return -1;
+
+       *result = 0;
+       fact = 1;
+
+       for(i=s->len-1; i>=0 ;i--)
+       {
+               xv = hex2int(s->s[i]);
+               if(xv<0)
+                       return -1;
+
+               *result += (xv * fact);
+               fact *= 16;
+       }
+       return 0;
+}
+
+static int fixup_t_relay_to(void** param, int param_no)
+{
+
+       int port;
+       int proto;
+       unsigned int flags;
+       struct proxy_l *proxy;
+       action_u_t *a;
+       str s;
+       str host;
+
+       s.s = (char*)*param;
+       s.len = strlen(s.s);
+       LM_DBG("fixing (%s, %d)\n", s.s, param_no);
+       if (param_no==1){
+               a = fixup_get_param(param, param_no, 2);
+               if(a==NULL)
+               {
+                       LM_CRIT("server error for parameter <%s>\n",s.s);
+                       return E_UNSPEC;
+               }
+               if(a->u.string!=NULL) {
+                       /* second parameter set, first should be proxy addr */
+                       if (parse_phostport(s.s, &host.s, &host.len, &port, &proto)!=0){
+                               LM_CRIT("invalid proxy addr parameter <%s>\n",s.s);
+                               return E_UNSPEC;
+                       }
+
+                       proxy = mk_proxy(&host, port, proto);
+                       if (proxy==0) {
+                               LM_ERR("failed to build proxy structure for <%.*s>\n", host.len, host.s );
+                               return E_UNSPEC;
+                       }
+                       *(param)=proxy;
+                       return 0;
+               } else {
+                       /* no second parameter, then is proxy addr or flags */
+                       flags = 0;
+                       if (s.len>2 && s.s[0]=='0' && s.s[1]=='x') {
+                               s.s += 2;
+                               s.len -= 2;
+                               if(hexatoi(&s, &flags)<0)
+                               {
+                                       LM_CRIT("invalid hexa flags <%s>\n", s.s);
+                                       return E_UNSPEC;
+                               }
+                               a->u.data = (void*)(unsigned long int)flags;
+                               *(param)= 0;
+                               return 0;
+                       } else {
+                               if(str2int(&s, &flags)==0)
+                               {
+                                       a->u.data = (void*)(unsigned long int)flags;
+                                       *(param)= 0;
+                                       return 0;
+                               } else {
+                                       /* try proxy */
+                                       if (parse_phostport(s.s, &host.s, &host.len, &port, &proto)!=0){
+                                               LM_CRIT("invalid proxy addr parameter <%s>\n",s.s);
+                                               return E_UNSPEC;
+                                       }
+
+                                       proxy = mk_proxy(&host, port, proto);
+                                       if (proxy==0) {
+                                               LM_ERR("failed to build proxy structure for <%.*s>\n", host.len, host.s );
+                                               return E_UNSPEC;
+                                       }
+                                       *(param)=proxy;
+                                       return 0;
+                               }
+                       }
+               }
+       } else if (param_no==2) {
+               /* flags */
+               flags = 0;
+               if (s.len>2 && s.s[0]=='0' && s.s[1]=='x') {
+                       s.s += 2;
+                       s.len -= 2;
+                       if(hexatoi(&s, &flags)<0)
+                       {
+                               LM_CRIT("invalid hexa flags <%s>\n", s.s);
+                               return E_UNSPEC;
+                       }
+                       *(param) = (void*)(unsigned long int)flags;
+                       return 0;
+               } else {
+                       if(str2int(&s, &flags)==0)
+                       {
+                               *(param) = (void*)(unsigned long int)flags;
+                               return 0;
+                       } else {
+                               LM_CRIT("invalid flags <%s>\n", s.s);
+                               return E_UNSPEC;
+                       }
+               }
+       } else {
+               LM_ERR("invalid parameter number %d\n", param_no);
+               return E_BUG;
+       }
+}
+
+
+inline static int w_t_relay_to(struct sip_msg *msg, char *proxy, char *flags)
+{
+       unsigned int fl;
+       struct proxy_l *px;
+       fparam_t param;
+
+       fl = (unsigned int)(long)(void*)flags;
+       px = (struct proxy_l*)proxy;
+
+       if(flags!=0)
+       {
+               memset(&param, 0, sizeof(fparam_t));
+               param.type = FPARAM_INT;
+               /* no auto 100 trying */
+               if(fl&1) {
+                       param.v.i = 0;
+                       t_set_auto_inv_100(msg, (char*)(&param), 0);
+               }
+               /* no auto negative reply - not implemented */
+               /*
+               if(fl&2) {
+                       param.v.i = 1;
+                       t_set_disable_internal_reply(msg, (char*)param, 0);
+               }
+               */
+               /* no dns failover */
+               if(fl&4) {
+                       param.v.i = 1;
+                       t_set_disable_failover(msg, (char*)(&param), 0);
+               }
+       }
+       return _w_t_relay_to(msg, px, PROTO_NONE);
+}
+
+
+
+/* rpc docs */
+
+static const char* rpc_cancel_doc[2] = {
+       "Cancel a pending transaction",
+       0
+};
+
+static const char* rpc_reply_doc[2] = {
+       "Reply transaction",
+       0
+};
+
+static const char* tm_rpc_stats_doc[2] = {
+       "Print transaction statistics.",
+       0
+};
+
+static const char* tm_rpc_hash_stats_doc[2] = {
+       "Prints hash table statistics (can be used only if tm is compiled"
+               " with -DTM_HASH_STATS).",
+       0
+};
+
+static const char* rpc_t_uac_start_doc[2] = {
+       "starts a tm uac using  a list of string parameters: method, ruri, dst_uri"
+               ", send_sock, headers (CRLF separated) and body (optional)",
+       0
+};
+
+static const char* rpc_t_uac_wait_doc[2] = {
+       "starts a tm uac and waits for the final reply, using a list of string "
+               "parameters: method, ruri, dst_uri send_sock, headers (CRLF separated)"
+               " and body (optional)",
+       0
+};
+
+
+/* rpc exports */
+static rpc_export_t tm_rpc[] = {
+       {"tm.cancel", rpc_cancel,   rpc_cancel_doc,   0},
+       {"tm.reply",  rpc_reply,    rpc_reply_doc,    0},
+       {"tm.stats",  tm_rpc_stats, tm_rpc_stats_doc, 0},
+       {"tm.hash_stats",  tm_rpc_hash_stats, tm_rpc_hash_stats_doc, 0},
+       {"tm.t_uac_start", rpc_t_uac_start, rpc_t_uac_start_doc, 0 },
+       {"tm.t_uac_wait",  rpc_t_uac_wait,  rpc_t_uac_wait_doc, RET_ARRAY},
+       {0, 0, 0, 0}
+};
+