Esempio n. 1
0
/* return (ticks_t)-1 on error/disable and 0 on success */
inline static ticks_t retransmission_handler(struct retr_buf *r_buf)
{
#ifdef EXTRA_DEBUG
	if(r_buf->my_T->flags & T_IN_AGONY) {
		LM_ERR("transaction %p scheduled for deletion and"
			   " called from RETR timer (flags %x)\n",
				r_buf->my_T, r_buf->my_T->flags);
		abort();
	}
#endif
	if(r_buf->rbtype == TYPE_LOCAL_CANCEL
			|| r_buf->rbtype == TYPE_REQUEST) {
#ifdef EXTRA_DEBUG
		LM_DBG("request resending (t=%p, %.9s ... )\n", r_buf->my_T,
				r_buf->buffer);
#endif
		if(SEND_BUFFER(r_buf) == -1) {
			/* disable retr. timers => return -1 */
			fake_reply(r_buf->my_T, r_buf->branch, 503);
			return (ticks_t)-1;
		}
		if(unlikely(has_tran_tmcbs(r_buf->my_T, TMCB_REQUEST_SENT)))
			run_trans_callbacks_with_buf(
					TMCB_REQUEST_SENT, r_buf, 0, 0, TMCB_RETR_F);
	} else {
#ifdef EXTRA_DEBUG
		LM_DBG("reply resending (t=%p, %.9s ... )\n", r_buf->my_T,
				r_buf->buffer);
#endif
		t_retransmit_reply(r_buf->my_T);
	}

	return 0;
}
Esempio n. 2
0
static inline void send_prepared_request_impl(struct retr_buf *request, int retransmit)
{
	if (SEND_BUFFER(request) == -1) {
		LOG(L_ERR, "t_uac: Attempt to send to precreated request failed\n");
	}
	
	if (retransmit && (start_retr(request)!=0))
		LOG(L_CRIT, "BUG: t_uac: failed to start retr. for %p\n", request);
}
Esempio n. 3
0
static inline void send_prepared_request_impl(struct retr_buf *request, int retransmit)
{
	if (SEND_BUFFER(request) == -1) {
		LOG(L_ERR, "t_uac: Attempt to send to precreated request failed\n");
	}
	else if (unlikely(has_tran_tmcbs(request->my_T, TMCB_REQUEST_SENT)))
		/* we don't know the method here */
			run_trans_callbacks_with_buf(TMCB_REQUEST_SENT, request, 0, 0,
			TMCB_LOCAL_F);
	
	if (retransmit && (start_retr(request)!=0))
		LOG(L_CRIT, "BUG: t_uac: failed to start retr. for %p\n", request);
}
Esempio n. 4
0
inline static void retransmission_handler( struct timer_link *retr_tl )
{
	struct retr_buf* r_buf ;
	enum lists id;

	r_buf = get_retr_timer_payload(retr_tl);
#ifdef EXTRA_DEBUG
	if (r_buf->my_T->damocles) {
		LM_ERR("transaction %p scheduled for deletion and"
			" called from RETR timer\n",r_buf->my_T);
		abort();
	}
#endif

	/* the transaction is already removed from RETRANSMISSION_LIST by timer*/
	/* re-transmission */
	if ( r_buf->activ_type==TYPE_LOCAL_CANCEL
		|| r_buf->activ_type==TYPE_REQUEST ) {
			LM_DBG("retransmission_handler : request resending"
				" (t=%p, %.9s ... )\n", r_buf->my_T, r_buf->buffer.s);
			set_t(r_buf->my_T);
			SEND_BUFFER( r_buf );
			/*if (SEND_BUFFER( r_buf )==-1) {
				reset_timer( &r_buf->fr_timer );
				fake_reply(r_buf->my_T, r_buf->branch, 503 );
				return;
			}*/
			set_t(T_UNDEFINED);
	} else {
			LM_DBG("retransmission_handler : reply resending "
				"(t=%p, %.9s ... )\n", r_buf->my_T, r_buf->buffer.s);
			set_t(r_buf->my_T);
			t_retransmit_reply(r_buf->my_T);
			set_t(T_UNDEFINED);
	}

	id = r_buf->retr_list;
	r_buf->retr_list = id < RT_T2 ? id + 1 : RT_T2;

	retr_tl->timer_list= NULL; /* set to NULL so that set_timer will work */
	set_timer( retr_tl, id < RT_T2 ? id + 1 : RT_T2, 0 );

	LM_DBG("retransmission_handler : done\n");
}
Esempio n. 5
0
void test_sporadic_refined_model__the_proc_the_sender_entrypoint_impl(
	BUFFER_ID_TYPE *  cnx_p_out,
	EVENT_ID_TYPE *  cnx_p_out_entrypoint_barrier)
{
  RETURN_CODE_TYPE test_sporadic_refined_model__the_proc_the_sender_entrypoint_impl_the_proc_the_sender_runtime_call_ret;
  common_pkg__Integer test_sporadic_refined_model__the_proc_the_sender_entrypoint_impl_p_out_localVariable;
  MESSAGE_SIZE_TYPE test_sporadic_refined_model__the_proc_the_sender_entrypoint_impl_p_out_Length = sizeof(common_pkg__Integer);
  SYSTEM_TIME_TYPE test_sporadic_refined_model__the_proc_the_sender_entrypoint_impl_p_out_TimeOut = 0;
  test_sporadic_refined_model__the_proc_the_sender_behaviorIdentifier_enum whichPortActivated = test_sporadic_refined_model__the_proc_the_sender_behaviorIdentifier_enum_default_behavior;
while(1)
{
  switch(the_proc_the_sender_entrypoint_impl_current_state)
  {
    case test_sporadic_refined_model__the_proc_the_sender_entrypoint_impl_BA_entrypoint_init_state:
    // Transition id: which_behavior_default_mode
    if(1) // no execution condition
    {
      the_proc_the_sender_entrypoint_impl_current_state = test_sporadic_refined_model__the_proc_the_sender_entrypoint_impl_BA_entrypoint_exec_state;
      whichPortActivated = test_sporadic_refined_model__the_proc_the_sender_behaviorIdentifier_enum_default_behavior;
      break;
    }
    case test_sporadic_refined_model__the_proc_the_sender_entrypoint_impl_BA_entrypoint_wait_dispatch_state:
    // Transition id: dispatch_transition
    if(1) // no execution condition
    {
      the_proc_the_sender_entrypoint_impl_current_state = test_sporadic_refined_model__the_proc_the_sender_entrypoint_impl_BA_entrypoint_exec_state;
      PERIODIC_WAIT (&test_sporadic_refined_model__the_proc_the_sender_entrypoint_impl_the_proc_the_sender_runtime_call_ret);
      break;
    }
    case test_sporadic_refined_model__the_proc_the_sender_entrypoint_impl_BA_entrypoint_exec_state:
    // Transition id: call -- Priority 0
    if(whichPortActivated == test_sporadic_refined_model__the_proc_the_sender_behaviorIdentifier_enum_default_behavior)
    {
      the_proc_the_sender_entrypoint_impl_current_state = test_sporadic_refined_model__the_proc_the_sender_entrypoint_impl_BA_entrypoint_wait_dispatch_state;
      send (&test_sporadic_refined_model__the_proc_the_sender_entrypoint_impl_p_out_localVariable);
      SEND_BUFFER ((*cnx_p_out), &test_sporadic_refined_model__the_proc_the_sender_entrypoint_impl_p_out_localVariable, test_sporadic_refined_model__the_proc_the_sender_entrypoint_impl_p_out_Length, test_sporadic_refined_model__the_proc_the_sender_entrypoint_impl_p_out_TimeOut, &test_sporadic_refined_model__the_proc_the_sender_entrypoint_impl_the_proc_the_sender_runtime_call_ret);
      SET_EVENT ((*cnx_p_out_entrypoint_barrier), &test_sporadic_refined_model__the_proc_the_sender_entrypoint_impl_the_proc_the_sender_runtime_call_ret);
      break;
    }
  }
}
}
Esempio n. 6
0
void cancel_branch( struct cell *t, int branch )
{
	char *cancel;
	unsigned int len;
	struct retr_buf *crb, *irb;

	crb=&t->uac[branch].local_cancel;
	irb=&t->uac[branch].request;

#	ifdef EXTRA_DEBUG
	if (crb->buffer.s!=0 && crb->buffer.s!=BUSY_BUFFER) {
		LM_CRIT("attempt to rewrite cancel buffer failed\n");
		abort();
	}
#	endif

	cancel=build_cancel(t, branch, &len);
	if (!cancel) {
		LM_ERR("attempt to build a CANCEL failed\n");
		return;
	}
	/* install cancel now */
	crb->buffer.s=cancel;
	crb->buffer.len=len;
	crb->dst=irb->dst;
	crb->branch=branch;
	/* label it as cancel so that FR timer can better now how
	 * to deal with it */
	crb->activ_type=TYPE_LOCAL_CANCEL;

	if ( has_tran_tmcbs( t, TMCB_REQUEST_BUILT) ) {
		set_extra_tmcb_params( &crb->buffer, &crb->dst);
		run_trans_callbacks( TMCB_REQUEST_BUILT, t, t->uas.request,0,
			-t->uas.request->REQ_METHOD);
	}

	LM_DBG("sending cancel...\n");
	SEND_BUFFER( crb );

	/*sets and starts the FINAL RESPONSE timer */
	start_retr( crb );
}
Esempio n. 7
0
/* function returns:
 *       1 - forward successful
 *      -1 - error during forward
 */
