tm: Reason header generation for local CANCELs
authorAndrei Pelinescu-Onciul <andrei@iptel.org>
Mon, 1 Mar 2010 17:40:19 +0000 (18:40 +0100)
committerAndrei Pelinescu-Onciul <andrei@iptel.org>
Mon, 1 Mar 2010 17:40:19 +0000 (18:40 +0100)
When cancel-ing branches due to final reply, 2xx or 6xx, add a
Reason header (according to RFC3326).

E.g.: Reason: SIP;cause=200

Internal implementation notes: the branch_bm_t/cancel_bitmap
parameter of many of the reply and cancel functions was replaced
with a bigger structure that contains all the information
associated with the cancel: the cancel branch bitmap, the reason
and reason text (optional) or the  received cancel message (if
any).
Note that the current commit does not implement copying Reason
headers from a received CANCEL, it deals only with CANCEL
generated locally because of a final reply, 2xx or 6xx.

modules/tm/t_cancel.c
modules/tm/t_cancel.h
modules/tm/t_fwd.c
modules/tm/t_msgbuilder.c
modules/tm/t_msgbuilder.h
modules/tm/t_reply.c
modules/tm/t_reply.h
modules/tm/timer.c

index 52f086b..e1cf97e 100644 (file)
@@ -46,6 +46,7 @@
  *              reflecting its purpose
  *             prepare_to_cancel() takes now an additional skip_branches
  *              bitmap parameter (andrei)
+ * 2010-02-26  cancel reason (rfc3326) basic support (andrei)
  */
 
 #include <stdio.h> /* for FILE* in fifo_uac_cancel */
