/* * Build and send an ACK to a negative reply */ static int send_ack(struct sip_msg* rpl, struct cell *trans, int branch) { str method = str_init(ACK); str to; char *ack_p; unsigned int ack_len; if(parse_headers(rpl,is_local(trans)?HDR_EOH_F:(HDR_TO_F|HDR_FROM_F),0)==-1 || !rpl->to || !rpl->from ) { LM_ERR("failed to generate a HBH ACK if key HFs in reply missing\n"); goto error; } to.s=rpl->to->name.s; to.len=rpl->to->len; ack_p = is_local(trans)? build_dlg_ack(rpl, trans, branch, &to, &ack_len): build_local( trans, branch, &method, NULL, rpl, &ack_len ); if (ack_p==0) { LM_ERR("failed to build ACK\n"); goto error; } SEND_PR_BUFFER(&trans->uac[branch].request, ack_p, ack_len); shm_free(ack_p); return 0; error: return -1; }
char *build_cancel(struct cell *Trans,unsigned int branch, unsigned int *len ) { str method = str_init(CANCEL); str reason = str_init(CANCEL_REASON CRLF); str *extra = NULL; /* add the reason hdr, as per RFC 3326 */ if (is_invite(Trans) && Trans->uas.status==200) { extra = &reason; } else if (_extra_cancel_hdrs.s) { extra = &_extra_cancel_hdrs; } return build_local( Trans, branch, &method, extra, Trans->uac[branch].reply , len ); }
char *build_cancel(struct cell *Trans,unsigned int branch, unsigned int *len ) { str method = str_init(CANCEL); str reason = str_init(CANCEL_REASON CRLF); str *extra = NULL; /* add the reason hdr, as per RFC 3326 */ if (is_invite(Trans) && Trans->uas.status==200) { extra = &reason; } else if (_extra_cancel_hdrs.s) { extra = &_extra_cancel_hdrs; } return build_local( Trans, branch, &method, extra, NULL /*reply*/ , len ); /* ^^^^ when CANCELing, there are 0 chances to have a reply stored into * transaction ; set it NULL to avoid using the temporary stored reply * (by t_should_relay_response) which may lead into races ( building the * cancel versus handling a final response in a different process )*/ }
/* should be called directly only if one of the condition bellow is true: * - prepare_cancel_branch or prepare_to_cancel returned true for this branch * - buffer value was 0 and then set to BUSY in an atomic op.: * if (atomic_cmpxchg_long(&buffer, 0, BUSY_BUFFER)==0). * * 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 * F_CANCEL_B_FAKE_REPLY - will send a fake 487 * to all branches that haven't received any response * (>=100). It assumes the REPLY_LOCK is not held * (if it is => deadlock) * F_CANCEL_B_FORCE_C - will send a cancel (and create the * corresp. local cancel rb) even if no reply was * received; F_CANCEL_B_FAKE_REPLY will be ignored. * F_CANCEL_B_FORCE_RETR - don't stop retransmission if no * reply was received on the branch; incompatible * with F_CANCEL_B_FAKE_REPLY, F_CANCEL_B_FORCE_C and * F_CANCEL_B_KILL (all of them take precedence) a * default: stop only the retransmissions for the branch * and leave it to timeout if it doesn't receive any * response to the CANCEL * returns: 0 - branch inactive after running cancel_branch() * 1 - branch still active (fr_timer) * -1 - error * WARNING: * - F_CANCEL_B_KILL should be used only if the transaction is killed * explicitly afterwards (since it might kill all the timers * the transaction won't be able to "kill" itself => if not * explicitly "put_on_wait" it might live forever) * - F_CANCEL_B_FAKE_REPLY must be used only if the REPLY_LOCK is not * held * - 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, #ifdef CANCEL_REASON_SUPPORT struct cancel_reason* reason, #endif /* CANCEL_REASON_SUPPORT */ int flags ) { char *cancel; unsigned int len; struct retr_buf *crb, *irb; int ret; struct cancel_info tmp_cd; void* pcbuf; crb=&t->uac[branch].local_cancel; irb=&t->uac[branch].request; irb->flags|=F_RB_CANCELED; ret=1; init_cancel_info(&tmp_cd); # ifdef EXTRA_DEBUG if (crb->buffer!=BUSY_BUFFER) { LOG(L_CRIT, "ERROR: attempt to rewrite cancel buffer: %p\n", crb->buffer); abort(); } # endif if (flags & F_CANCEL_B_KILL){ stop_rb_timers( irb ); ret=0; if ((t->uac[branch].last_received < 100) && !(flags & F_CANCEL_B_FORCE_C)) { DBG("DEBUG: cancel_branch: no response ever received: " "giving up on cancel\n"); /* remove BUSY_BUFFER -- mark cancel buffer as not used */ pcbuf=&crb->buffer; /* workaround for type punning warnings */ atomic_set_long(pcbuf, 0); /* try to relay auto-generated 487 canceling response only when * another one is not under relaying on the branch and there is * no forced response per transaction from script */ if((flags & F_CANCEL_B_FAKE_REPLY) && !(irb->flags&F_RB_RELAYREPLY) && !(t->flags&T_ADMIN_REPLY)) { LOCK_REPLIES(t); if (relay_reply(t, FAKED_REPLY, branch, 487, &tmp_cd, 1) == RPS_ERROR){ return -1; } } /* do nothing, hope that the caller will clean up */ return ret; } }else{ if (t->uac[branch].last_received < 100){ if (!(flags & F_CANCEL_B_FORCE_C)) { /* no response received => don't send a cancel on this branch, * just drop it */ if (!(flags & F_CANCEL_B_FORCE_RETR)) stop_rb_retr(irb); /* stop retransmissions */ /* remove BUSY_BUFFER -- mark cancel buffer as not used */ pcbuf=&crb->buffer; /* workaround for type punning warnings */ atomic_set_long(pcbuf, 0); 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_cd, 1)== RPS_ERROR){ return -1; } return 0; /* should be inactive after the 487 */ } /* do nothing, just wait for the final timeout */ return 1; } } stop_rb_retr(irb); /* stop retransmissions */ } if (cfg_get(tm, tm_cfg, reparse_invite) || (t->uas.request && t->uas.request->msg_flags&(FL_USE_UAC_FROM|FL_USE_UAC_TO))) { /* build the CANCEL from the INVITE which was sent out */ cancel = build_local_reparse(t, branch, &len, CANCEL, CANCEL_LEN, (t->uas.request && t->uas.request->msg_flags&FL_USE_UAC_TO)?0:&t->to #ifdef CANCEL_REASON_SUPPORT , reason #endif /* CANCEL_REASON_SUPPORT */ ); } else { /* build the CANCEL from the received INVITE */ cancel = build_local(t, branch, &len, CANCEL, CANCEL_LEN, &t->to #ifdef CANCEL_REASON_SUPPORT , reason #endif /* CANCEL_REASON_SUPPORT */ ); } if (!cancel) { LOG(L_ERR, "ERROR: attempt to build a CANCEL failed\n"); /* remove BUSY_BUFFER -- mark cancel buffer as not used */ pcbuf=&crb->buffer; /* workaround for type punning warnings */ atomic_set_long(pcbuf, 0); return -1; } /* install cancel now */ crb->dst = irb->dst; crb->branch = branch; /* label it as cancel so that FR timer can better know how to deal with it */ crb->activ_type = TYPE_LOCAL_CANCEL; /* be extra carefully and check for bugs (the below if could be replaced * by an atomic_set((void*)&crb->buffer, cancel) */ if (unlikely(atomic_cmpxchg_long((void*)&crb->buffer, (long)BUSY_BUFFER, (long)cancel)!= (long)BUSY_BUFFER)){ BUG("tm: cancel_branch: local_cancel buffer=%p != BUSY_BUFFER" " (trying to continue)\n", crb->buffer); shm_free(cancel); return -1; } membar_write_atomic_op(); /* cancel retr. can be called from reply_received w/o the reply lock held => they check for buffer_len to see if a valid reply exists */ crb->buffer_len = len; DBG("DEBUG: cancel_branch: sending cancel...\n"); if (SEND_BUFFER( crb )>=0){ if (unlikely (has_tran_tmcbs(t, TMCB_REQUEST_OUT))) run_trans_callbacks_with_buf(TMCB_REQUEST_OUT, crb, t->uas.request, 0, TMCB_LOCAL_F); if (unlikely (has_tran_tmcbs(t, TMCB_REQUEST_SENT))) run_trans_callbacks_with_buf(TMCB_REQUEST_SENT, crb, t->uas.request, 0, TMCB_LOCAL_F); } /*sets and starts the FINAL RESPONSE timer */ if (start_retr( crb )!=0) LOG(L_CRIT, "BUG: cancel_branch: failed to start retransmission" " for %p\n", crb); return ret; }