int t_forward_nonack( struct cell *t, struct sip_msg* p_msg ,
						struct proxy_l * proxy, int reset_bcounter, int locked)
{
	str reply_reason_487 = str_init("Request Terminated");
	str backup_uri;
	str backup_dst;
	int branch_ret, lowest_ret;
	str current_uri;
	branch_bm_t  added_branches;
	int i, q;
	struct cell *t_invite;
	int success_branch;
	str dst_uri;
	struct socket_info *bk_sock;
	unsigned int br_flags, bk_bflags;
	int idx;
	str path;
	str bk_path;

	/* make -Wall happy */
	current_uri.s=0;

	/* before doing enything, update the t flags from msg */
	t->uas.request->flags = p_msg->flags;

	if (p_msg->REQ_METHOD==METHOD_CANCEL) {
		t_invite=t_lookupOriginalT(  p_msg );
		if (t_invite!=T_NULL_CELL) {
			t_invite->flags |= T_WAS_CANCELLED_FLAG;
			cancel_invite( p_msg, t, t_invite, locked );
			return 1;
		}
	}

	/* do not forward requests which were already cancelled*/
	if (no_new_branches(t)) {
		LM_INFO("discarding fwd for a 6xx transaction\n");
		ser_error = E_NO_DESTINATION;
		return -1;
	}
	if (was_cancelled(t)) {
		/* is this the first attempt of sending a branch out ? */
		if (t->nr_of_outgoings==0) {
			/* if no other signalling was performed on the transaction
			 * and the transaction was already canceled, better
			 * internally generate the 487 reply here */
			if (locked)
				t_reply_unsafe( t, p_msg , 487 , &reply_reason_487);
			else
				t_reply( t, p_msg , 487 , &reply_reason_487);
		}
		LM_INFO("discarding fwd for a cancelled transaction\n");
		ser_error = E_NO_DESTINATION;
		return -1;
	}

	/* backup current uri, sock and flags... add_uac changes it */
	backup_uri = p_msg->new_uri;
	backup_dst = p_msg->dst_uri;
	bk_sock = p_msg->force_send_socket;
	bk_path = p_msg->path_vec;
	bk_bflags = p_msg->ruri_bflags;
	/* advertised address/port are not changed */

	/* check if the UAS retranmission port needs to be updated */
	if ( (p_msg->msg_flags ^ t->uas.request->msg_flags) & FL_FORCE_RPORT )
		su_setport( &t->uas.response.dst.to, p_msg->rcv.src_port );

	/* if no more specific error code is known, use this */
	lowest_ret=E_BUG;
	/* branches added */
	added_branches=0;
	/* branch to begin with */
	if (reset_bcounter) {
		t->first_branch=t->nr_of_outgoings;
		/* check if the previous branch is a PHONY one and if yes
		 * keep it in the set of active branches; that means the 
		 * transaction had a t_wait_for_new_branches() call prior to relay() */
		if ( t->first_branch>0 &&
		(t->uac[t->first_branch-1].flags & T_UAC_IS_PHONY) )
			t->first_branch--;
	}

	/* as first branch, use current uri */
	current_uri = *GET_RURI(p_msg);
	branch_ret = add_uac( t, p_msg, &current_uri, &backup_dst,
		getb0flags(p_msg), &p_msg->path_vec, proxy);
	if (branch_ret>=0)
		added_branches |= 1<<branch_ret;
	else
		lowest_ret=branch_ret;

	/* ....and now add the remaining additional branches */
	for( idx=0; (current_uri.s=get_branch( idx, &current_uri.len, &q,
	&dst_uri, &path, &br_flags, &p_msg->force_send_socket))!=0 ; idx++ ) {
		branch_ret = add_uac( t, p_msg, &current_uri, &dst_uri,
			br_flags, &path, proxy);
		/* pick some of the errors in case things go wrong;
		   note that picking lowest error is just as good as
		   any other algorithm which picks any other negative
		   branch result */
		if (branch_ret>=0)
			added_branches |= 1<<branch_ret;
		else
			lowest_ret=branch_ret;
	}
	/* consume processed branches */
	clear_branches();

	/* restore original stuff */
	p_msg->new_uri=backup_uri;
	p_msg->parsed_uri_ok = 0;/* just to be sure; add_uac may parse other uris*/
	p_msg->dst_uri = backup_dst;
	p_msg->force_send_socket = bk_sock;
	p_msg->path_vec = bk_path;
	p_msg->ruri_bflags = bk_bflags;

	/* update on_branch, _only_ if modified, otherwise it overwrites
	 * whatever it is already in the transaction */
	if (get_on_branch())
		t->on_branch = get_on_branch();

	/* update flags, if changed in branch route */
	t->uas.request->flags = p_msg->flags;

	/* things went wrong ... no new branch has been fwd-ed at all */
	if (added_branches==0) {
		LM_ERR("failure to add branches\n");
		ser_error = lowest_ret;
		return lowest_ret;
	}