@@ -102,13 +103,14 @@ void prepare_to_cancel(struct cell *t, branch_bm_t *cancel_bm,
 
 /* cancel branches scheduled for deletion
  * params: t          - transaction
- *          cancel_bm - bitmap with the branches that are supposed to be 
- *                       canceled 
+ *          cancel_data - structure filled with the cancel bitmap (bitmap with
+ *                       the branches that are supposed to be canceled) and
+ *                       the cancel reason.
  *          flags     - how_to_cancel flags, see cancel_branch()
  * returns: bitmap with the still active branches (on fr timer)
- * WARNING: always fill cancel_bm using prepare_to_cancel(), supplying values
- *          in any other way is a bug*/
-int cancel_uacs( struct cell *t, branch_bm_t cancel_bm, int flags)
+ * WARNING: always fill cancel_data->cancel_bitmap using prepare_to_cancel(),
+ *          supplying values in any other way is a bug*/
+int cancel_uacs( struct cell *t, struct cancel_info* cancel_data, int flags)
 {
        int i;
        int ret;
@@ -117,10 +119,11 @@ int cancel_uacs( struct cell *t, branch_bm_t cancel_bm, int flags)
        ret=0;
        /* cancel pending client transactions, if any */
        for( i=0 ; i<t->nr_of_outgoings ; i++ ) 
-               if (cancel_bm & (1<<i)){
+               if (cancel_data->cancel_bitmap & (1<<i)){
                        r=cancel_branch(
                                t,
                                i,
+                               &cancel_data->reason,
                                flags | ((t->uac[i].request.buffer==NULL)?
                                        F_CANCEL_B_FAKE_REPLY:0) /* blind UAC? */
                        );
@@ -131,7 +134,7 @@ int cancel_uacs( struct cell *t, branch_bm_t cancel_bm, int flags)
 
 int cancel_all_uacs(struct cell *trans, int how)
 {
-       branch_bm_t cancel_bm;
+       struct cancel_info cancel_data;
        int i,j;
 
 #ifdef EXTRA_DEBUG
@@ -139,10 +142,10 @@ int cancel_all_uacs(struct cell *trans, int how)
 #endif
        DBG("Canceling T@%p [%u:%u]\n", trans, trans->hash_index, trans->label);
        
-       cancel_bm=0;
-       prepare_to_cancel(trans, &cancel_bm, 0);
+       init_cancel_info(&cancel_data);
+       prepare_to_cancel(trans, &cancel_data.cancel_bitmap, 0);
         /* tell tm to cancel the call */
-       i=cancel_uacs(trans, cancel_bm, how);
+       i=cancel_uacs(trans, &cancel_data, how);
        
        if (how & F_CANCEL_UNREF)
 #ifndef TM_DEL_UNREF
@@ -172,6 +175,7 @@ int cancel_all_uacs(struct cell *trans, int how)
  *
  * params:  t - transaction
  *          branch - branch number to be canceled
+ *          reason - cancel reason structure
  *          flags - howto cancel: 
  *                   F_CANCEL_B_KILL - will completely stop the 
  *                     branch (stops the timers), use with care
@@ -202,13 +206,14 @@ int cancel_all_uacs(struct cell *trans, int how)
  *          - checking for buffer==0 under REPLY_LOCK is no enough, an 
  *           atomic_cmpxhcg or atomic_get_and_set _must_ be used.
  */
-int cancel_branch( struct cell *t, int branch, int flags )
+int cancel_branch( struct cell *t, int branch, struct cancel_reason* reason,
+                                       int flags )
 {
        char *cancel;
        unsigned int len;
        struct retr_buf *crb, *irb;
        int ret;
-       branch_bm_t tmp_bm;
+       struct cancel_info tmp_cd;
        void* pcbuf;
 
        crb=&t->uac[branch].local_cancel;
@@ -236,7 +241,7 @@ int cancel_branch( struct cell *t, int branch, int flags )
                        atomic_set_long(pcbuf, 0);
                        if (flags & F_CANCEL_B_FAKE_REPLY){
                                LOCK_REPLIES(t);
-                               if (relay_reply(t, FAKED_REPLY, branch, 487, &tmp_bm, 1) == 
+                               if (relay_reply(t, FAKED_REPLY, branch, 487, &tmp_cd, 1) == 
                                                                                RPS_ERROR){
                                        return -1;
                                }
@@ -257,7 +262,7 @@ int cancel_branch( struct cell *t, int branch, int flags )
                                if (flags & F_CANCEL_B_FAKE_REPLY){
                                        stop_rb_timers( irb ); /* stop even the fr timer */
                                        LOCK_REPLIES(t);
-                                       if (relay_reply(t, FAKED_REPLY, branch, 487, &tmp_bm, 1)== 
+                                       if (relay_reply(t, FAKED_REPLY, branch, 487, &tmp_cd, 1)== 
                                                                                        RPS_ERROR){
                                                return -1;
                                        }
@@ -272,10 +277,12 @@ int cancel_branch( struct cell *t, int branch, int flags )
 
        if (cfg_get(tm, tm_cfg, reparse_invite)) {
                /* build the CANCEL from the INVITE which was sent out */
-               cancel = build_local_reparse(t, branch, &len, CANCEL, CANCEL_LEN, &t->to);
+               cancel = build_local_reparse(t, branch, &len, CANCEL, CANCEL_LEN,
+                                                                        &t->to, reason);
        } else {
                /* build the CANCEL from the reveived INVITE */
-               cancel = build_local(t, branch, &len, CANCEL, CANCEL_LEN, &t->to);
+               cancel = build_local(t, branch, &len, CANCEL, CANCEL_LEN, &t->to,
+                                                        reason);
        }
        if (!cancel) {
                LOG(L_ERR, "ERROR: attempt to build a CANCEL failed\n");
@@ -339,7 +346,7 @@ void rpc_cancel(rpc_t* rpc, void* c)
 {
        struct cell *trans;
        static char cseq[128], callid[128];
-       branch_bm_t cancel_bm;
+       struct cancel_info cancel_data;
        int i,j;
 
        str cseq_s;   /* cseq */
@@ -347,7 +354,7 @@ void rpc_cancel(rpc_t* rpc, void* c)
 
        cseq_s.s=cseq;
        callid_s.s=callid;
-       cancel_bm=0;
+       init_cancel_info(&cancel_data);
 
        if (rpc->scan(c, "SS", &callid_s, &cseq_s) < 2) {
                rpc->fault(c, 400, "Callid and CSeq expected as parameters");
@@ -360,10 +367,10 @@ void rpc_cancel(rpc_t* rpc, void* c)
                return;
        }
        /*  find the branches that need cancel-ing */
-       prepare_to_cancel(trans, &cancel_bm, 0);
+       prepare_to_cancel(trans, &cancel_data.cancel_bitmap, 0);
         /* tell tm to cancel the call */
        DBG("Now calling cancel_uacs\n");
-       i=cancel_uacs(trans, cancel_bm, 0); /* don't fake 487s, 
+       i=cancel_uacs(trans, &cancel_data, 0); /* don't fake 487s, 
                                                                                 just wait for timeout */
        
        /* t_lookup_callid REF`d the transaction for us, we must UNREF here! */
index 0ef3f37..ddb1e9e 100644 (file)
@@ -35,6 +35,7 @@
  *  2009-07-14  should_cancel_branch() renamed to prepare_cancel_branch() to
  *               better reflect its purpose
  *              which_cancel() renamed to prepare_to_cancel() (andrei)
+ * 2010-02-26  cancel reason (rfc3326) basic support (andrei)
  */
 
 
@@ -46,6 +47,7 @@
 #include "../../atomic_ops.h"
 #include "defs.h"
 #include "h_table.h"
+#include "t_reply.h"
 
 
 /* a buffer is empty but cannot be used by anyone else;
 
 
 void prepare_to_cancel(struct cell *t, branch_bm_t *cancel_bm, branch_bm_t s);
-int cancel_uacs( struct cell *t, branch_bm_t cancel_bm, int flags );
+int cancel_uacs( struct cell *t, struct cancel_info* cancel_data, int flags );
 int cancel_all_uacs(struct cell *trans, int how);
-int cancel_branch( struct cell *t, int branch, int flags );
+int cancel_branch( struct cell *t, int branch, struct cancel_reason* reason,
+                                       int flags );
 
 typedef int(*cancel_uacs_f)( struct cell *t, branch_bm_t cancel_bm,
                                                                int flags );
index c518592..4f7cb02 100644 (file)
@@ -918,7 +918,7 @@ int e2e_cancel_branch( struct sip_msg *cancel_msg, struct cell *t_cancel,
                        "thus lumps are not applied to the message!\n");
                }
                shbuf=build_local_reparse( t_invite, branch, &len, CANCEL,
-                                                                       CANCEL_LEN, &t_invite->to);
+                                                                       CANCEL_LEN, &t_invite->to, 0);
                if (unlikely(!shbuf)) {
                        LOG(L_ERR, "e2e_cancel_branch: printing e2e cancel failed\n");
                        ret=ser_error=E_OUT_OF_MEM;
@@ -1011,6 +1011,7 @@ void e2e_cancel( struct sip_msg *cancel_msg,
                        ret=cancel_branch(
                                t_invite,
                                i,
+                               0,
                                cfg_get(tm,tm_cfg, cancel_b_flags)
                                        | ((t_invite->uac[i].request.buffer==NULL)?
                                                F_CANCEL_B_FAKE_REPLY:0) /* blind UAC? */
index b76c659..bf49c64 100644 (file)
@@ -46,6 +46,7 @@
  *               resolving nexthop twice (andrei)
  * 2007-05-28: build_local_reparse() is introdued: it uses the outgoing
  *             INVITE as a source to construct a CANCEL or ACK (Miklos)
+ * 2010-02-26  cancel reason (rfc3326) basic support (andrei)
  */
 
 #include "defs.h"
 #include "../../cfg_core.h" /* cfg_get(core, core_cfg, use_dns_failover) */
 #endif
 
+
+/* reason building blocks (see rfc3326) */
+#define REASON_PREFIX "Reason: SIP;cause="
+#define REASON_PREFIX_LEN (sizeof(REASON_PREFIX)-1)
+#define REASON_TEXT ";text="
+#define REASON_TEXT_LEN (sizeof(REASON_TEXT)-1)
+
 /* convenience macros */
 #define memapp(_d,_s,_len) \
        do{\
@@ -84,7 +92,8 @@
    customers of this function are local ACK and local CANCEL
  */
 char *build_local(struct cell *Trans,unsigned int branch,
-       unsigned int *len, char *method, int method_len, str *to)
+       unsigned int *len, char *method, int method_len, str *to,
+       struct cancel_reason* reason)
 {
        char                *cancel_buf, *p, *via;
        unsigned int         via_len;
@@ -94,6 +103,7 @@ char *build_local(struct cell *Trans,unsigned int branch,
        str branch_str;
        str via_id;
        struct hostport hp;
+       int reason_len, code_len;
 
        /* init */
        via_id.s=0;
@@ -157,7 +167,24 @@ char *build_local(struct cell *Trans,unsigned int branch,
                *len += user_agent_hdr.len + CRLF_LEN;
        }
        /* Content Length, EoM */
-       *len+=CONTENT_LENGTH_LEN+1 + CRLF_LEN + CRLF_LEN;
+       *len+=CONTENT_LENGTH_LEN+1 + CRLF_LEN;
+       reason_len = 0;
+       /* compute reason size */
+       if (reason && reason->cause != CANCEL_REAS_UNKNOWN){
+               if (likely(reason->cause > 0)){
+                       /* Reason: SIP;cause=<reason->cause>[;text=<reason->u.text.s>] */
+                       reason_len = REASON_PREFIX_LEN + USHORT2SBUF_MAX_LEN +
+                               (reason->u.text.s?
+                                       REASON_TEXT_LEN + 1 + reason->u.text.len + 1 : 0) +
+                               CRLF_LEN;
+               } else if (reason->cause == CANCEL_REAS_RCVD_CANCEL &&
+                                       reason->u.e2e_cancel) {
+                       /* FIXME: TODO */
+               } else if (unlikely(reason->cause != -1))
+                       BUG("unhandled reason cause %d\n", reason->cause);
+       }
+       *len+= reason_len;
+       *len+= CRLF_LEN; /* end of msg. */
 
        cancel_buf=shm_malloc( *len+1 );
        if (!cancel_buf)
@@ -197,9 +224,29 @@ char *build_local(struct cell *Trans,unsigned int branch,
                append_str(p, user_agent_hdr.s, user_agent_hdr.len );
                append_str(p, CRLF, CRLF_LEN );
        }
-       /* Content Length, EoM */
-       append_str(p, CONTENT_LENGTH "0" CRLF CRLF ,
-               CONTENT_LENGTH_LEN+1 + CRLF_LEN + CRLF_LEN);
+       /* Content Length */
+       append_str(p, CONTENT_LENGTH "0" CRLF, CONTENT_LENGTH_LEN + 1 + CRLF_LEN);
+       /* add reason if needed */
+       if (reason_len) {
+               if (likely(reason->cause > 0)) {
+                       append_str(p, REASON_PREFIX, REASON_PREFIX_LEN);
+                       code_len=ushort2sbuf(reason->cause, p,
+                                                                       *len-(int)(p-cancel_buf));
+                       if (unlikely(code_len==0))
+                               BUG("not enough space to write reason code");
+                       p+=code_len;
+                       if (reason->u.text.s){
+                               append_str(p, REASON_TEXT, REASON_TEXT_LEN);
+                               *p='"'; p++;
+                               append_str(p, reason->u.text.s, reason->u.text.len);
+                               *p='"'; p++;
+                       }
+                       append_str(p, CRLF, CRLF_LEN);
+               } else if (reason->cause == CANCEL_REAS_RCVD_CANCEL) {
+                       /* FIXME: handle cancel */
+               }
+       }
+       append_str(p, CRLF, CRLF_LEN); /* msg. end */
        *p=0;
 
        pkg_free(via);
@@ -217,7 +264,8 @@ error:
  * Can not be used to build other type of requests!
  */
 char *build_local_reparse(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,
+       struct cancel_reason *reason)
 {
        char    *invite_buf, *invite_buf_end;
        char    *cancel_buf;
@@ -225,6 +273,7 @@ char *build_local_reparse(struct cell *Trans,unsigned int branch,
        short   invite_len;
        enum _hdr_types_t       hf_type;
        int     first_via, to_len;
+       int cancel_buf_len, reason_len, code_len;
 
        invite_buf = Trans->uac[branch].request.buffer;
        invite_len = Trans->uac[branch].request.buffer_len;
@@ -234,9 +283,27 @@ char *build_local_reparse(struct cell *Trans,unsigned int branch,
                goto error;
        }
        if ((*invite_buf != 'I') && (*invite_buf != 'i')) {
-               LOG(L_ERR, "ERROR: build_local_reparse: trying to call build_local_reparse() for a non-INVITE request?\n");
+               LOG(L_ERR, "ERROR: trying to call build_local_reparse()"
+                                       " for a non-INVITE request?\n");
                goto error;
        }
+       
+       reason_len = 0;
+       /* compute reason size */
+       if (reason && reason->cause != CANCEL_REAS_UNKNOWN){
+               if (likely(reason->cause > 0)){
+                       /* Reason: SIP;cause=<reason->cause>[;text=<reason->u.text.s>] */
+                       reason_len = REASON_PREFIX_LEN + USHORT2SBUF_MAX_LEN +
+                               (reason->u.text.s?
+                                       REASON_TEXT_LEN + 1 + reason->u.text.len + 1 : 0) +
+                               CRLF_LEN;
+               } else if (reason->cause == CANCEL_REAS_RCVD_CANCEL &&
+                                       reason->u.e2e_cancel) {
+                       /* FIXME: TODO */
+               } else if (unlikely(reason->cause != -1))
+                       BUG("unhandled reason cause %d\n", reason->cause);
+       }
+
        invite_buf_end = invite_buf + invite_len;
        s = invite_buf;
 
@@ -245,10 +312,11 @@ char *build_local_reparse(struct cell *Trans,unsigned int branch,
        I just extend it with the length of new To HF to be sure.
        Ugly, but we avoid lots of checks and memory allocations this way */
        to_len = to ? to->len : 0;
-       cancel_buf = shm_malloc(sizeof(char)*(invite_len + to_len));
+       cancel_buf_len = invite_len + to_len + reason_len;
+       cancel_buf = shm_malloc(sizeof(char)*cancel_buf_len);
        if (!cancel_buf)
        {
-               LOG(L_ERR, "ERROR: build_local_reparse: cannot allocate shared memory\n");
+               LOG(L_ERR, "ERROR: cannot allocate shared memory\n");
                goto error;
        }
        d = cancel_buf;
@@ -281,7 +349,8 @@ char *build_local_reparse(struct cell *Trans,unsigned int branch,
                        case HDR_CSEQ_T:
                                /* find the method name and replace it */
                                while ((s < invite_buf_end)
-                                       && ((*s == ':') || (*s == ' ') || (*s == '\t') || ((*s >= '0') && (*s <= '9')))
+                                       && ((*s == ':') || (*s == ' ') || (*s == '\t') ||
+                                               ((*s >= '0') && (*s <= '9')))
                                        ) s++;
                                append_str(d, s1, s - s1);
                                append_str(d, method, method_len);
@@ -300,7 +369,8 @@ char *build_local_reparse(struct cell *Trans,unsigned int branch,
 
                        case HDR_TO_T:
                                if (to_len == 0) {
-                                       /* there is no To tag required, just copy paste the header */
+                                       /* there is no To tag required, just copy paste
+                                          the header */
                                        s = lw_next_line(s, invite_buf_end);
                                        append_str(d, s1, s - s1);
                                } else {
@@ -336,6 +406,27 @@ char *build_local_reparse(struct cell *Trans,unsigned int branch,
 
                        case HDR_EOH_T:
                                /* end of SIP message found */
+                               /* add reason if needed */
+                               if (reason_len) {
+                                       if (likely(reason->cause > 0)) {
+                                               append_str(d, REASON_PREFIX, REASON_PREFIX_LEN);
+                                               code_len=ushort2sbuf(reason->cause, d,
+                                                                               cancel_buf_len-(int)(d-cancel_buf));
+                                               if (unlikely(code_len==0))
+                                                       BUG("not enough space to write reason code");
+                                               d+=code_len;
+                                               if (reason->u.text.s){
+                                                       append_str(d, REASON_TEXT, REASON_TEXT_LEN);
+                                                       *d='"'; d++;
+                                                       append_str(d, reason->u.text.s,
+                                                                                       reason->u.text.len);
+                                                       *d='"'; d++;
+                                               }
+                                               append_str(d, CRLF, CRLF_LEN);
+                                       } else if (reason->cause == CANCEL_REAS_RCVD_CANCEL) {
+                                               /* FIXME: handle cancel */
+                                       }
+                               }
                                append_str(d, CRLF, CRLF_LEN);
                                *len = d - cancel_buf;
                                /* LOG(L_DBG, "DBG: build_local: %.*s\n", *len, cancel_buf); */
index 13fb57a..95a6db1 100644 (file)
 
 
 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,
+       struct cancel_reason* reason);
 
 char *build_local_reparse(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,
+       struct cancel_reason* reason);
 
 char *build_uac_request(  str msg_type, str dst, str from,
        str fromtag, int cseq, str callid, str headers, 
index f28cf05..a42820e 100644 (file)
@@ -96,6 +96,7 @@
  *             (andrei)
  * 2010-02-26  added experimental support for final reply dropping, not
  *             enabled by default (performance hit) (andrei)
+ * 2010-02-26  cancel reason (rfc3326) basic support (andrei)
  *
  */
 
@@ -391,11 +392,11 @@ static char *build_ack(struct sip_msg* rpl,struct cell *trans,int branch,
        if (cfg_get(tm, tm_cfg, reparse_invite)) {
                /* build the ACK from the INVITE which was sent out */
                return build_local_reparse( trans, branch, ret_len,
-                                       ACK, ACK_LEN, &to );
+                                       ACK, ACK_LEN, &to, 0 );
        } else {
                /* build the ACK from the reveived INVITE */
                return build_local( trans, branch, ret_len,
-                                       ACK, ACK_LEN, &to );
+                                       ACK, ACK_LEN, &to, 0 );
        }
 }
 
@@ -536,23 +537,23 @@ static int _reply_light( struct cell *trans, char* buf, unsigned int len,
 {
        struct retr_buf *rb;
        unsigned int buf_len;
-       branch_bm_t cancel_bitmap;
+       struct cancel_info cancel_data;
 #ifdef TMCB_ONSEND
        struct tmcb_params onsend_params;
 #endif
 
+       init_cancel_info(&cancel_data);
        if (!buf)
        {
                DBG("DEBUG: _reply_light: response building failed\n");
                /* determine if there are some branches to be canceled */
                if ( is_invite(trans) ) {
-                       prepare_to_cancel(trans, &cancel_bitmap, 0);
+                       prepare_to_cancel(trans, &cancel_data.cancel_bitmap, 0);
                }
                /* and clean-up, including cancellations, if needed */
                goto error;
        }
 
-       cancel_bitmap=0;
        if (lock) LOCK_REPLIES( trans );
        if (trans->uas.status>=200) {
                LOG( L_ERR, "ERROR: _reply_light: can't generate %d reply"
@@ -592,8 +593,9 @@ static int _reply_light( struct cell *trans, char* buf, unsigned int len,
                                                                        0, FAKED_REPLY, code);
                cleanup_uac_timers( trans );
                if (is_invite(trans)){
-                       prepare_to_cancel(trans, &cancel_bitmap, 0);
-                       cancel_uacs( trans, cancel_bitmap, F_CANCEL_B_KILL );
+                       prepare_to_cancel(trans, &cancel_data.cancel_bitmap, 0);
+                       cancel_data.reason.cause=code;
+                       cancel_uacs( trans, &cancel_data, F_CANCEL_B_KILL );
                }
                start_final_repl_retr(  trans );
        }
@@ -642,15 +644,15 @@ static int _reply_light( struct cell *trans, char* buf, unsigned int len,
        return 1;
 
 error3:
-       prepare_to_cancel(trans, &cancel_bitmap, 0);
+       prepare_to_cancel(trans, &cancel_data.cancel_bitmap, 0);
 error2:
        if (lock) UNLOCK_REPLIES( trans );
        pkg_free ( buf );
 error:
        /* do UAC cleanup */
        cleanup_uac_timers( trans );
-       if ( is_invite(trans) && cancel_bitmap )
-               cancel_uacs( trans, cancel_bitmap, F_CANCEL_B_KILL);
+       if ( is_invite(trans) && cancel_data.cancel_bitmap )
+               cancel_uacs( trans, &cancel_data, F_CANCEL_B_KILL);
        /* we did not succeed -- put the transaction on wait */
        put_on_wait(trans);
        return -1;
@@ -1063,7 +1065,7 @@ static unsigned char drop_replies;
  */
 static enum rps t_should_relay_response( struct cell *Trans , int new_code,
        int branch , int *should_store, int *should_relay,
-       branch_bm_t *cancel_bitmap, struct sip_msg *reply )
+       struct cancel_info *cancel_data, struct sip_msg *reply )
 {
        int branch_cnt;
        int picked_code;
@@ -1136,8 +1138,9 @@ static enum rps t_should_relay_response( struct cell *Trans , int new_code,
                                if (!(Trans->flags & (T_6xx | T_DISABLE_6xx))){
                                        /* cancel only the first time we get a 6xx and only
                                          if the 6xx handling is not disabled */
-                                       prepare_to_cancel(Trans, cancel_bitmap, 0);
+                                       prepare_to_cancel(Trans, &cancel_data->cancel_bitmap, 0);
                                        Trans->flags|=T_6xx;
+                                       cancel_data->reason.cause=new_code;
                                }
                        }
                        return RPS_STORE;
@@ -1291,7 +1294,8 @@ static enum rps t_should_relay_response( struct cell *Trans , int new_code,
                Trans->uac[branch].last_received=new_code;
                *should_relay= new_code==100? -1 : branch;
                if (new_code>=200 ) {
-                       prepare_to_cancel( Trans, cancel_bitmap, 0);
+                       prepare_to_cancel( Trans, &cancel_data->cancel_bitmap, 0);
+                       cancel_data->reason.cause=new_code;
                        return RPS_COMPLETED;
                } else return RPS_PROVISIONAL;
        }
@@ -1534,7 +1538,8 @@ skip:
    wait timer will be started (put_on_wait(t)).
 */
 enum rps relay_reply( struct cell *t, struct sip_msg *p_msg, int branch,
-       unsigned int msg_status, branch_bm_t *cancel_bitmap, int do_put_on_wait )
+       unsigned int msg_status, struct cancel_info *cancel_data,
+       int do_put_on_wait )
 {
        int relay;
        int save_clone;
@@ -1567,7 +1572,7 @@ enum rps relay_reply( struct cell *t, struct sip_msg *p_msg, int branch,
 
        /* *** store and relay message as needed *** */
        reply_status = t_should_relay_response(t, msg_status, branch,
-               &save_clone, &relay, cancel_bitmap, p_msg );
+               &save_clone, &relay, cancel_data, p_msg );
        DBG("DEBUG: relay_reply: branch=%d, save=%d, relay=%d\n",
                branch, save_clone, relay );
 
@@ -1778,7 +1783,8 @@ error02:
        }
 error01:
        t_reply_unsafe( t, t->uas.request, 500, "Reply processing error" );
-       *cancel_bitmap=0; /* t_reply_unsafe already canceled everything needed */
+       cancel_data->cancel_bitmap=0; /* t_reply_unsafe already canceled
+                                                                        everything needed */
        UNLOCK_REPLIES(t);
        /* if (is_invite(t)) cancel_uacs( t, *cancel_bitmap, 0); 
         *  -- not needed, t_reply_unsafe took care of this */
@@ -1795,7 +1801,7 @@ error01:
    it is entered locked with REPLY_LOCK and it returns unlocked!
 */
 enum rps local_reply( struct cell *t, struct sip_msg *p_msg, int branch,
-       unsigned int msg_status, branch_bm_t *cancel_bitmap)
+       unsigned int msg_status, struct cancel_info *cancel_data)
 {
        /* how to deal with replies for local transaction */
        int local_store, local_winner;
@@ -1803,17 +1809,16 @@ enum rps local_reply( struct cell *t, struct sip_msg *p_msg, int branch,
        struct sip_msg *winning_msg;
        int winning_code;
        int totag_retr;
-       /* branch_bm_t cancel_bitmap; */
 
        /* keep warning 'var might be used un-inited' silent */
        winning_msg=0;
        winning_code=0;
        totag_retr=0;
 
-       *cancel_bitmap=0;
+       cancel_data->cancel_bitmap=0;
 
        reply_status=t_should_relay_response( t, msg_status, branch,
-               &local_store, &local_winner, cancel_bitmap, p_msg );
+               &local_store, &local_winner, cancel_data, p_msg );
        DBG("DEBUG: local_reply: branch=%d, save=%d, winner=%d\n",
                branch, local_store, local_winner );
        if (local_store) {
@@ -1862,13 +1867,14 @@ enum rps local_reply( struct cell *t, struct sip_msg *p_msg, int branch,
        return reply_status;
 
 error:
-       prepare_to_cancel(t, cancel_bitmap, 0);
+       prepare_to_cancel(t, &cancel_data->cancel_bitmap, 0);
        UNLOCK_REPLIES(t);
        cleanup_uac_timers(t);
        if (p_msg && p_msg!=FAKED_REPLY && get_cseq(p_msg)->method.len==INVITE_LEN
-               && memcmp( get_cseq(p_msg)->method.s, INVITE, INVITE_LEN)==0)
-               cancel_uacs( t, *cancel_bitmap, F_CANCEL_B_KILL);
-       *cancel_bitmap=0; /* we've already took care of everything */
+               && memcmp( get_cseq(p_msg)->method.s, INVITE, INVITE_LEN)==0){
+               cancel_uacs( t, cancel_data, F_CANCEL_B_KILL);
+       }
+       cancel_data->cancel_bitmap=0; /* we've already took care of everything */
        put_on_wait(t);
        return RPS_ERROR;
 }
@@ -1893,7 +1899,7 @@ int reply_received( struct sip_msg  *p_msg )
        /* has the transaction completed now and we need to clean-up? */
        int reply_status;
        int onreply_route;
-       branch_bm_t cancel_bitmap;
+       struct cancel_info cancel_data;
        struct ua_client *uac;
        struct cell *t;
        struct dest_info  lack_dst;
@@ -1928,7 +1934,7 @@ int reply_received( struct sip_msg  *p_msg )
        if (unlikely(branch==T_BR_UNDEFINED))
                BUG("invalid branch, please report to sr-dev@sip-router.org\n");
        tm_ctx_set_branch_index(branch);
-       cancel_bitmap=0;
+       init_cancel_info(&cancel_data);
        msg_status=p_msg->REPLY_STATUS;
        replies_locked=0;
 
@@ -2035,7 +2041,7 @@ int reply_received( struct sip_msg  *p_msg )
                                 * if BUSY or set just exit, a cancel will be (or was) sent 
                                 * shortly on this branch */
                                DBG("tm: reply_received: branch CANCEL created\n");
-                               cancel_branch(t, branch, F_CANCEL_B_FORCE_C);
+                               cancel_branch(t, branch, 0, F_CANCEL_B_FORCE_C);
                        }
                        goto done; /* nothing to do */
                }
@@ -2179,24 +2185,24 @@ int reply_received( struct sip_msg  *p_msg )
                replies_locked=1;
        }
        if ( is_local(t) ) {
-               reply_status=local_reply( t, p_msg, branch, msg_status, &cancel_bitmap );
+               reply_status=local_reply( t, p_msg, branch, msg_status, &cancel_data );
                if (reply_status == RPS_COMPLETED) {
                             /* no more UAC FR/RETR (if I received a 2xx, there may
                              * be still pending branches ...
                              */
                        cleanup_uac_timers( t );
-                       if (is_invite(t)) cancel_uacs(t, cancel_bitmap, F_CANCEL_B_KILL);
+                       if (is_invite(t)) cancel_uacs(t, &cancel_data, F_CANCEL_B_KILL);
                        /* There is no need to call set_final_timer because we know
                         * that the transaction is local */
                        put_on_wait(t);
-               }else if (cancel_bitmap){
+               }else if (unlikely(cancel_data.cancel_bitmap)){
                        /* cancel everything, even non-INVITEs (e.g in case of 6xx), use
                         * cancel_b_method for canceling unreplied branches */
-                       cancel_uacs(t, cancel_bitmap, cfg_get(tm,tm_cfg, cancel_b_flags));
+                       cancel_uacs(t, &cancel_data, cfg_get(tm,tm_cfg, cancel_b_flags));
                }
        } else {
                reply_status=relay_reply( t, p_msg, branch, msg_status,
-                                                                       &cancel_bitmap, 1 );
+                                                                       &cancel_data, 1 );
                if (reply_status == RPS_COMPLETED) {
                             /* no more UAC FR/RETR (if I received a 2xx, there may
                                be still pending branches ...
@@ -2204,16 +2210,16 @@ int reply_received( struct sip_msg  *p_msg )
                        cleanup_uac_timers( t );
                        /* 2xx is a special case: we can have a COMPLETED request
                         * with branches still open => we have to cancel them */
-                       if (is_invite(t) && cancel_bitmap) 
-                               cancel_uacs( t, cancel_bitmap,  F_CANCEL_B_KILL);
+                       if (is_invite(t) && cancel_data.cancel_bitmap) 
+                               cancel_uacs( t, &cancel_data,  F_CANCEL_B_KILL);
                        /* FR for negative INVITES, WAIT anything else */
                        /* Call to set_final_timer is embedded in relay_reply to avoid
                         * race conditions when reply is sent out and an ACK to stop
                         * retransmissions comes before retransmission timer is set.*/
-               }else if (cancel_bitmap){
+               }else if (unlikely(cancel_data.cancel_bitmap)){
                        /* cancel everything, even non-INVITEs (e.g in case of 6xx), use
                         * cancel_b_method for canceling unreplied branches */
-                       cancel_uacs(t, cancel_bitmap, cfg_get(tm,tm_cfg, cancel_b_flags));
+                       cancel_uacs(t, &cancel_data, cfg_get(tm,tm_cfg, cancel_b_flags));
                }
        }
        uac->request.flags|=F_RB_REPLIED;
index 0a97e6a..f960414 100644 (file)
@@ -64,6 +64,38 @@ int unmatched_totag(struct cell *t, struct sip_msg *ack);
 /* branch bitmap type */
 typedef unsigned int branch_bm_t;
 
+
+#define CANCEL_REAS_UNKNOWN 0
+#define CANCEL_REAS_RCVD_CANCEL -1
+#define CANCEL_REAS_FINAL_REPLY(x) (x)
+
+/** cancel reason structure.*/
+struct cancel_reason {
+       short cause; /**< 0 = unknown, -1 =  cancel, > 0 final reply code. */
+       union{
+               str text; /**< reason text if reason is final reply .*/
+               struct sip_msg* e2e_cancel; /**< cancel msg if reason is cancel. */
+       }u;
+};
+
+struct cancel_info {
+       branch_bm_t cancel_bitmap; /**< cancel branch bitmap */
+       struct cancel_reason reason;
+};
+
+
+#define init_cancel_reason(cr) \
+       do {\
+               (cr)->cause=0; \
+               (cr)->u.e2e_cancel=0; \
+       } while(0)
+
+#define init_cancel_info(ci) \
+       do {\
+               (ci)->cancel_bitmap=0; \
+               init_cancel_reason(&(ci)->reason); \
+       }while (0);
+
 /* reply export types */
 typedef int (*treply_f)(struct sip_msg * , unsigned int , char * );
 typedef int (*treply_wb_f)( struct cell* trans,
@@ -121,10 +153,11 @@ int t_reply_unsafe( struct cell *t, struct sip_msg * , unsigned int , char * );
 
 
 enum rps relay_reply( struct cell *t, struct sip_msg *p_msg, int branch, 
-       unsigned int msg_status, branch_bm_t *cancel_bitmap, int do_put_on_wait );
+       unsigned int msg_status, struct cancel_info *cancel_data,
+       int do_put_on_wait );
 
 enum rps local_reply( struct cell *t, struct sip_msg *p_msg, int branch,
-    unsigned int msg_status, branch_bm_t *cancel_bitmap );
+    unsigned int msg_status, struct cancel_info *cancel_data );
 
 void set_final_timer( /* struct s_table *h_table,*/ struct cell *t );
 
index 534fdd3..3d4eb2f 100644 (file)
@@ -322,7 +322,7 @@ inline static ticks_t  delete_cell( struct cell *p_cell, int unlock )
  * it assumes the REPLY_LOCK is already held and returns unlocked */
 static void fake_reply(struct cell *t, int branch, int code )
 {
-       branch_bm_t cancel_bitmap;
+       struct cancel_info cancel_data;
        short do_cancel_branch;
        enum rps reply_status;
 
@@ -331,15 +331,15 @@ static void fake_reply(struct cell *t, int branch, int code )
        t->uac[branch].request.flags|=F_RB_CANCELED;
        if ( is_local(t) ) {
                reply_status=local_reply( t, FAKED_REPLY, branch, 
-                                         code, &cancel_bitmap );
+                                         code, &cancel_data );
        } else {
                /* rely reply, but don't put on wait, we still need t
                 * to send the cancels */
                reply_status=relay_reply( t, FAKED_REPLY, branch, code,
-                                         &cancel_bitmap, 0 );
+                                         &cancel_data, 0 );
        }
        /* now when out-of-lock do the cancel I/O */
-       if (do_cancel_branch) cancel_branch(t, branch, 0);
+       if (do_cancel_branch) cancel_branch(t, branch, &cancel_data.reason, 0);
        /* it's cleaned up on error; if no error occurred and transaction
           completed regularly, I have to clean-up myself
        */