Merge remote branch 'origin/daniel/xavp'
[sip-router] / modules / tm / t_reply.c
index 3bcf8c9..2ef60df 100644 (file)
@@ -89,7 +89,9 @@
  * 2008-03-12  use cancel_b_method on 6xx (andrei)
  * 2008-05-30  make sure the wait timer is started after we don't need t
  *             anymore to allow safe calls from fr_timer (andrei)
- * 2009-06-01  Pre- and post-script callbacks of branch route are executed (Miklos)
+ * 2009-06-01  Pre- and post-script callbacks of branch route are 
+ *             executed (Miklos)
+ * 2009-12-10  reply route is executed under lock to protect the avps (andrei)
  *
  */
 
 #include "t_msgbuilder.h"
 #include "t_lookup.h"
 #include "t_fwd.h"
-#include "fix_lumps.h"
+#include "../../fix_lumps.h"
+#include "../../sr_compat.h"
 #include "t_stats.h"
 #include "uac.h"
 
@@ -271,9 +274,9 @@ int unmatched_totag(struct cell *t, struct sip_msg *ack)
                if (i->tag.len==tag->len
                                && memcmp(i->tag.s, tag->s, tag->len)==0) {
                        DBG("DEBUG: totag for e2e ACK found: %d\n", i->acked);
-                       /* mark totag as acked and return 0 if this was the first ack
-                        * and 1 otherwise */
-                       return atomic_get_and_set_int(&i->acked, 1);
+                       /* mark totag as acked and return 1 if this was the first ack
+                        * and 0 otherwise */
+                       return (atomic_get_and_set_int(&i->acked, 1)==0);
                }
                i=i->next;
        }
@@ -417,6 +420,10 @@ static char *build_local_ack(struct sip_msg* rpl, struct cell *trans,
        }
 
        /* set the new buffer, but only if not already set (concurrent 2xx) */
+       /* a memory write barrier is needed to make sure the local_ack
+          content is fully written, before we try to add it to the transaction
+          -- andrei */
+       membar_write_atomic_op();
        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 */