	/* send them out now */
	success_branch=0;
	for (i=t->first_branch; i<t->nr_of_outgoings; i++) {
		if (added_branches & (1<<i)) {

			if (t->uac[i].br_flags & tcp_no_new_conn_bflag)
				tcp_no_new_conn = 1;

			do {
				if (check_blacklists( t->uac[i].request.dst.proto,
				&t->uac[i].request.dst.to,
				t->uac[i].request.buffer.s,
				t->uac[i].request.buffer.len)) {
					LM_DBG("blocked by blacklists\n");
					ser_error=E_IP_BLOCKED;
				} else {
					run_trans_callbacks(TMCB_PRE_SEND_BUFFER, t, p_msg, 0, i);

					if (SEND_BUFFER( &t->uac[i].request)==0) {
						ser_error = 0;
						break;
					}

					LM_ERR("sending request failed\n");
					ser_error=E_SEND;
				}
				/* get next dns entry */
				if ( t->uac[i].proxy==0 ||
				get_next_su( t->uac[i].proxy, &t->uac[i].request.dst.to,
				(ser_error==E_IP_BLOCKED)?0:1)!=0 )
					break;
				t->uac[i].request.dst.proto = t->uac[i].proxy->proto;
				/* update branch */
				if ( update_uac_dst( p_msg, &t->uac[i] )!=0)
					break;
			}while(1);

			tcp_no_new_conn = 0;

			if (ser_error) {
				shm_free(t->uac[i].request.buffer.s);
				t->uac[i].request.buffer.s = NULL;
				t->uac[i].request.buffer.len = 0;
				continue;
			}

			success_branch++;

			start_retr( &t->uac[i].request );
			set_kr(REQ_FWDED);

			/* successfully sent out -> run callbacks */
			if ( has_tran_tmcbs( t, TMCB_REQUEST_BUILT|TMCB_MSG_SENT_OUT) ) {
				set_extra_tmcb_params( &t->uac[i].request.buffer,
					&t->uac[i].request.dst);
				run_trans_callbacks( TMCB_REQUEST_BUILT|TMCB_MSG_SENT_OUT, t,
					p_msg, 0, 0);
			}

		}
	}

	return (success_branch>0)?1:-1;
}
Esempio n. 8
0
/**
 * This function cancels a previously created local invite
 * transaction. The cancel parameter should NOT have any via (CANCEL is
 * hop by hop). returns 0 if error return >0 if OK (returns the LABEL of
 * the cancel).*/
unsigned int t_uac_cancel( str *headers, str *body,
		unsigned int cancelled_hashIdx, unsigned int cancelled_label,
		transaction_cb cb, void* cbp)
{
	struct cell *t_invite,*cancel_cell;
	struct retr_buf *cancel,*invite;
	unsigned int len,ret;
	char *buf;

	ret=0;
	if(t_lookup_ident(&t_invite,cancelled_hashIdx,cancelled_label)<0){
		LM_ERR("failed to t_lookup_ident hash_idx=%d,"
				"label=%d\n", cancelled_hashIdx,cancelled_label);
		return 0;
	}
	/* <sanity_checks> */
	if(! is_local(t_invite))
	{
		LM_ERR("tried to cancel a non-local transaction\n");
		goto error3;
	}
	if(t_invite->uac[0].last_received < 100)
	{
		LM_WARN("trying to cancel a transaction not in "
					"Proceeding state !\n");
		goto error3;
	}
	if(t_invite->uac[0].last_received > 200)
	{
		LM_WARN("trying to cancel a completed transaction !\n");
		goto error3;
	}
	/* </sanity_checks*/
	/* <build_cell> */
	if(!(cancel_cell = build_cell(0))){
		ret=0;
		LM_ERR("no more shm memory!\n");
		goto error3;
	}
	reset_avps();
	if(cb && insert_tmcb(&(cancel_cell->tmcb_hl),
			TMCB_RESPONSE_IN|TMCB_LOCAL_COMPLETED,cb,cbp,0)!=1){
		ret=0;
		LM_ERR("short of tmcb shmem !\n");
		goto error2;
	}
	/* </build_cell> */

	/* <insert_into_hashtable> */
	cancel_cell->flags |= T_IS_LOCAL_FLAG;
	cancel_cell->hash_index=t_invite->hash_index;

	LOCK_HASH(cancel_cell->hash_index);
	insert_into_hash_table_unsafe(cancel_cell,cancel_cell->hash_index);
	ret=cancel_cell->label;
	cancel_cell->label=t_invite->label;
	UNLOCK_HASH(cancel_cell->hash_index);
	/* </insert_into_hashtable> */

	/* <prepare_cancel> */

	cancel=&cancel_cell->uac[0].request;
	invite=&t_invite->uac[0].request;

	cancel->dst.to              = invite->dst.to;
	cancel->dst.send_sock       = invite->dst.send_sock;
	cancel->dst.proto           = invite->dst.proto;
	//cancel->dst.proto_reserved1 = invite->dst.proto_reserved1;

	if(!(buf = build_uac_cancel(headers,body,t_invite,0,&len,
					&(cancel->dst)))){
		ret=0;
		LM_ERR("attempt to build a CANCEL failed\n");
		goto error1;
	}
	cancel->buffer=buf;
	cancel->buffer_len=len;
	cancel_cell->method.s = buf;
	cancel_cell->method.len = 6 /*c-a-n-c-e-l*/;
	/* </prepare_cancel> */

	/* <strart_sending> */
	cancel_cell->nr_of_outgoings++;
	if (SEND_BUFFER(cancel)==-1) {
		ret=0;
		LM_ERR("send failed\n");
		goto error1;
	}
	start_retr(cancel);
	/* </start_sending> */

	return ret;

error1:
	LOCK_HASH(cancel_cell->hash_index);
	remove_from_hash_table_unsafe(cancel_cell);
	UNLOCK_HASH(cancel_cell->hash_index);
error2:
	free_cell(cancel_cell);
error3:
	return ret;
}
Esempio n. 9
0
/* 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;
}
Esempio n. 10
0
/* function returns:
 *       1 - forward successful
 *      -1 - error during forward
 */
int t_forward_nonack( struct cell *t, struct sip_msg* p_msg ,
	struct proxy_l * proxy)
{
	str backup_uri;
	str backup_dst;
	int branch_ret, lowest_ret;
	str current_uri;
	branch_bm_t  added_branches;
	int i, q;
	struct cell *t_invite;
	int success_branch;
	str dst_uri;
	struct socket_info *bk_sock;
	unsigned int br_flags;
	unsigned int bk_br_flags;
	int idx;
	str path;
	str bk_path;

	/* make -Wall happy */
	current_uri.s=0;

	/* before doing enything, update the t falgs from msg */
	t->uas.request->flags = p_msg->flags;

	if (p_msg->REQ_METHOD==METHOD_CANCEL) {
		t_invite=t_lookupOriginalT(  p_msg );
		if (t_invite!=T_NULL_CELL) {
			t_invite->flags |= T_WAS_CANCELLED_FLAG;
			cancel_invite( p_msg, t, t_invite );
			return 1;
		}
	}

	/* do not forward requests which were already cancelled*/
	if (was_cancelled(t) || no_new_branches(t)) {
		LM_ERR("discarding fwd for a cancelled/6xx transaction\n");
		ser_error = E_NO_DESTINATION;
		return -1;
	}

	/* backup current uri, sock and flags... add_uac changes it */
	backup_uri = p_msg->new_uri;
	backup_dst = p_msg->dst_uri;
	bk_sock = p_msg->force_send_socket;
	bk_br_flags = getb0flags();
	bk_path = p_msg->path_vec;

