inline static int w_t_cancel_branch(struct sip_msg *msg, char *sflags) { branch_bm_t cancel_bitmap = 0; struct cell *t; unsigned int flags = (unsigned long)sflags; t=get_t(); if (t==NULL || t==T_UNDEFINED) { /* no transaction */ LM_ERR("cannot cancel a reply with no transaction"); return -1; } if (!is_invite(t)) return -1; if (flags&TM_CANCEL_BRANCH_ALL) { /* lock and get the branches to cancel */ if (!onreply_avp_mode) { LOCK_REPLIES(t); which_cancel( t, &cancel_bitmap ); UNLOCK_REPLIES(t); } else { which_cancel( t, &cancel_bitmap ); } if (msg->first_line.u.reply.statuscode>=200) /* do not cancel the current branch as we got * a final response here */ cancel_bitmap &= ~(1<<_tm_branch_index); } else if (flags&TM_CANCEL_BRANCH_OTHERS) { /* lock and get the branches to cancel */ if (!onreply_avp_mode) { LOCK_REPLIES(t); which_cancel( t, &cancel_bitmap ); UNLOCK_REPLIES(t); } else { which_cancel( t, &cancel_bitmap ); } /* ignore current branch */ cancel_bitmap &= ~(1<<_tm_branch_index); } else { /* cancel only local branch (only if still ongoing) */ if (msg->first_line.u.reply.statuscode<200) cancel_bitmap = 1<<_tm_branch_index; } /* send cancels out */ cancel_uacs(t, cancel_bitmap); return 1; }
void cancel_invite(struct sip_msg *cancel_msg, struct cell *t_cancel, struct cell *t_invite ) { #define CANCEL_REASON_SIP_487 \ "Reason: SIP;cause=487;text=\"ORIGINATOR_CANCEL\"" CRLF branch_bm_t cancel_bitmap; branch_bm_t dummy_bm; str reason; unsigned int i; struct hdr_field *hdr; cancel_bitmap=0; /* send back 200 OK as per RFC3261 */ reason.s = CANCELING; reason.len = sizeof(CANCELING)-1; t_reply( t_cancel, cancel_msg, 200, &reason ); reason.s = NULL; reason.len = 0; /* propagate the REASON flag ? */ if ( t_cancel->flags&T_CANCEL_REASON_FLAG ) { /* look for the Reason header */ if (parse_headers(cancel_msg, HDR_EOH_F, 0)<0) { LM_ERR("failed to parse all hdrs - ignoring Reason hdr\n"); } else { hdr = get_header_by_static_name(cancel_msg, "Reason"); if (hdr!=NULL) { reason.s = hdr->name.s; reason.len = hdr->len; } } } /* if no reason, use NORMAL CLEARING */ if (reason.s == NULL) { reason.s = CANCEL_REASON_SIP_487; reason.len = sizeof(CANCEL_REASON_SIP_487) - 1; } /* generate local cancels for all branches */ which_cancel(t_invite, &cancel_bitmap ); set_cancel_extra_hdrs( reason.s, reason.len); cancel_uacs(t_invite, cancel_bitmap ); set_cancel_extra_hdrs( NULL, 0); /* internally cancel branches with no received reply */ for (i=t_invite->first_branch; i<t_invite->nr_of_outgoings; i++) { if (t_invite->uac[i].last_received==0){ /* reset the "request" timers */ reset_timer(&t_invite->uac[i].request.retr_timer); reset_timer(&t_invite->uac[i].request.fr_timer); LOCK_REPLIES( t_invite ); relay_reply(t_invite,FAKED_REPLY,i,487,&dummy_bm); } } }
void cancel_invite(struct sip_msg *cancel_msg, struct cell *t_cancel, struct cell *t_invite, int locked) { branch_bm_t cancel_bitmap; str reason; cancel_bitmap=0; /* send back 200 OK as per RFC3261 */ reason.s = CANCELING; reason.len = sizeof(CANCELING)-1; if (locked) t_reply_unsafe( t_cancel, cancel_msg, 200, &reason ); else t_reply( t_cancel, cancel_msg, 200, &reason ); get_cancel_reason(cancel_msg, t_cancel->flags, &reason); /* generate local cancels for all branches */ which_cancel(t_invite, &cancel_bitmap ); set_cancel_extra_hdrs( reason.s, reason.len); cancel_uacs(t_invite, cancel_bitmap ); set_cancel_extra_hdrs( NULL, 0); /* Do not do anything about branches with no received reply; * continue the retransmission hoping to get something back; * if still not, we will generate the 408 Timeout based on FR * timer; this helps with better coping with missed/lated provisional * replies in the context of cancelling the transaction */ #if 0 /* internally cancel branches with no received reply */ for (i=t_invite->first_branch; i<t_invite->nr_of_outgoings; i++) { if (t_invite->uac[i].last_received==0){ /* reset the "request" timers */ reset_timer(&t_invite->uac[i].request.retr_timer); reset_timer(&t_invite->uac[i].request.fr_timer); LOCK_REPLIES( t_invite ); relay_reply(t_invite,FAKED_REPLY,i,487,&dummy_bm); } } #endif }
int t_cancel_trans(struct cell *t, str *extra_hdrs) { branch_bm_t cancel_bitmap = 0; if (t==NULL || t==T_UNDEFINED) { /* no transaction */ LM_ERR("cannot cancel with no transaction"); return -1; } LOCK_REPLIES(t); which_cancel( t, &cancel_bitmap ); UNLOCK_REPLIES(t); /* send cancels out */ if (extra_hdrs) set_cancel_extra_hdrs( extra_hdrs->s, extra_hdrs->len); cancel_uacs(t, cancel_bitmap); set_cancel_extra_hdrs( NULL, 0); return 0; }
/* This is the neurological point of reply processing -- called * from within a REPLY_LOCK, t_should_relay_response decides * how a reply shall be processed and how transaction state is * affected. * * Checks if the new reply (with new_code status) should be sent or not * based on the current * transaction status. * Returns - branch number (0,1,...) which should be relayed * -1 if nothing to be relayed */ 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 ) { int branch_cnt; int picked_code; int inv_through; int do_cancel; /* note: this code never lets replies to CANCEL go through; we generate always a local 200 for CANCEL; 200s are not relayed because it's not an INVITE transaction; >= 300 are not relayed because 200 was already sent out */ LM_DBG("T_code=%d, new_code=%d\n", Trans->uas.status,new_code); inv_through=new_code>=200 && new_code<300 && is_invite(Trans); /* if final response sent out, allow only INVITE 2xx */ if ( Trans->uas.status >= 200 ) { if (inv_through) { LM_DBG("200 OK for INVITE after final sent\n"); *should_store=0; Trans->uac[branch].last_received=new_code; *should_relay=branch; return RPS_PUSHED_AFTER_COMPLETION; } if ( is_hopbyhop_cancel(Trans) && new_code>=200) { *should_store=0; *should_relay=-1; picked_branch=-1; return RPS_COMPLETED; } /* except the exception above, too late messages will be discarded */ goto discard; } /* if final response received at this branch, allow only INVITE 2xx */ if (Trans->uac[branch].last_received>=200 && !(inv_through && Trans->uac[branch].last_received<300)) { #ifdef EXTRA_DEBUG /* don't report on retransmissions */ if (Trans->uac[branch].last_received==new_code) { LM_DBG("final reply retransmission\n"); } else /* if you FR-timed-out, faked a local 408 and 487 came, don't * report on it either */ if (Trans->uac[branch].last_received==408 && new_code==487) { LM_DBG("487 reply came for a timed-out branch\n"); } else { /* this looks however how a very strange status rewrite attempt; * report on it */ LM_DBG("status rewrite by UAS: stored: %d, received: %d\n", Trans->uac[branch].last_received, new_code ); } #endif goto discard; } /* no final response sent yet */ /* negative replies subject to fork picking */ if (new_code >=300 ) { Trans->uac[branch].last_received=new_code; /* also append the current reply to the transaction to * make it available in failure routes - a kind of "fake" * save of the final reply per branch */ Trans->uac[branch].reply = reply; if (new_code>=600 && !disable_6xx_block) { /* this is a winner and close all branches */ which_cancel( Trans, cancel_bitmap ); picked_branch=branch; /* no more new branches should be added to this transaction */ Trans->flags |= T_NO_NEW_BRANCHES_FLAG; } else { /* if all_final return lowest */ picked_branch = t_pick_branch( Trans, &picked_code, &do_cancel); if (picked_branch==-2) { /* branches open yet */ *should_store=1; *should_relay=-1; picked_branch=-1; Trans->uac[branch].reply = 0; return RPS_STORE; } if (picked_branch==-1) { LM_CRIT("pick_branch failed (lowest==-1) for code %d\n",new_code); Trans->uac[branch].reply = 0; goto discard; } if (do_cancel) { branch_bm_t cb = 0; which_cancel( Trans, &cb ); cleanup_uac_timers(Trans); cancel_uacs( Trans, cb); } } /* no more pending branches -- try if that changes after * a callback; save branch count to be able to determine * later if new branches were initiated */ branch_cnt=Trans->nr_of_outgoings; reset_kr(); if ( !(Trans->flags&T_NO_DNS_FAILOVER_FLAG) && Trans->uac[picked_branch].proxy!=NULL ) { /* is is a DNS failover scenario, according to RFC 3263 ? */ if (is_3263_failure(Trans)) { LM_DBG("trying DNS-based failover\n"); /* do DNS failover -> add new branches */ if (do_dns_failover( Trans )!=0) { /* skip the failed added branches */ branch_cnt = Trans->nr_of_outgoings; } } } /* run ON_FAILURE handlers ( route and callbacks) */ if ( branch_cnt==Trans->nr_of_outgoings && (has_tran_tmcbs( Trans, TMCB_ON_FAILURE) || Trans->on_negative) ) { run_failure_handlers( Trans ); } /* now reset it; after the failure logic, the reply may * not be stored any more and we don't want to keep into * transaction some broken reference */ Trans->uac[branch].reply = 0; /* look if the callback perhaps replied transaction; it also covers the case in which a transaction is replied localy on CANCEL -- then it would make no sense to proceed to new branches bellow */ if (Trans->uas.status >= 200) { *should_store=0; *should_relay=-1; /* this might deserve an improvement -- if something was already replied, it was put on wait and then, returning RPS_COMPLETED will make t_on_reply put it on wait again; perhaps splitting put_on_wait from send_reply or a new RPS_ code would be healthy */ picked_branch=-1; return RPS_COMPLETED; } /* look if the callback/failure_route introduced new branches ... */ if (branch_cnt<Trans->nr_of_outgoings && get_kr()==REQ_FWDED) { /* await then result of new branches */ *should_store=1; *should_relay=-1; picked_branch=-1; return RPS_STORE; } /* really no more pending branches -- return selected code */ *should_store=0; *should_relay=picked_branch; picked_branch=-1; return RPS_COMPLETED; } /* not >=300 ... it must be 2xx or provisional 1xx */ if (new_code>=100) { /* 1xx and 2xx except 100 will be relayed */ Trans->uac[branch].last_received=new_code; *should_store=0; *should_relay= new_code==100? -1 : branch; if (new_code>=200 ) { which_cancel( Trans, cancel_bitmap ); return RPS_COMPLETED; } else return RPS_PROVISIONAL; } discard: *should_store=0; *should_relay=-1; return RPS_DISCARDED; }
static int _reply_light( struct cell *trans, char* buf, unsigned int len, unsigned int code, char *to_tag, unsigned int to_tag_len, int lock, struct bookmark *bm) { struct retr_buf *rb; unsigned int buf_len; branch_bm_t cancel_bitmap; str cb_s; if (!buf) { LM_DBG("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 ); } /* and clean-up, including cancellations, if needed */ goto error; } cancel_bitmap=0; if (lock) LOCK_REPLIES( trans ); if ( is_invite(trans) ) which_cancel(trans, &cancel_bitmap ); if (trans->uas.status>=200) { LM_ERR("failed to generate %d reply when a final %d was sent out\n", code, trans->uas.status); goto error2; } rb = & trans->uas.response; rb->activ_type=code; trans->uas.status = code; buf_len = rb->buffer.s ? len : len + REPLY_OVERBUFFER_LEN; rb->buffer.s = (char*)shm_resize( rb->buffer.s, buf_len ); /* puts the reply's buffer to uas.response */ if (! rb->buffer.s ) { LM_ERR("failed to allocate shmem buffer\n"); goto error3; } update_local_tags(trans, bm, rb->buffer.s, buf); rb->buffer.len = len ; memcpy( rb->buffer.s , buf , len ); /* needs to be protected too because what timers are set depends on current transactions status */ /* t_update_timers_after_sending_reply( rb ); */ trans->relaied_reply_branch=-2; if (lock) UNLOCK_REPLIES( trans ); /* do UAC cleanup procedures in case we generated a final answer whereas there are pending UACs */ if (code>=200) { if ( is_local(trans) ) { LM_DBG("local transaction completed\n"); if ( has_tran_tmcbs(trans, TMCB_LOCAL_COMPLETED) ) { run_trans_callbacks( TMCB_LOCAL_COMPLETED, trans, 0, FAKED_REPLY, code); } } else { /* run the PRE send callbacks */ if ( has_tran_tmcbs(trans, TMCB_RESPONSE_PRE_OUT) ) { cb_s.s = buf; cb_s.len = len; set_extra_tmcb_params( &cb_s, &rb->dst); if (lock) run_trans_callbacks_locked( TMCB_RESPONSE_PRE_OUT, trans, trans->uas.request, FAKED_REPLY, code); else run_trans_callbacks( TMCB_RESPONSE_PRE_OUT, trans, trans->uas.request, FAKED_REPLY, code); } } if (!is_hopbyhop_cancel(trans)) { cleanup_uac_timers( trans ); if (is_invite(trans)) cancel_uacs( trans, cancel_bitmap ); /* for auth related replies, we do not do retransmission (via set_final_timer()), but only wait for a final reply (put_on_wait() ) - see RFC 3261 (26.3.2.4 DoS Protection) */ if ((code != 401) && (code != 407)) set_final_timer( trans ); else put_on_wait(trans); } } /* send it out : response.dst.send_sock is valid all the time now, * as it's taken from original request -bogdan */ if (!trans->uas.response.dst.send_sock) { LM_CRIT("send_sock is NULL\n"); } SEND_PR_BUFFER( rb, buf, len ); LM_DBG("reply sent out. buf=%p: %.9s..., " "shmem=%p: %.9s\n", buf, buf, rb->buffer.s, rb->buffer.s ); /* run the POST send callbacks */ if (code>=200&&!is_local(trans)&&has_tran_tmcbs(trans,TMCB_RESPONSE_OUT)) { cb_s.s = buf; cb_s.len = len; set_extra_tmcb_params( &cb_s, &rb->dst); if (lock) run_trans_callbacks_locked( TMCB_RESPONSE_OUT, trans, trans->uas.request, FAKED_REPLY, code); else run_trans_callbacks( TMCB_RESPONSE_OUT, trans, trans->uas.request, FAKED_REPLY, code); } pkg_free( buf ) ; stats_trans_rpl( code, 1 /*local*/ ); LM_DBG("finished\n"); return 1; error3: error2: if (lock) UNLOCK_REPLIES( trans ); pkg_free ( buf ); error: /* do UAC cleanup */ cleanup_uac_timers( trans ); if ( is_invite(trans) ) cancel_uacs( trans, cancel_bitmap ); /* we did not succeed -- put the transaction on wait */ put_on_wait(trans); return -1; }
/* this is the "UAC" above transaction layer; if a final reply is received, it triggers a callback; note well -- it assumes 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) { /* how to deal with replies for local transaction */ int local_store, local_winner; enum rps reply_status; 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; reply_status=t_should_relay_response( t, msg_status, branch, &local_store, &local_winner, cancel_bitmap, p_msg ); LM_DBG("branch=%d, save=%d, winner=%d\n", branch, local_store, local_winner ); if (local_store) { if (!store_reply(t, branch, p_msg)) goto error; } if (local_winner>=0) { winning_msg= branch==local_winner ? p_msg : t->uac[local_winner].reply; if (winning_msg==FAKED_REPLY) { winning_code = branch==local_winner ? msg_status : t->uac[local_winner].last_received; } else { winning_code=winning_msg->REPLY_STATUS; } t->uas.status = winning_code; stats_trans_rpl( winning_code, (winning_msg==FAKED_REPLY)?1:0 ); if (is_invite(t) && winning_msg!=FAKED_REPLY && winning_code>=200 && winning_code <300 && has_tran_tmcbs(t, TMCB_RESPONSE_OUT|TMCB_RESPONSE_PRE_OUT) ) { totag_retr=update_totag_set(t, winning_msg); } } UNLOCK_REPLIES(t); if ( local_winner >= 0 ) { if (winning_code < 200) { if (!totag_retr && has_tran_tmcbs(t,TMCB_LOCAL_RESPONSE_OUT)) { LM_DBG("Passing provisional reply %d to FIFO application\n", winning_code); run_trans_callbacks( TMCB_LOCAL_RESPONSE_OUT, t, 0, winning_msg, winning_code); } } else { LM_DBG("local transaction completed\n"); if (!totag_retr && has_tran_tmcbs(t,TMCB_LOCAL_COMPLETED) ) { run_trans_callbacks( TMCB_LOCAL_COMPLETED, t, 0, winning_msg, winning_code ); } } } return reply_status; error: which_cancel(t, cancel_bitmap); UNLOCK_REPLIES(t); cleanup_uac_timers(t); if ( get_cseq(p_msg)->method_id==METHOD_INVITE ) cancel_uacs( t, *cancel_bitmap ); put_on_wait(t); return RPS_ERROR; }
int t_inject_branch( struct cell *t, struct sip_msg *msg, int flags) { static struct sip_msg faked_req; branch_bm_t cancel_bm; str reason = str_init(CANCEL_REASON_200); int rc; /* does the transaction state still accept new branches ? */ if (t->uas.status >= 200) { LM_DBG("cannot add branches to a transaction with %d UAS\n", t->uas.status); return -2; } if (!fake_req( &faked_req, t->uas.request, &t->uas, NULL, 0)) { LM_ERR("fake_req failed\n"); return -1; } /* do we have the branches to be injected ? */ if (flags&TM_INJECT_SRC_EVENT) { /* get the branch from Event AVPs, as populated by EVI/EBR */ if (ul_contact_event_to_msg( &faked_req )<0) { LM_ERR("failed to grab new branch from Event\n"); goto error; } } else { /* use the RURI+dset array as destinations to be injected */ if (msg->first_line.type==SIP_REQUEST) { /* take the RURI branch from the script msg and move it * into the faked msg (that will be used by t_fwd function) */ if (dst_to_msg( msg, &faked_req )<0) { LM_ERR("failed to grab new branch from Event\n"); goto error; } } else { /* current message is a reply, so take the first branch from dset * and move it into the faked msg (that will be used by t_fwd * function)*/ if (move_branch_to_ruri( 0, &faked_req)<0) { LM_ERR("no branch found to be moved as new destination\n"); goto error; } /* remove it from set */ remove_branch(0); } } /* do we have to cancel the existing branches before injecting new ones? */ if (flags&TM_INJECT_FLAG_CANCEL) { which_cancel( t, &cancel_bm ); } /* generated the new branches, without branch counter reset */ rc = t_forward_nonack( t, &faked_req , NULL, 0, 1/*locked*/ ); /* do we have to cancel the existing branches before injecting new ones? */ if (flags&TM_INJECT_FLAG_CANCEL) { set_cancel_extra_hdrs( reason.s, reason.len); cancel_uacs( t, cancel_bm ); set_cancel_extra_hdrs( NULL, 0); } /* cleanup the faked request */ free_faked_req( &faked_req, t); return (rc==1)?1:-3; error: /* cleanup the faked request */ free_faked_req( &faked_req, t); return -1; }
void e2e_cancel( struct sip_msg *cancel_msg, struct cell *t_cancel, struct cell *t_invite ) { branch_bm_t cancel_bm, tmp_bm; int i; int lowest_error; str backup_uri; int ret; cancel_bm=0; lowest_error=0; backup_uri=cancel_msg->new_uri; /* determine which branches to cancel ... */ which_cancel( t_invite, &cancel_bm ); t_cancel->nr_of_outgoings=t_invite->nr_of_outgoings; /* fix label -- it must be same for reply matching */ t_cancel->label=t_invite->label; /* ... and install CANCEL UACs */ for (i=0; i<t_invite->nr_of_outgoings; i++) if (cancel_bm & (1<<i)) { ret=e2e_cancel_branch(cancel_msg, t_cancel, t_invite, i); if (ret<0) cancel_bm &= ~(1<<i); if (ret<lowest_error) lowest_error=ret; } cancel_msg->new_uri=backup_uri; /* send them out */ for (i=0; i<t_cancel->nr_of_outgoings; i++) { if (cancel_bm & (1<<i)) { /* Provisional reply received on this branch, send CANCEL */ /* No need to stop timers as they have already been stopped by the reply */ if (SEND_BUFFER( &t_cancel->uac[i].request)==-1) { LOG(L_ERR, "ERROR: e2e_cancel: send failed\n"); } start_retr( &t_cancel->uac[i].request ); } else { if (t_invite->uac[i].last_received < 100) { /* No provisional response received, stop * retransmission timers */ reset_timer(&t_invite->uac[i].request.retr_timer); reset_timer(&t_invite->uac[i].request.fr_timer); /* Generate faked reply */ LOCK_REPLIES(t_invite); if (relay_reply(t_invite, FAKED_REPLY, i, 487, &tmp_bm) == RPS_ERROR) { lowest_error = -1; } } } } /* if error occurred, let it know upstream (final reply will also move the transaction on wait state */ if (lowest_error<0) { LOG(L_ERR, "ERROR: cancel error\n"); t_reply( t_cancel, cancel_msg, 500, "cancel error"); /* if there are pending branches, let upstream know we are working on it */ } else if (cancel_bm) { DBG("DEBUG: e2e_cancel: e2e cancel proceeding\n"); t_reply( t_cancel, cancel_msg, 200, CANCELING ); /* if the transaction exists, but there is no more pending branch, tell upstream we're done */ } else { DBG("DEBUG: e2e_cancel: e2e cancel -- no more pending branches\n"); t_reply( t_cancel, cancel_msg, 200, CANCEL_DONE ); } #ifdef LOCAL_487 /* local 487s have been deprecated -- it better handles * race conditions (UAS sending 200); hopefully there are * no longer UACs who go crazy waiting for the 487 whose * forwarding is being blocked by other unresponsive branch */ /* we could await downstream UAS's 487 replies; however, if some of the branches does not do that, we could wait long time and annoy upstream UAC which wants to see a result of CANCEL quickly */ DBG("DEBUG: e2e_cancel: sending 487\n"); /* in case that something in the meantime has been sent upstream (like if FR hit at the same time), don't try to send */ if (t_invite->uas.status>=200) return; /* there is still a race-condition -- the FR can hit now; that's not too bad -- we take care in t_reply's REPLY_LOCK; in the worst case, both this t_reply and other replier will try, and the later one will result in error message "can't reply twice" */ t_reply(t_invite, t_invite->uas.request, 487, CANCELED ); #endif }
void e2e_cancel( struct sip_msg *cancel_msg, struct cell *t_cancel, struct cell *t_invite ) { branch_bm_t cancel_bm; branch_bm_t dummy_bm; int i; int lowest_error; str backup_uri; int rurib_flags; int ret; cancel_bm=0; lowest_error=0; /* e2e_cancel_branch() makes no RURI parsing, so no need to * save the ->parse_uri_ok */ backup_uri = cancel_msg->new_uri; /* branch flags specific to RURI */ rurib_flags = cancel_msg->flags&(~gflags_mask); /* determine which branches to cancel ... */ which_cancel( t_invite, &cancel_bm ); t_cancel->nr_of_outgoings=t_invite->nr_of_outgoings; t_cancel->first_branch=t_invite->first_branch; /* fix label -- it must be same for reply matching */ t_cancel->label=t_invite->label; /* ... and install CANCEL UACs */ for (i=t_invite->first_branch; i<t_invite->nr_of_outgoings; i++) { if (cancel_bm & (1<<i)) { ret=e2e_cancel_branch(cancel_msg, t_cancel, t_invite, i); if (ret<0) cancel_bm &= ~(1<<i); if (ret<lowest_error) lowest_error=ret; } } /* restore new_uri */ cancel_msg->new_uri = backup_uri; cancel_msg->parsed_uri_ok = 0; /* set flags */ cancel_msg->flags = (cancel_msg->flags&gflags_mask)|rurib_flags; t_cancel->uas.request->flags = cancel_msg->flags&gflags_mask; /* send them out */ for (i=t_cancel->first_branch; i<t_cancel->nr_of_outgoings; i++) { if (cancel_bm & (1<<i)) { if (SEND_BUFFER( &t_cancel->uac[i].request)==-1) { LOG(L_ERR, "ERROR: e2e_cancel: send failed\n"); } start_retr( &t_cancel->uac[i].request ); } } /* internally cancel branches with no received reply */ for (i=t_invite->first_branch; i<t_invite->nr_of_outgoings; i++) { if (t_invite->uac[i].last_received==0){ /* mark as cancelled */ t_invite->uac[i].flags |= T_UAC_TO_CANCEL_FLAG; /* reset the "request" timers */ reset_timer(&t_invite->uac[i].request.retr_timer); reset_timer(&t_invite->uac[i].request.fr_timer); LOCK_REPLIES( t_invite ); if (RPS_ERROR==relay_reply(t_invite,FAKED_REPLY,i,487,&dummy_bm)) lowest_error = -1; /* force sending 500 error */ } } /* do not attmpt to send reply for CANCEL if we already did it once; * to work arround the race between receiveing reply and generating * local reply, we better check if we are in failure route (which means that * the reply to UAC is /to be/ sent) or if was actually sent out */ /* calling here t_relay from within failure route will lead to dead lock * on the transaction's reply lock -bogdan */ if (route_type==FAILURE_ROUTE || t_cancel->uas.status>=200) return; /* if error occurred, let it know upstream (final reply will also move the transaction on wait state */ if (lowest_error<0) { LOG(L_ERR, "ERROR: cancel error\n"); t_reply( t_cancel, cancel_msg, 500, "cancel error"); /* if there are pending branches, let upstream know we are working on it */ } else if (cancel_bm) { DBG("DEBUG: e2e_cancel: e2e cancel proceeding\n"); t_reply( t_cancel, cancel_msg, 200, CANCELING ); /* if the transaction exists, but there is no more pending branch, tell upstream we're done */ } else { DBG("DEBUG: e2e_cancel: e2e cancel -- no more pending branches\n"); t_reply( t_cancel, cancel_msg, 200, CANCEL_DONE ); } #ifdef LOCAL_487 /* local 487s have been deprecated -- it better handles * race conditions (UAS sending 200); hopefully there are * no longer UACs who go crazy waiting for the 487 whose * forwarding is being blocked by other unresponsive branch */ /* we could await downstream UAS's 487 replies; however, if some of the branches does not do that, we could wait long time and annoy upstream UAC which wants to see a result of CANCEL quickly */ DBG("DEBUG: e2e_cancel: sending 487\n"); /* in case that something in the meantime has been sent upstream (like if FR hit at the same time), don't try to send */ if (t_invite->uas.status>=200) return; /* there is still a race-condition -- the FR can hit now; that's not too bad -- we take care in t_reply's REPLY_LOCK; in the worst case, both this t_reply and other replier will try, and the later one will result in error message "can't reply twice" */ t_reply(t_invite, t_invite->uas.request, 487, CANCELED ); #endif }
void e2e_cancel( struct sip_msg *cancel_msg, struct cell *t_cancel, struct cell *t_invite ) { branch_bm_t cancel_bm; int i; int lowest_error; str backup_uri; int ret; cancel_bm=0; lowest_error=0; backup_uri=cancel_msg->new_uri; /* determine which branches to cancel ... */ which_cancel( t_invite, &cancel_bm ); t_cancel->nr_of_outgoings=t_invite->nr_of_outgoings; /* fix label -- it must be same for reply matching */ t_cancel->label=t_invite->label; /* ... and install CANCEL UACs */ for (i=0; i<t_invite->nr_of_outgoings; i++) if (cancel_bm & (1<<i)) { ret=e2e_cancel_branch(cancel_msg, t_cancel, t_invite, i); if (ret<0) cancel_bm &= ~(1<<i); if (ret<lowest_error) lowest_error=ret; } cancel_msg->new_uri=backup_uri; /* send them out */ for (i=0; i<t_cancel->nr_of_outgoings; i++) { if (cancel_bm & (1<<i)) { if (SEND_BUFFER( &t_cancel->uac[i].request)==-1) { LOG(L_ERR, "ERROR: e2e_cancel: send failed\n"); } start_retr( &t_cancel->uac[i].request ); } } /* if error occured, let it know upstream (final reply will also move the transaction on wait state */ if (lowest_error<0) { LOG(L_ERR, "ERROR: cancel error\n"); t_reply( t_cancel, cancel_msg, 500, "cancel error"); /* if there are pending branches, let upstream know we are working on it */ } else if (cancel_bm) { DBG("DEBUG: e2e_cancel: e2e cancel proceeding\n"); t_reply( t_cancel, cancel_msg, 200, CANCELLING ); /* if the transaction exists, but there is no more pending branch, tell usptream we're done */ } else { DBG("DEBUG: e2e_cancel: e2e cancel -- no more pending branches\n"); t_reply( t_cancel, cancel_msg, 200, CANCEL_DONE ); } /* we could await downstream UAS's 487 replies; however, if some of the branches does not do that, we could wait long time and annoy upstream UAC which wants to see a result of CANCEL quickly */ DBG("DEBUG: e2e_cancel: sending 487\n"); /* in case that something in the meantime has been sent upstream (like if FR hit at the same time), don't try to send */ if (t_invite->uas.status>=200) return; /* there is still a race-condition -- the FR can hit now; that's not too bad -- we take care in t_reply's REPLY_LOCK; in the worst case, both this t_reply and other replier will try, and the later one will result in error message "can't reply twice" */ t_reply(t_invite, t_invite->uas.request, 487, CANCELLED ); }