@@ -525,9 +532,7 @@ static int _reply_light( struct cell *trans, char* buf, unsigned int len,
                DBG("DEBUG: _reply_light: response building failed\n");
                /* determine if there are some branches to be canceled */
                if ( is_invite(trans) ) {
-                       if (lock) LOCK_REPLIES( trans );
-                       which_cancel(trans, &cancel_bitmap );
-                       if (lock) UNLOCK_REPLIES( trans );
+                       prepare_to_cancel(trans, &cancel_bitmap, 0);
                }
                /* and clean-up, including cancellations, if needed */
                goto error;
@@ -535,7 +540,6 @@ static int _reply_light( struct cell *trans, char* buf, unsigned int len,
 
        cancel_bitmap=0;
        if (lock) LOCK_REPLIES( trans );
-       if ( is_invite(trans) ) which_cancel(trans, &cancel_bitmap );
        if (trans->uas.status>=200) {
                LOG( L_ERR, "ERROR: _reply_light: can't generate %d reply"
                        " when a final %d was sent out\n", code, trans->uas.status);
@@ -573,8 +577,10 @@ static int _reply_light( struct cell *trans, char* buf, unsigned int len,
                        run_trans_callbacks(TMCB_LOCAL_COMPLETED, trans,
                                                                        0, FAKED_REPLY, code);
                cleanup_uac_timers( trans );
-               if (is_invite(trans)) 
+               if (is_invite(trans)){
+                       prepare_to_cancel(trans, &cancel_bitmap, 0);
                        cancel_uacs( trans, cancel_bitmap, F_CANCEL_B_KILL );
+               }
                start_final_repl_retr(  trans );
        }
 
@@ -599,14 +605,15 @@ static int _reply_light( struct cell *trans, char* buf, unsigned int len,
                                                                        trans->uas.request, FAKED_REPLY, code);
 #ifdef TMCB_ONSEND
                        if (unlikely(has_tran_tmcbs(trans, TMCB_RESPONSE_SENT))){
-                               INIT_TMCB_ONSEND_PARAMS(onsend_params, 0, 0, rb, &rb->dst, 
+                               INIT_TMCB_ONSEND_PARAMS(onsend_params, trans->uas.request,
+                                                               FAKED_REPLY, rb, &rb->dst, 
                                                                buf, len, TMCB_LOCAL_F, rb->branch, code);
                                run_onsend_callbacks2(TMCB_RESPONSE_SENT, trans,
                                                                                &onsend_params);
                        }
 #endif /* TMCB_ONSEND */
                }
-               DBG("DEBUG: reply sent out. buf=%p: %.9s..., shmem=%p: %.9s\n",
+               DBG("DEBUG: reply sent out. buf=%p: %.20s..., shmem=%p: %.20s\n",
                        buf, buf, rb->buffer, rb->buffer );
        }
        if (code>=200) {
@@ -621,13 +628,14 @@ static int _reply_light( struct cell *trans, char* buf, unsigned int len,
        return 1;
 
 error3:
+       prepare_to_cancel(trans, &cancel_bitmap, 0);
 error2:
        if (lock) UNLOCK_REPLIES( trans );
        pkg_free ( buf );
 error:
        /* do UAC cleanup */
        cleanup_uac_timers( trans );
-       if ( is_invite(trans) )
+       if ( is_invite(trans) && cancel_bitmap )
                cancel_uacs( trans, cancel_bitmap, F_CANCEL_B_KILL);
        /* we did not succeed -- put the transaction on wait */
        put_on_wait(trans);
@@ -880,7 +888,7 @@ int run_failure_handlers(struct cell *t, struct sip_msg *rpl,
                t->on_negative=0;
                if (exec_pre_script_cb(&faked_req, FAILURE_CB_TYPE)>0) {
                        /* run a reply_route action if some was marked */
-                       if (run_top_route(failure_rt.rlist[on_failure], &faked_req)<0)
+                       if (run_top_route(failure_rt.rlist[on_failure], &faked_req, 0)<0)
                                LOG(L_ERR, "ERROR: run_failure_handlers: Error in run_top_route\n");
                        exec_post_script_cb(&faked_req, FAILURE_CB_TYPE);
                }
@@ -1034,6 +1042,7 @@ static enum rps t_should_relay_response( struct cell *Trans , int new_code,
        int inv_through;
        int extra_flags;
        int i;
+       int replies_dropped;
 
        /* note: this code never lets replies to CANCEL go through;
           we generate always a local 200 for CANCEL; 200s are
@@ -1095,9 +1104,10 @@ static enum rps t_should_relay_response( struct cell *Trans , int new_code,
                        *should_store=1;
                        *should_relay=-1;
                        if (new_code>=600 && new_code<=699){
-                               if (!(Trans->flags & T_6xx)){
-                                       /* cancel only the first time we get a 6xx */
-                                       which_cancel(Trans, cancel_bitmap);
+                               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);
                                        Trans->flags|=T_6xx;
                                }
                        }
@@ -1119,7 +1129,11 @@ static enum rps t_should_relay_response( struct cell *Trans , int new_code,
                Trans->flags&=~T_6xx; /* clear the 6xx flag , we want to 
                                                                 allow new branches from the failure route */
 
-               drop_replies = 0;
+               if(sr_cfg_compat==SR_COMPAT_KAMAILIO)
+                       drop_replies = 3;
+               else
+                       drop_replies = 0;
+               replies_dropped = 0;
                /* run ON_FAILURE handlers ( route and callbacks) */
                if (unlikely(has_tran_tmcbs( Trans, TMCB_ON_FAILURE_RO|TMCB_ON_FAILURE)
                                                || Trans->on_negative )) {
@@ -1130,9 +1144,19 @@ static enum rps t_should_relay_response( struct cell *Trans , int new_code,
                                                        FL_REPLIED:0);
                        run_failure_handlers( Trans, Trans->uac[picked_branch].reply,
                                                                        picked_code, extra_flags);
-                       if (unlikely(drop_replies)) {
+                       if (unlikely((drop_replies==3 && branch_cnt<Trans->nr_of_outgoings) ||
+                                                        (drop_replies!=0 && drop_replies!=3))
+                                       ) {
                                /* drop all the replies that we have already saved */
-                               for (i=0; i<branch_cnt; i++) {
+                               i = 0;
+                               if(drop_replies==2)
+                               {
+                                       for(i=branch_cnt-1; i>=0; i--)
+                                               if(Trans->uac[i].flags&TM_UAC_FLAG_FB)
+                                                       break;
+                                       if(i<0) i=0;
+                               }
+                               for (; i<branch_cnt; i++) {
                                        if (Trans->uac[i].reply &&
                                        (Trans->uac[i].reply != FAKED_REPLY) &&
                                        (Trans->uac[i].reply->msg_flags & FL_SHM_CLONE))
@@ -1144,6 +1168,7 @@ static enum rps t_should_relay_response( struct cell *Trans , int new_code,
                                /* make sure that the selected reply is not relayed even if
                                there is not any new branch added -- should not happen */
                                picked_branch = -1;
+                               replies_dropped = 1;
                        }
                }
 
@@ -1172,7 +1197,7 @@ static enum rps t_should_relay_response( struct cell *Trans , int new_code,
                if (branch_cnt<Trans->nr_of_outgoings){
                        /* the new branches might be already "finished" => we
                         * must use t_pick_branch again */
-                       new_branch=t_pick_branch((drop_replies==0)?
+                       new_branch=t_pick_branch((replies_dropped==0)?
                                                        branch :
                                                        -1, /* make sure we do not pick
                                                                the current branch */
@@ -1181,7 +1206,7 @@ static enum rps t_should_relay_response( struct cell *Trans , int new_code,
                                                &picked_code);
 
                        if (new_branch<0){
-                               if (likely(drop_replies==0)) {
+                               if (likely(replies_dropped==0)) {
                                        if (new_branch==-2) { /* branches open yet */
                                                *should_store=1;
                                                *should_relay=-1;
@@ -1207,7 +1232,7 @@ static enum rps t_should_relay_response( struct cell *Trans , int new_code,
                                /* found a new_branch */
                                picked_branch=new_branch;
                        }
-               } else if (unlikely(drop_replies)) {
+               } else if (unlikely(replies_dropped)) {
                        /* Either the script writer did not add new branches
                        after calling t_drop_replies(), or tm was unable
                        to add the new branches to the transaction. */
@@ -1217,9 +1242,9 @@ static enum rps t_should_relay_response( struct cell *Trans , int new_code,
                /* really no more pending branches -- return lowest code */
                *should_store=0;
                *should_relay=picked_branch;
-               /* we dont need 'which_cancel' here -- all branches
+               /* we dont need 'prepare_to_cancel' here -- all branches
                   known to have completed */
-               /* which_cancel( Trans, cancel_bitmap ); */
+               /* prepare_to_cancel( Trans, cancel_bitmap, 0 ); */
                return RPS_COMPLETED;
        }
 
@@ -1237,7 +1262,7 @@ 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 ) {
-                       which_cancel( Trans, cancel_bitmap );
+                       prepare_to_cancel( Trans, cancel_bitmap, 0);
                        return RPS_COMPLETED;
                } else return RPS_PROVISIONAL;
        }
@@ -1253,16 +1278,19 @@ discard:
 
 branches_failed:
        *should_store=0;
-       *should_relay=-1;
-       /* We have hopefully set tm_error in failure_route when
-       the branches failed. If not, reply with E_UNSPEC */
-       if ((kill_transaction_unsafe(Trans,
-                                   tm_error ? tm_error : E_UNSPEC)
-                                   ) <=0 ){
-               LOG(L_ERR, "ERROR: t_should_relay_response: "
-                       "reply generation failed\n");
+       if (is_local(Trans)){
+               /* for local transactions use the current reply */
+               *should_relay=branch;
+       }else{
+               *should_relay=-1;
+               /* We have hopefully set tm_error in failure_route when
+                       the branches failed. If not, reply with E_UNSPEC */
+               if ((kill_transaction_unsafe(Trans,
+                               tm_error ? tm_error : E_UNSPEC)) <=0 ){
+                       LOG(L_ERR, "ERROR: t_should_relay_response: "
+                                               "reply generation failed\n");
+               }
        }
-       
        return RPS_COMPLETED;
 }
 
@@ -1615,6 +1643,9 @@ enum rps relay_reply( struct cell *t, struct sip_msg *p_msg, int branch,
                                if (branch!=relay) {
                                        free_via_clen_lump(&relayed_msg->add_rm);
                                }
+                               /* update send_flags with possible additions from the
+                                  reply route */
+                               uas_rb->dst.send_flags|=relayed_msg->rpl_send_flags;
                        }
                }
                update_reply_stats( relayed_code );
@@ -1801,10 +1832,10 @@ enum rps local_reply( struct cell *t, struct sip_msg *p_msg, int branch,
        return reply_status;
 
 error:
-       which_cancel(t, cancel_bitmap);
+       prepare_to_cancel(t, cancel_bitmap, 0);
        UNLOCK_REPLIES(t);
        cleanup_uac_timers(t);
-       if ( get_cseq(p_msg)->method.len==INVITE_LEN
+       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 */
@@ -1841,6 +1872,7 @@ int reply_received( struct sip_msg  *p_msg )
 #ifdef WITH_XAVP
        sr_xavp_t **backup_xavps;
 #endif
+       int replies_locked;
 #ifdef USE_DNS_FAILOVER
        int branch_ret;
        int prev_branch;
@@ -1853,6 +1885,7 @@ int reply_received( struct sip_msg  *p_msg )
 #ifdef TMCB_ONSEND
        struct tmcb_params onsend_params;
 #endif
+       struct run_act_ctx ctx;
 
        /* make sure we know the associated transaction ... */
        if (t_check( p_msg  , &branch )==-1)
@@ -1867,6 +1900,7 @@ int reply_received( struct sip_msg  *p_msg )
        tm_ctx_set_branch_index(branch);
        cancel_bitmap=0;
        msg_status=p_msg->REPLY_STATUS;
+       replies_locked=0;
 
        uac=&t->uac[branch];
        DBG("DEBUG: reply_received: org. status uas=%d, "
@@ -1992,10 +2026,19 @@ int reply_received( struct sip_msg  *p_msg )
 #endif
                setbflagsval(0, uac->branch_flags);
                /* Pre- and post-script callbacks have already
-                * been execueted by the core. (Miklos)
+                * been executed by the core. (Miklos)
                 */
-               if (run_top_route(onreply_rt.rlist[t->on_reply], p_msg)<0)
-                       LOG(L_ERR, "ERROR: on_reply processing failed\n");
+               /* lock onreply_route, for safe avp usage */
+               LOCK_REPLIES( t );
+               replies_locked=1;
+               run_top_route(onreply_rt.rlist[t->on_reply], p_msg, &ctx);
+               if ((ctx.run_flags&DROP_R_F)  && (msg_status<200)) {
+                       if (unlikely(replies_locked)) {
+                               replies_locked = 0;
+                               UNLOCK_REPLIES( t );
+                       }
+                       goto done;
+               }
                /* transfer current message context back to t */
                if (t->uas.request) t->uas.request->flags=p_msg->flags;
                getbflagsval(0, &uac->branch_flags);
@@ -2048,15 +2091,22 @@ int reply_received( struct sip_msg  *p_msg )
                 *  reply lock is held (the lock won't be held while sending the
                 *   message)*/
                if (cfg_get(core, core_cfg, use_dns_failover) && (msg_status==503)) {
-                       branch_ret=add_uac_dns_fallback(t, t->uas.request, uac, 1);
+                       branch_ret=add_uac_dns_fallback(t, t->uas.request,
+                                                                                               uac, !replies_locked);
                        prev_branch=-1;
+                       /* unlock replies to avoid sending() while holding a lock */
+                       if (unlikely(replies_locked)) {
+                               UNLOCK_REPLIES( t );
+                               replies_locked = 0;
+                       }
                        while((branch_ret>=0) &&(branch_ret!=prev_branch)){
                                prev_branch=branch_ret;
                                branch_ret=t_send_branch(t, branch_ret, t->uas.request , 0, 1);
                        }
                }
 #endif
-       LOCK_REPLIES( t );
+       if (unlikely(!replies_locked))
+               LOCK_REPLIES( t );
        if ( is_local(t) ) {
                reply_status=local_reply( t, p_msg, branch, msg_status, &cancel_bitmap );
                if (reply_status == RPS_COMPLETED) {
@@ -2129,7 +2179,7 @@ trans_not_found:
                 * Pre- and post-script callbacks have already
                 * been execueted by the core. (Miklos)
                 */
-               return run_top_route(onreply_rt.rlist[goto_on_sl_reply], p_msg);
+               return run_top_route(onreply_rt.rlist[goto_on_sl_reply], p_msg, 0);
        } else {
                /* let the core forward the reply */
                return 1;
@@ -2226,14 +2276,14 @@ error:
 /* drops all the replies to make sure
  * that none of them is picked up again
  */
-void t_drop_replies(void)
+void t_drop_replies(int v)
 {
        /* It is too risky to free the replies that are in shm mem
        at the middle of failure_route block, because other functions might
        need them as well. And it can also happen that the current reply is not yet
        in shm mem, we are just going to clone it. So better to set a flag
        and check it after failure_route has ended. (Miklos) */
-       drop_replies = 1;
+       drop_replies = v;
 }
 
 #if 0