	/* check if the UAS retranmission port needs to be updated */
	if ( (p_msg->msg_flags ^ t->uas.request->msg_flags) & FL_FORCE_RPORT )
		su_setport( &t->uas.response.dst.to, p_msg->rcv.src_port );

	/* if no more specific error code is known, use this */
	lowest_ret=E_BUG;
	/* branches added */
	added_branches=0;
	/* branch to begin with */
	t->first_branch=t->nr_of_outgoings;

	/* as first branch, use current uri */
	current_uri = *GET_RURI(p_msg);
	branch_ret = add_uac( t, p_msg, &current_uri, &backup_dst,
		&p_msg->path_vec, proxy);
	if (branch_ret>=0)
		added_branches |= 1<<branch_ret;
	else
		lowest_ret=branch_ret;

	/* ....and now add the remaining additional branches */
	for( idx=0; (current_uri.s=get_branch( idx, &current_uri.len, &q,
	&dst_uri, &path, &br_flags, &p_msg->force_send_socket))!=0 ; idx++ ) {
		setb0flags(br_flags);
		branch_ret = add_uac( t, p_msg, &current_uri, &dst_uri, &path, proxy);
		/* pick some of the errors in case things go wrong;
		   note that picking lowest error is just as good as
		   any other algorithm which picks any other negative
		   branch result */
		if (branch_ret>=0)
			added_branches |= 1<<branch_ret;
		else
			lowest_ret=branch_ret;
	}
	/* consume processed branches */
	clear_branches();

	/* restore original stuff */
	p_msg->new_uri=backup_uri;
	p_msg->parsed_uri_ok = 0;/* just to be sure; add_uac may parse other uris*/
	p_msg->dst_uri = backup_dst;
	p_msg->force_send_socket = bk_sock;
	p_msg->path_vec = bk_path;
	setb0flags(bk_br_flags);
	/* update on_branch, if modified */
	t->on_branch = get_on_branch();
	/* update flags, if changed in branch route */
	t->uas.request->flags = p_msg->flags;

	/* things went wrong ... no new branch has been fwd-ed at all */
	if (added_branches==0) {
		LM_ERR("failure to add branches\n");
		ser_error = lowest_ret;
		return lowest_ret;
	}

	/* send them out now */
	success_branch=0;
	for (i=t->first_branch; i<t->nr_of_outgoings; i++) {
		if (added_branches & (1<<i)) {

#ifdef USE_TCP
			if (t->uac[i].br_flags & tcp_no_new_conn_bflag)
				tcp_no_new_conn = 1;
#endif

			do {
				if (check_blacklists( t->uac[i].request.dst.proto,
				&t->uac[i].request.dst.to,
				t->uac[i].request.buffer.s,
				t->uac[i].request.buffer.len)) {
					LM_DBG("blocked by blacklists\n");
					ser_error=E_IP_BLOCKED;
				} else {
					if (SEND_BUFFER( &t->uac[i].request)==0) {
						ser_error = 0;
						break;
					}
					LM_ERR("sending request failed\n");
					ser_error=E_SEND;
				}
				/* get next dns entry */
				if ( t->uac[i].proxy==0 ||
				get_next_su( t->uac[i].proxy, &t->uac[i].request.dst.to,
				(ser_error==E_IP_BLOCKED)?0:1)!=0 )
					break;
				t->uac[i].request.dst.proto = t->uac[i].proxy->proto;
				/* update branch */
				if ( update_uac_dst( p_msg, &t->uac[i] )!=0)
					break;
			}while(1);

#ifdef USE_TCP
			tcp_no_new_conn = 0;
#endif

			if (ser_error) {
				shm_free(t->uac[i].request.buffer.s);
				t->uac[i].request.buffer.s = NULL;
				t->uac[i].request.buffer.len = 0;
				continue;
			}

			success_branch++;

			start_retr( &t->uac[i].request );
			set_kr(REQ_FWDED);

			/* successfully sent out -> run callbacks */
			if ( has_tran_tmcbs( t, TMCB_REQUEST_BUILT) ) {
				set_extra_tmcb_params( &t->uac[i].request.buffer,
					&t->uac[i].request.dst);
				run_trans_callbacks( TMCB_REQUEST_BUILT, t, p_msg,0,
					-p_msg->REQ_METHOD);
			}

		}
	}

	return (success_branch>0)?1:-1;
}
Esempio n. 11
0
File: t_fwd.c Progetto: OPSF/uClinux
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
}
Esempio n. 12
0
File: t_fwd.c Progetto: OPSF/uClinux
/* function returns:
 *       1 - forward successful
 *      -1 - error during forward
 */
int t_forward_nonack( struct cell *t, struct sip_msg* p_msg ,
                      struct proxy_l * proxy, int proto)
{
    str          backup_uri;
    int branch_ret, lowest_ret;
    str current_uri;
    branch_bm_t	added_branches;
    int first_branch;
    int i, q;
    struct cell *t_invite;
    int success_branch;
    int try_new;
    str dst_uri;

    /* make -Wall happy */
    current_uri.s=0;

    set_kr(REQ_FWDED);

    if (p_msg->REQ_METHOD==METHOD_CANCEL) {
        t_invite=t_lookupOriginalT(  p_msg );
        if (t_invite!=T_NULL_CELL) {
            e2e_cancel( p_msg, t, t_invite );
            UNREF(t_invite);
            return 1;
        }
    }

    /* backup current uri ... add_uac changes it */
    backup_uri = p_msg->new_uri;
    /* if no more specific error code is known, use this */
    lowest_ret=E_BUG;
    /* branches added */
    added_branches=0;
    /* branch to begin with */
    first_branch=t->nr_of_outgoings;

    /* on first-time forwarding, use current uri, later only what
       is in additional branches (which may be continuously refilled
    */
    if (first_branch==0) {
        try_new=1;
        branch_ret=add_uac( t, p_msg, GET_RURI(p_msg), GET_NEXT_HOP(p_msg), proxy, proto );
        if (branch_ret>=0)
            added_branches |= 1<<branch_ret;
        else
            lowest_ret=branch_ret;
    } else try_new=0;

    init_branch_iterator();
    while((current_uri.s=next_branch( &current_uri.len, &q, &dst_uri.s, &dst_uri.len))) {
        try_new++;
        branch_ret=add_uac( t, p_msg, &current_uri,
                            (dst_uri.len) ? (&dst_uri) : &current_uri,
                            proxy, proto);
        /* pick some of the errors in case things go wrong;
           note that picking lowest error is just as good as
           any other algorithm which picks any other negative
           branch result */
        if (branch_ret>=0)
            added_branches |= 1<<branch_ret;
        else
            lowest_ret=branch_ret;
    }
    /* consume processed branches */
    clear_branches();

    /* restore original URI */
    p_msg->new_uri=backup_uri;

    /* don't forget to clear all branches processed so far */

    /* things went wrong ... no new branch has been fwd-ed at all */
    if (added_branches==0) {
        if (try_new==0) {
            LOG(L_ERR, "ERROR: t_forward_nonack: no branched for forwarding\n");
            return -1;
        }
        LOG(L_ERR, "ERROR: t_forward_nonack: failure to add branches\n");
        return lowest_ret;
    }

    /* send them out now */
    success_branch=0;
    for (i=first_branch; i<t->nr_of_outgoings; i++) {
        if (added_branches & (1<<i)) {
            if (SEND_BUFFER( &t->uac[i].request)==-1) {
                LOG(L_ERR, "ERROR: t_forward_nonack: sending request failed\n");
                if (proxy) {
                    proxy->errors++;
                    proxy->ok=0;
                }
            } else {
                success_branch++;
            }
            start_retr( &t->uac[i].request );
        }
    }
    if (success_branch<=0) {
        ser_error=E_SEND;
        return -1;
    }
    return 1;
}
Esempio n. 13
0
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
}
Esempio n. 14
0
/*
 * Send a request using data from the dialog structure
 */
int t_uac(str* method, str* headers, str* body, dlg_t* dialog,
				transaction_cb cb, void* cbp,release_tmcb_param release_func)
{
	union sockaddr_union to_su, new_to_su;
	struct cell *new_cell;
	struct cell *backup_cell;
	struct retr_buf *request;
	static struct sip_msg *req;
	struct usr_avp **backup;
	char *buf, *buf1;
	int buf_len, buf_len1;
	int ret, flags, sflag_bk;
	int backup_route_type;
	int sip_msg_len;
	unsigned int hi;
	struct socket_info *new_send_sock;
	str h_to, h_from, h_cseq, h_callid;
	struct proxy_l *proxy, *new_proxy;
	unsigned short dst_changed;

	ret=-1;

	/*** added by dcm
	 * - needed by external ua to send a request within a dlg
	 */
	if(!dialog->hooks.next_hop && w_calculate_hooks(dialog)<0)
		goto error3;

	if(dialog->obp.s)
		dialog->hooks.next_hop = &dialog->obp;

	LM_DBG("next_hop=<%.*s>\n",dialog->hooks.next_hop->len,
			dialog->hooks.next_hop->s);

	/* calculate the socket corresponding to next hop */
	proxy = uri2proxy( dialog->hooks.next_hop,
		dialog->send_sock ? dialog->send_sock->proto : PROTO_NONE );
	if (proxy==0)  {
		ret=E_BAD_ADDRESS;
		goto error3;
	}
	/* use the first address */
	hostent2su( &to_su,
		&proxy->host, proxy->addr_idx, proxy->port ? proxy->port:SIP_PORT);

	/* check/discover the send socket */
	if (dialog->send_sock) {
		/* if already set, the protocol of send sock must have the 
		   the same type as the proto required by destination URI */
		if (proxy->proto != dialog->send_sock->proto)
			dialog->send_sock = NULL;
	}
	if (dialog->send_sock==NULL) {
		/* get the send socket */
		dialog->send_sock = get_send_socket( NULL/*msg*/, &to_su, proxy->proto);
		if (!dialog->send_sock) {
			LM_ERR("no corresponding socket for af %d\n", to_su.s.sa_family);
			ser_error = E_NO_SOCKET;
			goto error2;
		}
	}
	LM_DBG("sending socket is %.*s \n",
		dialog->send_sock->name.len,dialog->send_sock->name.s);


	/* ***** Create TRANSACTION and all related  ***** */
	new_cell = build_cell( NULL/*msg*/, 1/*full UAS clone*/);
	if (!new_cell) {
		ret=E_OUT_OF_MEM;
		LM_ERR("short of cell shmem\n");
		goto error2;
	}

	/* pass the transaction flags from dialog to transaction */
	new_cell->flags |= dialog->T_flags;

	/* add the callback the transaction for LOCAL_COMPLETED event */
	flags = TMCB_LOCAL_COMPLETED;
	/* Add also TMCB_LOCAL_RESPONSE_OUT if provisional replies are desired */
	if (pass_provisional_replies || pass_provisional(new_cell))
		flags |= TMCB_LOCAL_RESPONSE_OUT;
	if(cb && insert_tmcb(&(new_cell->tmcb_hl),flags,cb,cbp,release_func)!=1){
		ret=E_OUT_OF_MEM;
		LM_ERR("short of tmcb shmem\n");
		goto error2;
	}

	if (method->len==INVITE_LEN && memcmp(method->s, INVITE, INVITE_LEN)==0)
		new_cell->flags |= T_IS_INVITE_FLAG;
	new_cell->flags |= T_IS_LOCAL_FLAG;

	request = &new_cell->uac[0].request;
	if (dialog->forced_to_su.s.sa_family == AF_UNSPEC)
		request->dst.to = to_su;
	else
		request->dst.to = dialog->forced_to_su;
	request->dst.send_sock = dialog->send_sock;
	request->dst.proto = dialog->send_sock->proto;
	request->dst.proto_reserved1 = 0;

	hi=dlg2hash(dialog);
	LOCK_HASH(hi);
	insert_into_hash_table_unsafe(new_cell, hi);
	UNLOCK_HASH(hi);

	/* copy AVPs into transaction */
	new_cell->user_avps = dialog->avps;
	dialog->avps = NULL;


	/* ***** Create the message buffer ***** */
	buf = build_uac_req(method, headers, body, dialog, 0, new_cell, &buf_len);
	if (!buf) {
		LM_ERR("failed to build message\n");
		ret=E_OUT_OF_MEM;
		goto error1;
	}

	if (local_rlist.a) {
		LM_DBG("building sip_msg from buffer\n");
		req = buf_to_sip_msg(buf, buf_len, dialog);
		if (req==NULL) {
			LM_ERR("failed to build sip_msg from buffer\n");
		} else {
			/* set this transaction as active one */
			backup_cell = get_t();
			set_t( new_cell );
			/* set transaction AVP list */
			backup = set_avp_list( &new_cell->user_avps );
			/* backup script flags */
			sflag_bk = getsflags();
			/* disable parallel forking */
			set_dset_state( 0 /*disable*/);

			/* run the route */
			swap_route_type( backup_route_type, LOCAL_ROUTE);
			run_top_route( local_rlist.a, req);
			set_route_type( backup_route_type );

			/* transfer current message context back to t */
			new_cell->uac[0].br_flags = getb0flags(req);
			/* restore the prevoius active transaction */
			set_t( backup_cell );

			set_dset_state( 1 /*enable*/);
			setsflagsval(sflag_bk);
			set_avp_list( backup );

			/* check for changes - if none, do not regenerate the buffer */
			dst_changed = 1;
			if (req->new_uri.s || req->force_send_socket!=dialog->send_sock ||
			req->dst_uri.len != dialog->hooks.next_hop->len ||
			memcmp(req->dst_uri.s,dialog->hooks.next_hop->s,req->dst_uri.len) ||
			(dst_changed=0)==0 || req->add_rm || req->body_lumps){

				new_send_sock = NULL;
				/* do we also need to change the destination? */
				if (dst_changed) {
					/* calculate the socket corresponding to next hop */
					new_proxy = uri2proxy(
						req->dst_uri.s ? &(req->dst_uri) : &req->new_uri,
						PROTO_NONE );
					if (new_proxy==0)
						goto abort_update;
					/* use the first address */
					hostent2su( &new_to_su,
						&new_proxy->host, new_proxy->addr_idx,
						new_proxy->port ? new_proxy->port:SIP_PORT);
					/* get the send socket */
					new_send_sock = get_send_socket( req, &new_to_su,
						new_proxy->proto);
					if (!new_send_sock) {
						free_proxy( new_proxy );
						pkg_free( new_proxy );
						LM_ERR("no socket found for the new destination\n");
						goto abort_update;
					}
				}

				/* if interface change, we need to re-build the via */
				if (new_send_sock && new_send_sock != dialog->send_sock) {
					LM_DBG("Interface change in local route -> "
						"rebuilding via\n");
					if (!del_lump(req,req->h_via1->name.s - req->buf,
					req->h_via1->len,0)) {
						LM_ERR("Failed to remove initial via \n");
						goto abort_update;
					}

					memcpy(req->add_to_branch_s,req->via1->branch->value.s,
						req->via1->branch->value.len);
					req->add_to_branch_len = req->via1->branch->value.len;

					/* update also info about new destination and send sock */
					dialog->send_sock = new_send_sock;
					free_proxy( proxy );
					pkg_free( proxy );
					proxy = new_proxy;
					request->dst.send_sock = new_send_sock;
					request->dst.proto = new_send_sock->proto;
					request->dst.proto_reserved1 = 0;

					/* build the shm buffer now */
					set_init_lump_flags(LUMPFLAG_BRANCH);
					buf1 = build_req_buf_from_sip_req(req,
						(unsigned int*)&buf_len1,
						new_send_sock, new_send_sock->proto,
						MSG_TRANS_SHM_FLAG);
					reset_init_lump_flags();
					del_flaged_lumps( &req->add_rm, LUMPFLAG_BRANCH);

				} else {

					LM_DBG("Change in local route -> rebuilding buffer\n");
					/* build the shm buffer now */
					buf1 = build_req_buf_from_sip_req(req,
						(unsigned int*)&buf_len1,
						dialog->send_sock, dialog->send_sock->proto,
						MSG_TRANS_SHM_FLAG|MSG_TRANS_NOVIA_FLAG);
					/* now as it used, hide the original VIA header */
					del_lump(req,req->h_via1->name.s - req->buf,
						req->h_via1->len, 0);

				}

				if (!buf1) {
					LM_ERR("no more shm mem\n");
					/* keep original buffer */
					goto abort_update;
				}
				/* update shortcuts */
				if(!req->add_rm && !req->new_uri.s) {
					/* headers are not affected, simply tranlate */
					new_cell->from.s = new_cell->from.s - buf + buf1;
					new_cell->to.s = new_cell->to.s - buf + buf1;
					new_cell->callid.s = new_cell->callid.s - buf + buf1;
					new_cell->cseq_n.s = new_cell->cseq_n.s - buf + buf1;
				} else {
					/* use heavy artilery :D */
					if (extract_ftc_hdrs( buf1, buf_len1, &h_from, &h_to,
					&h_cseq, &h_callid)!=0 ) {
						LM_ERR("failed to update shortcut pointers\n");
						shm_free(buf1);
						goto abort_update;
					}
					new_cell->from = h_from;
					new_cell->to = h_to;
					new_cell->callid = h_callid;
					new_cell->cseq_n = h_cseq;
				}
				/* here we rely on how build_uac_req()
				   builds the first line */
				new_cell->uac[0].uri.s = buf1 +
					req->first_line.u.request.method.len + 1;
				new_cell->uac[0].uri.len = GET_RURI(req)->len;

				/* update also info about new destination and send sock */
				if (new_send_sock)
					request->dst.to = new_to_su;

				shm_free(buf);
				buf = buf1;
				buf_len = buf_len1;
				/* use new buffer */
			} else {
				/* no changes over the message, buffer is already generated,
				   just hide the original VIA for potential further branches */
				del_lump(req,req->h_via1->name.s-req->buf,req->h_via1->len,0);
			}
abort_update:
			/* save the SIP message into transaction */
			new_cell->uas.request = sip_msg_cloner( req, &sip_msg_len, 1);
			if (new_cell->uas.request==NULL) {
				/* reset any T triggering */
				new_cell->on_negative = 0;
				new_cell->on_reply = 0;
			} else {
				new_cell->uas.end_request=
					((char*)new_cell->uas.request)+sip_msg_len;
			}
			/* no parallel support in UAC transactions */
			new_cell->on_branch = 0;
			free_sip_msg(req);
		}
	}

	/* for DNS based failover, copy the DNS proxy into transaction */
	if (!disable_dns_failover) {
		new_cell->uac[0].proxy = shm_clone_proxy( proxy, 1/*do_free*/);
		if (new_cell->uac[0].proxy==NULL)
			LM_ERR("failed to store DNS info -> no DNS based failover\n");
	}

	new_cell->method.s = buf;
	new_cell->method.len = method->len;

	request->buffer.s = buf;
	request->buffer.len = buf_len;
	new_cell->nr_of_outgoings++;

	if(last_localT) {
		*last_localT = new_cell;
		REF_UNSAFE(new_cell);
	}

	if (SEND_BUFFER(request) == -1) {
		LM_ERR("attempt to send to '%.*s' failed\n",
			dialog->hooks.next_hop->len,
			dialog->hooks.next_hop->s);
	}

	if (method->len==ACK_LEN && memcmp(method->s, ACK, ACK_LEN)==0 ) {
		t_release_transaction(new_cell);
	} else {
		start_retr(request);
	}

	free_proxy( proxy );
	pkg_free( proxy );

	return 1;

error1:
	LOCK_HASH(hi);
	remove_from_hash_table_unsafe(new_cell);
	UNLOCK_HASH(hi);
	free_cell(new_cell);
error2:
	free_proxy( proxy );
	pkg_free( proxy );
error3:
	return ret;
}
Esempio n. 15
0
/*
 * Send a request using data from the dialog structure
 */
int t_uac(str* method, str* headers, str* body, dlg_t* dialog,
												transaction_cb cb, void* cbp)
{
	struct socket_info* send_sock;
	union sockaddr_union to_su;
	struct cell *new_cell;
	struct retr_buf *request;
	char* buf;
	int buf_len;
	int ret;
	unsigned int hi;

	ret=-1;
	
	/*** added by dcm 
	 * - needed by external ua to send a request within a dlg
	 */
	if(!dialog->hooks.next_hop && w_calculate_hooks(dialog)<0)
		goto error2;

	DBG("DEBUG:tm:t_uac: next_hop=<%.*s>\n",dialog->hooks.next_hop->len,
			dialog->hooks.next_hop->s);
	/* it's a new message, so we will take the default socket */
	send_sock = uri2sock(0, dialog->hooks.next_hop, &to_su, PROTO_NONE);
	if (!send_sock) {
		ret=ser_error;
		LOG(L_ERR, "t_uac: no socket found\n");
		goto error2;
	}	

	new_cell = build_cell(0); 
	if (!new_cell) {
		ret=E_OUT_OF_MEM;
		LOG(L_ERR, "t_uac: short of cell shmem\n");
		goto error2;
	}

	/* better reset avp list now - anyhow, it's useless from
	 * this point (bogdan) */
	reset_avps();

	/* add the callback the the transaction for LOCAL_COMPLETED event */
	if(cb && insert_tmcb(&(new_cell->tmcb_hl),TMCB_LOCAL_COMPLETED,cb,cbp)!=1){
		ret=E_OUT_OF_MEM;
		LOG(L_ERR, "t_uac: short of tmcb shmem\n");
		goto error2;
	}

	if (method->len==INVITE_LEN && memcmp(method->s, INVITE, INVITE_LEN)==0)
		new_cell->flags |= T_IS_INVITE_FLAG;
	new_cell->flags |= T_IS_LOCAL_FLAG;
	set_kr(REQ_FWDED);

	request = &new_cell->uac[0].request;
	request->dst.to = to_su;
	request->dst.send_sock = send_sock;
	request->dst.proto = send_sock->proto;
	request->dst.proto_reserved1 = 0;

	hi=dlg2hash(dialog);
	LOCK_HASH(hi);
	insert_into_hash_table_unsafe(new_cell, hi);
	UNLOCK_HASH(hi);

	buf = build_uac_req(method, headers, body, dialog, 0, new_cell,
		&buf_len, send_sock);
	if (!buf) {
		LOG(L_ERR, "t_uac: Error while building message\n");
		ret=E_OUT_OF_MEM;
		goto error1;
	}

	new_cell->method.s = buf;
	new_cell->method.len = method->len;

	request->buffer = buf;
	request->buffer_len = buf_len;
	new_cell->nr_of_outgoings++;
	
	if (SEND_BUFFER(request) == -1) {
		LOG(L_ERR, "t_uac: Attempt to send to '%.*s' failed\n", 
			dialog->hooks.next_hop->len,
			dialog->hooks.next_hop->s);
	}
	
	start_retr(request);
	return 1;

 error1:
	LOCK_HASH(hi);
	remove_from_hash_table_unsafe(new_cell);
	UNLOCK_HASH(hi);
	free_cell(new_cell);
error2:
	return ret;
}
Esempio n. 16
0
/* function returns:
 *       1 - forward successful
 *      -1 - error during forward
 */
int t_forward_nonack( struct cell *t, struct sip_msg* p_msg , 
	struct proxy_l * proxy, int proto)
{
	str backup_uri;
	str backup_dst;
	int branch_ret, lowest_ret;
	str current_uri;
	branch_bm_t  added_branches;
	int i, q;
	struct cell *t_invite;
	int success_branch;
	int try_new;
	str dst_uri;
	struct socket_info *bk_sock;
	int rurib_flags;
	int br_flags;
	int idx;

	/* make -Wall happy */
	current_uri.s=0;

	set_kr(REQ_FWDED);

	if (p_msg->REQ_METHOD==METHOD_CANCEL) {
		t_invite=t_lookupOriginalT(  p_msg );
		if (t_invite!=T_NULL_CELL) {
			t_invite->flags |= T_WAS_CANCELLED_FLAG;
			e2e_cancel( p_msg, t, t_invite );
			UNREF(t_invite);
			return 1;
		}
	}

	/* do not forward requests which were already cancelled*/
	if (was_cancelled(t)) {
		LOG(L_ERR,"ERROR:tm:t_forward_nonack: discarding fwd for "
				"a cancelled transaction\n");
		return -1;
	}

	/* backup current uri, sock and flags ... add_uac changes it */
	backup_uri = p_msg->new_uri;
	backup_dst = p_msg->dst_uri;
	bk_sock = p_msg->force_send_socket;
	rurib_flags = p_msg->flags&(~gflags_mask);

	/* if no more specific error code is known, use this */
	lowest_ret=E_BUG;
	/* branches added */
	added_branches=0;
	/* branch to begin with */
	t->first_branch=t->nr_of_outgoings;

	/* on first-time forwarding, use current uri, later only what
	   is in additional branches (which may be continuously refilled)
	*/
	if (t->first_branch==0) {
		try_new=1;
		current_uri = *GET_RURI(p_msg);
		branch_ret = add_uac( t, p_msg, &current_uri, &backup_dst,
			proxy, proto );
		if (branch_ret>=0)
			added_branches |= 1<<branch_ret;
		else
			lowest_ret=branch_ret;
	} else try_new=0;

	for( idx=0; (current_uri.s=get_branch( idx, &current_uri.len, &q,
	&dst_uri, &br_flags, &p_msg->force_send_socket))!=0 ; idx++ ) {
		try_new++;
		p_msg->flags = (p_msg->flags&gflags_mask) | br_flags;
		branch_ret=add_uac( t, p_msg, &current_uri, &dst_uri, proxy, proto);
		/* pick some of the errors in case things go wrong;
		   note that picking lowest error is just as good as
		   any other algorithm which picks any other negative
		   branch result */
		if (branch_ret>=0) 
			added_branches |= 1<<branch_ret;
		else
			lowest_ret=branch_ret;
	}
	/* consume processed branches */
	clear_branches();

	/* restore original stuff */
	p_msg->new_uri=backup_uri;
	p_msg->parsed_uri_ok = 0;/* just to be sure; add_uac may parse other uris*/
	p_msg->dst_uri = backup_dst;
	p_msg->force_send_socket = bk_sock;
	/* update on_branch, if modified */
	t->on_branch = get_on_branch();
	/* set flags */
	p_msg->flags = (p_msg->flags&gflags_mask)|rurib_flags;
	t->uas.request->flags = p_msg->flags&gflags_mask;

	/* don't forget to clear all branches processed so far */

	/* things went wrong ... no new branch has been fwd-ed at all */
	if (added_branches==0) {
		if (try_new==0) {
			LOG(L_ERR, "ERROR:tm:t_forward_nonack: no branch for "
				"forwarding\n");
			return -1;
		}
		LOG(L_ERR, "ERROR:tm:t_forward_nonack: failure to add branches\n");
		return lowest_ret;
	}

	/* send them out now */
	success_branch=0;
	for (i=t->first_branch; i<t->nr_of_outgoings; i++) {
		if (added_branches & (1<<i)) {
			if (SEND_BUFFER( &t->uac[i].request)==-1) {
				LOG(L_ERR, "ERROR:tm:t_forward_nonack: sending request "
					"failed\n");
				if (proxy) { proxy->errors++; proxy->ok=0; }
			} else {
				success_branch++;
			}
			start_retr( &t->uac[i].request );
		}
	}
	if (success_branch<=0) {
		ser_error=E_SEND;
		return -1;
	}
	return 1;
}
Esempio n. 17
0
/*
 * Send a request using data from the dialog structure
 */
int t_uac(str* method, str* headers, str* body, dlg_t* dialog,
				transaction_cb cb, void* cbp,release_tmcb_param release_func)
{
	union sockaddr_union to_su;
	struct cell *new_cell;
	struct retr_buf *request;
	static struct sip_msg *req;
	struct usr_avp **backup;
	char *buf, *buf1;
	int buf_len, buf_len1;
	int ret, flags, sflag_bk;
	int backup_route_type;
	unsigned int hi;
	struct socket_info* send_sock;

	ret=-1;
	
	/*** added by dcm 
	 * - needed by external ua to send a request within a dlg
	 */
	if(!dialog->hooks.next_hop && w_calculate_hooks(dialog)<0)
		goto error2;

	if(dialog->obp.s)
		dialog->hooks.next_hop = &dialog->obp;

	LM_DBG("next_hop=<%.*s>\n",dialog->hooks.next_hop->len,
			dialog->hooks.next_hop->s);

	/* calculate the socket corresponding to next hop */
	send_sock = uri2sock(0, dialog->hooks.next_hop, &to_su,
			PROTO_NONE);
	if (send_sock==0) {
		ret=ser_error;
		LM_ERR("no socket found\n");
		goto error2;
	}
	/* if a send socket defined verify if the same protocol */
	if(dialog->send_sock) {
		if(send_sock->proto != dialog->send_sock->proto)
		{
			dialog->send_sock = send_sock;
		}
	}
	else
	{
		dialog->send_sock = send_sock;
	}

	new_cell = build_cell(0);
	if (!new_cell) {
		ret=E_OUT_OF_MEM;
		LM_ERR("short of cell shmem\n");
		goto error2;
	}

	/* pass the transaction flags from dialog to transaction */
	new_cell->flags |= dialog->T_flags;

	/* add the callback the transaction for LOCAL_COMPLETED event */
	flags = TMCB_LOCAL_COMPLETED;
	/* Add also TMCB_LOCAL_RESPONSE_OUT if provisional replies are desired */
	if (pass_provisional_replies || pass_provisional(new_cell))
		flags |= TMCB_LOCAL_RESPONSE_OUT;
	if(cb && insert_tmcb(&(new_cell->tmcb_hl),flags,cb,cbp,release_func)!=1){
		ret=E_OUT_OF_MEM;
		LM_ERR("short of tmcb shmem\n");
		goto error2;
	}

	if (method->len==INVITE_LEN && memcmp(method->s, INVITE, INVITE_LEN)==0)
		new_cell->flags |= T_IS_INVITE_FLAG;
	new_cell->flags |= T_IS_LOCAL_FLAG;

	request = &new_cell->uac[0].request;
	request->dst.to = to_su;
	request->dst.send_sock = dialog->send_sock;
	request->dst.proto = dialog->send_sock->proto;
	request->dst.proto_reserved1 = 0;

	hi=dlg2hash(dialog);
	LOCK_HASH(hi);
	insert_into_hash_table_unsafe(new_cell, hi);
	UNLOCK_HASH(hi);

	buf = build_uac_req(method, headers, body, dialog, 0, new_cell, &buf_len);
	if (!buf) {
		LM_ERR("failed to build message\n");
		ret=E_OUT_OF_MEM;
		goto error1;
	}

	if (local_rlist.a) {
		LM_DBG("building sip_msg from buffer\n");
		req = buf_to_sip_msg(buf, buf_len, dialog);
		if (req==NULL) {
			LM_ERR("failed to build sip_msg from buffer");
		} else {
			/* set transaction AVP list */
			backup = set_avp_list( &new_cell->user_avps );
			/* backup script flags */
			sflag_bk = getsflags();
			/* disable parallel forking */
			set_dset_state( 0 /*disable*/);

			/* transfer current message context back to t */
			new_cell->uac[0].br_flags = req->flags;

			/* run the route */
			swap_route_type( backup_route_type, LOCAL_ROUTE);
			run_top_route( local_rlist.a, req);
			set_route_type( backup_route_type );

			set_dset_state( 1 /*enable*/);
			setsflagsval(sflag_bk);
			set_avp_list( backup );

			/* check for changes - if none, do not regenerate the buffer 
			 * we ignore any change on RURI and DSTURI and they should not
			 * be changed  -bogdan */
			if (req->new_uri.s)
				{ pkg_free(req->new_uri.s); req->new_uri.s=0; req->new_uri.len=0; }
			if (req->dst_uri.s)
				{ pkg_free(req->dst_uri.s); req->dst_uri.s=0; req->dst_uri.len=0; }

			if (req->add_rm || req->body_lumps) {
				LM_DBG("re-building the buffer (sip_msg changed) - lumps are"
					"%p %p\n",req->add_rm, req->body_lumps);
				/* build the shm buffer now */
				buf1 = build_req_buf_from_sip_req(req,(unsigned int*)&buf_len1,
					dialog->send_sock, dialog->send_sock->proto,
					MSG_TRANS_SHM_FLAG|MSG_TRANS_NOVIA_FLAG );
				if (!buf1) {
					LM_ERR("no more shm mem\n"); 
					/* keep original buffer */
				} else {
					shm_free(buf);
					buf = buf1;
					buf_len = buf_len1;
					/* use new buffer */
				}
			}
			free_sip_msg(req);
		}
	}

	new_cell->method.s = buf;
	new_cell->method.len = method->len;

	request->buffer.s = buf;
	request->buffer.len = buf_len;
	new_cell->nr_of_outgoings++;

	if(last_localT)
	{
		*last_localT = new_cell;
		REF_UNSAFE(new_cell);
	}

	if (SEND_BUFFER(request) == -1) {
		LM_ERR("attempt to send to '%.*s' failed\n", 
			dialog->hooks.next_hop->len,
			dialog->hooks.next_hop->s);
	}

	if (method->len==ACK_LEN && memcmp(method->s, ACK, ACK_LEN)==0 ) {
		t_release_transaction(new_cell);
	} else {
		start_retr(request);
	}


	return 1;

error1:
	LOCK_HASH(hi);
	remove_from_hash_table_unsafe(new_cell);
	UNLOCK_HASH(hi);
	free_cell(new_cell);
error2:
	return ret;
}
Esempio n. 18
0
/*
 * Send a request using data from the dialog structure
 */
int t_uac(str* method, str* headers, str* body, dlg_t* dialog, transaction_cb cb, void* cbp)
{
	struct socket_info* send_sock;
	union sockaddr_union to_su;
	struct cell *new_cell;
	struct retr_buf *request;
	char* buf;
	int buf_len;
	int ret;
	unsigned int hi;

	ret=-1;
	
	/*** added by dcm 
	 * - needed by external ua to send a request within a dlg
	 */
	if(!dialog->hooks.next_hop && w_calculate_hooks(dialog)<0)
		goto error2;

	send_sock = uri2sock(dialog->hooks.next_hop, &to_su, PROTO_NONE);
	if (!send_sock) {
		ret=ser_error;
		LOG(L_ERR, "t_uac: no socket found\n");
		goto error2;
	}	

	new_cell = build_cell(0); 
	if (!new_cell) {
		ret=E_OUT_OF_MEM;
		LOG(L_ERR, "t_uac: short of cell shmem\n");
		goto error2;
	}

	new_cell->completion_cb = cb;
	new_cell->cbp = cbp;
	
	     /* cbp is installed -- tell error handling bellow not to free it */
	cbp = 0;

	new_cell->is_invite = method->len == INVITE_LEN && memcmp(method->s, INVITE, INVITE_LEN) == 0;
	new_cell->local= 1;
	set_kr(REQ_FWDED);
	
	request = &new_cell->uac[0].request;
	request->dst.to = to_su;
	request->dst.send_sock = send_sock;
	request->dst.proto = send_sock->proto;
	request->dst.proto_reserved1 = 0;

	hi=dlg2hash(dialog);
	LOCK_HASH(hi);
	insert_into_hash_table_unsafe(new_cell, hi);
	UNLOCK_HASH(hi);

	buf = build_uac_req(method, headers, body, dialog, 0, new_cell, &buf_len, send_sock);
	if (!buf) {
		LOG(L_ERR, "t_uac: Error while building message\n");
		ret=E_OUT_OF_MEM;
		goto error1;
	}

	new_cell->method.s = buf;
	new_cell->method.len = method->len;

	request->buffer = buf;
	request->buffer_len = buf_len;
	new_cell->nr_of_outgoings++;
	
	if (SEND_BUFFER(request) == -1) {
		LOG(L_ERR, "t_uac: Attempt to send to '%.*s' failed\n", 
		    dialog->hooks.next_hop->len,
		    dialog->hooks.next_hop->s
		    );
	}
	
	start_retr(request);
	return 1;

 error1:
	LOCK_HASH(hi);
	remove_from_hash_table_unsafe(new_cell);
	UNLOCK_HASH(hi);
	free_cell(new_cell);

 error2:
	     /* if we did not install cbp, release it now */
	if (cbp) shm_free(cbp);
	return ret;
}
Esempio n. 19
0
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 );
}