示例#1
0
文件: timer.c 项目: btriller/kamailio
/* generate a fake reply
 * it assumes the REPLY_LOCK is already held and returns unlocked */
static void fake_reply(struct cell *t, int branch, int code)
{
	struct cancel_info cancel_data;
	short do_cancel_branch;
	enum rps reply_status;

	init_cancel_info(&cancel_data);
	do_cancel_branch = is_invite(t) && prepare_cancel_branch(t, branch, 0);
	/* mark branch as canceled */
	t->uac[branch].request.flags |= F_RB_CANCELED;
	t->uac[branch].request.flags |= F_RB_RELAYREPLY;
	if(is_local(t)) {
		reply_status = local_reply(t, FAKED_REPLY, branch, code, &cancel_data);
	} else {
		/* rely reply, but don't put on wait, we still need t
		 * to send the cancels */
		reply_status =
				relay_reply(t, FAKED_REPLY, branch, code, &cancel_data, 0);
	}
/* now when out-of-lock do the cancel I/O */
#ifdef CANCEL_REASON_SUPPORT
	if(do_cancel_branch)
		cancel_branch(t, branch, &cancel_data.reason, 0);
#else /* CANCEL_REASON_SUPPORT */
	if(do_cancel_branch)
		cancel_branch(t, branch, 0);
#endif /* CANCEL_REASON_SUPPORT */
	/* it's cleaned up on error; if no error occurred and transaction
	   completed regularly, I have to clean-up myself
	*/
	if(reply_status == RPS_COMPLETED)
		put_on_wait(t);
}
示例#2
0
static void fake_reply(struct cell *t, int branch, int code )
{
	branch_bm_t cancel_bitmap;
	short do_cancel_branch;
	enum rps reply_status;

	do_cancel_branch = is_invite(t) && should_cancel_branch(t, branch);

	cancel_bitmap=do_cancel_branch ? 1<<branch : 0;
	if ( is_local(t) ) {
		reply_status=local_reply( t, FAKED_REPLY, branch,
					  code, &cancel_bitmap );
		if (reply_status==RPS_COMPLETED) {
			put_on_wait(t);
		}
	} else {
		reply_status=relay_reply( t, FAKED_REPLY, branch, code,
			&cancel_bitmap );
	}
}
示例#3
0
文件: t_reply.c 项目: iamroger/voip
/*  This function is called whenever a reply for our module is received; 
  * we need to register  this function on module initialization;
  *  Returns :   0 - core router stops
  *              1 - core router relay statelessly
  */
int reply_received( struct sip_msg  *p_msg )
{
	int msg_status;
	int last_uac_status;
	int branch;
	int reply_status;
	utime_t timer;
	/* has the transaction completed now and we need to clean-up? */
	branch_bm_t cancel_bitmap;
	struct ua_client *uac;
	struct cell *t;
	struct usr_avp **backup_list;
	unsigned int has_reply_route;

	set_t(T_UNDEFINED);

	/* make sure we know the associated transaction ... */
	if (t_check(p_msg, &branch ) == -1) goto not_found;

	/*... if there is none, tell the core router to fwd statelessly */
	t = get_t();
	if ((t == 0) || (t == T_UNDEFINED)) goto not_found;

	cancel_bitmap=0;
	msg_status=p_msg->REPLY_STATUS;

	uac=&t->uac[branch];
	LM_DBG("org. status uas=%d, uac[%d]=%d local=%d is_invite=%d)\n",
		t->uas.status, branch, uac->last_received, 
		is_local(t), is_invite(t));
	last_uac_status=uac->last_received;
	if_update_stat( tm_enable_stats, tm_rcv_rpls , 1);

	/* it's a cancel which is not e2e ? */
	if ( get_cseq(p_msg)->method_id==METHOD_CANCEL && is_invite(t) ) {
		/* ... then just stop timers */
		reset_timer( &uac->local_cancel.retr_timer);
		if ( msg_status >= 200 ) {
				reset_timer( &uac->local_cancel.fr_timer);
		}
		LM_DBG("reply to local CANCEL processed\n");
		goto done;
	}

	/* *** stop timers *** */
	/* stop retransmission */
	reset_timer(&uac->request.retr_timer);

	/* stop final response timer only if I got a final response */
	if ( msg_status >= 200 ) {
		reset_timer( &uac->request.fr_timer);
	}

	/* acknowledge negative INVITE replies (do it before detailed
	 * on_reply processing, which may take very long, like if it
	 * is attempted to establish a TCP connection to a fail-over dst */
	if (is_invite(t) && ((msg_status >= 300) ||
	(is_local(t) && !no_autoack(t) && msg_status >= 200) )) {
		if (send_ack(p_msg, t, branch)!=0)
			LM_ERR("failed to send ACK (local=%s)\n", is_local(t)?"yes":"no");
	}

	_tm_branch_index = branch;

	/* processing of on_reply block */
	has_reply_route = (t->on_reply) || (t->uac[branch].on_reply);
	if (has_reply_route) {
		if (onreply_avp_mode) {
			/* lock the reply*/
			LOCK_REPLIES( t );
			/* set the as avp_list the one from transaction */
			backup_list = set_avp_list(&t->user_avps);
		} else {
			backup_list = 0;
		}
		/* transfer transaction flag to branch context */
		p_msg->flags = t->uas.request->flags;
		setb0flags(t->uac[branch].br_flags);
		/* run block - first per branch and then global one */
		if ( t->uac[branch].on_reply &&
		(run_top_route(onreply_rlist[t->uac[branch].on_reply].a,p_msg)
		&ACT_FL_DROP) && (msg_status<200) ) {
			if (onreply_avp_mode) {
				UNLOCK_REPLIES( t );
				set_avp_list( backup_list );
			}
			LM_DBG("dropping provisional reply %d\n", msg_status);
			goto done;
		}
		if ( t->on_reply && (run_top_route(onreply_rlist[t->on_reply].a,p_msg)
		&ACT_FL_DROP) && (msg_status<200) ) {
			if (onreply_avp_mode) {
				UNLOCK_REPLIES( t );
				set_avp_list( backup_list );
			}
			LM_DBG("dropping provisional reply %d\n", msg_status);
			goto done;
		}
		/* transfer current message context back to t */
		t->uac[branch].br_flags = getb0flags();
		t->uas.request->flags = p_msg->flags;
		if (onreply_avp_mode)
			/* restore original avp list */
			set_avp_list( backup_list );
	}

	if (!onreply_avp_mode || !has_reply_route)
		/* lock the reply*/
		LOCK_REPLIES( t );

	/* mark that the UAC received replies */
	uac->flags |= T_UAC_HAS_RECV_REPLY;

	/* we fire a cancel on spot if (a) branch is marked "to be canceled" or (b)
	 * the whole transaction was canceled (received cancel) and no cancel sent
	 * yet on this branch; and of course, only if a provisional reply :) */
	if (t->uac[branch].flags&T_UAC_TO_CANCEL_FLAG ||
	((t->flags&T_WAS_CANCELLED_FLAG) && !t->uac[branch].local_cancel.buffer.s)) {
		if ( msg_status < 200 )
			/* reply for an UAC with a pending cancel -> do cancel now */
			cancel_branch(t, branch);
		/* reset flag */
		t->uac[branch].flags &= ~(T_UAC_TO_CANCEL_FLAG);
	}

	if (is_local(t)) {
		reply_status = local_reply(t,p_msg, branch,msg_status,&cancel_bitmap);
		if (reply_status == RPS_COMPLETED) {
			cleanup_uac_timers(t);
			if (is_invite(t)) cancel_uacs(t, cancel_bitmap);
			/* There is no need to call set_final_timer because we know
			 * that the transaction is local */
			put_on_wait(t);
		}
	} else {
		reply_status = relay_reply(t,p_msg,branch,msg_status,&cancel_bitmap);
		/* clean-up the transaction when transaction completed */
		if (reply_status == RPS_COMPLETED) {
			/* no more UAC FR/RETR (if I received a 2xx, there may
			 * be still pending branches ...
			 */
			cleanup_uac_timers(t);
			if (is_invite(t)) cancel_uacs(t, cancel_bitmap);
			/* FR for negative INVITES, WAIT anything else */
			/* set_final_timer(t); */
		}
	}
	
	if (reply_status!=RPS_PROVISIONAL)
		goto done;
	
	/* update FR/RETR timers on provisional replies */
	if (msg_status < 200 && (restart_fr_on_each_reply ||
	((last_uac_status<msg_status) &&
	((msg_status >= 180) || (last_uac_status == 0)))
	) ) { /* provisional now */
		if (is_invite(t)) {
			/* invite: change FR to longer FR_INV, do not
			 * attempt to restart retransmission any more
			 */
			backup_list = set_avp_list(&t->user_avps);
			if (!fr_inv_avp2timer(&timer)) {
				LM_DBG("FR_INV_TIMER = %lld\n", timer);
				set_timer(&uac->request.fr_timer,
					FR_INV_TIMER_LIST, &timer);
			} else {
				set_timer(& uac->request.fr_timer, FR_INV_TIMER_LIST, 0);
			}
			set_avp_list(backup_list);
		} else {
			/* non-invite: restart retransmissions (slow now) */
			uac->request.retr_list = RT_T2;
			set_timer(&uac->request.retr_timer, RT_T2, 0);
		}
	} /* provisional replies */
	
done:
	/* we are done with the transaction, so unref it - the reference
	 * was incremented by t_check() function -bogdan*/
	t_unref(p_msg);
	/* don't try to relay statelessly neither on success
	 * (we forwarded statefully) nor on error; on troubles, 
	 * simply do nothing; that will make the other party to 
	 * retransmit; hopefuly, we'll then be better off 
	 */
	_tm_branch_index = 0;
	return 0;
not_found:
	set_t(T_UNDEFINED);
	return 1;
}
示例#4
0
/* Continues the SIP request processing previously saved by
 * t_suspend(). The script does not continue from the same
 * point, but a separate route block is executed instead.
 *
 * Return value:
 * 	0  - success
 * 	<0 - failure
 */
int t_continue(unsigned int hash_index, unsigned int label,
		struct action *route)
{
	struct cell	*t;
	struct sip_msg	faked_req;
	struct cancel_info cancel_data;
	int	branch;
	struct ua_client *uac =NULL;
	int	ret;
	int cb_type;
	int msg_status;
	int last_uac_status;
	int reply_status;
	int do_put_on_wait;
	struct hdr_field *hdr, *prev = 0, *tmp = 0;

	if (t_lookup_ident(&t, hash_index, label) < 0) {
		LM_ERR("transaction not found\n");
		return -1;
	}

	if (!(t->flags & T_ASYNC_SUSPENDED)) {
		LM_WARN("transaction is not suspended [%u:%u]\n", hash_index, label);
		return -2;
	}

	if (t->flags & T_CANCELED) {
		t->flags &= ~T_ASYNC_SUSPENDED;
		/* The transaction has already been canceled,
		 * needless to continue */
		UNREF(t); /* t_unref would kill the transaction */
		/* reset T as we have no working T anymore */
		set_t(T_UNDEFINED, T_BR_UNDEFINED);
		return 1;
	}

	/* The transaction has to be locked to protect it
	 * form calling t_continue() multiple times simultaneously */
	LOCK_ASYNC_CONTINUE(t);

	t->flags |= T_ASYNC_CONTINUE;   /* we can now know anywhere in kamailio
					 * that we are executing post a suspend */

	/* which route block type were we in when we were suspended */
	cb_type =  FAILURE_CB_TYPE;;
	switch (t->async_backup.backup_route) {
		case REQUEST_ROUTE:
			cb_type = FAILURE_CB_TYPE;
			break;
		case FAILURE_ROUTE:
			cb_type = FAILURE_CB_TYPE;
			break;
		case TM_ONREPLY_ROUTE:
			cb_type = ONREPLY_CB_TYPE;
			break;
		case BRANCH_ROUTE:
			cb_type = FAILURE_CB_TYPE;
			break;
	}

	if(t->async_backup.backup_route != TM_ONREPLY_ROUTE){
		branch = t->async_backup.blind_uac;	/* get the branch of the blind UAC setup 
			* during suspend */
		if (branch >= 0) {
			stop_rb_timers(&t->uac[branch].request);
 
			if (t->uac[branch].last_received != 0) {
				/* Either t_continue() has already been
				* called or the branch has already timed out.
				* Needless to continue. */
				t->flags &= ~T_ASYNC_SUSPENDED;
				UNLOCK_ASYNC_CONTINUE(t);
				UNREF(t); /* t_unref would kill the transaction */
				return 1;
			}

			/* Set last_received to something >= 200,
			 * the actual value does not matter, the branch
			 * will never be picked up for response forwarding.
			 * If last_received is lower than 200,
			 * then the branch may tried to be cancelled later,
			 * for example when t_reply() is called from
			 * a failure route => deadlock, because both
			 * of them need the reply lock to be held. */
			t->uac[branch].last_received=500;
			uac = &t->uac[branch];
		}
		/* else
			Not a huge problem, fr timer will fire, but CANCEL
			will not be sent. last_received will be set to 408. */

		/* We should not reset kr here to 0 as it's quite possible before continuing the dev. has correctly set the
		 * kr by, for example, sending a transactional reply in code - resetting here will cause a dirty log message
		 * "WARNING: script writer didn't release transaction" to appear in log files. TODO: maybe we need to add 
		 * a special kr for async?
		 * reset_kr();
		 */

		/* fake the request and the environment, like in failure_route */
		if (!fake_req(&faked_req, t->uas.request, 0 /* extra flags */, uac)) {
			LM_ERR("building fake_req failed\n");
			ret = -1;
			goto kill_trans;
		}
		faked_env( t, &faked_req, 1);

		/* execute the pre/post -script callbacks based on original route block */
		if (exec_pre_script_cb(&faked_req, cb_type)>0) {
			if (run_top_route(route, &faked_req, 0)<0)
				LM_ERR("failure inside run_top_route\n");
			exec_post_script_cb(&faked_req, cb_type);
		}

		/* TODO: save_msg_lumps should clone the lumps to shm mem */

		/* restore original environment and free the fake msg */
		faked_env( t, 0, 1);
		free_faked_req(&faked_req, t);

		/* update the flags */
		t->uas.request->flags = faked_req.flags;

		if (t->uas.status < 200) {
			/* No final reply has been sent yet.
			* Check whether or not there is any pending branch.
			*/
			for (	branch = 0;
				branch < t->nr_of_outgoings;
				branch++
			) {
				if (t->uac[branch].last_received < 200)
					break;
			}

			if (branch == t->nr_of_outgoings) {
			/* There is not any open branch so there is
			* no chance that a final response will be received. */
				ret = 0;
				goto kill_trans;
			}
		}

	} else {
		branch = t->async_backup.backup_branch;

		init_cancel_info(&cancel_data);

		LM_DBG("continuing from a suspended reply"
				" - resetting the suspend branch flag\n");

		if (t->uac[branch].reply) {
		t->uac[branch].reply->msg_flags &= ~FL_RPL_SUSPENDED;
                } else {
			LM_WARN("no reply in t_continue for branch. not much we can do\n");
			return 0;
		}
                
		if (t->uas.request) t->uas.request->msg_flags&= ~FL_RPL_SUSPENDED;

		faked_env( t, t->uac[branch].reply, 1);

		if (exec_pre_script_cb(t->uac[branch].reply, cb_type)>0) {
			if (run_top_route(route, t->uac[branch].reply, 0)<0){
				LOG(L_ERR, "ERROR: t_continue_reply: Error in run_top_route\n");
			}
			exec_post_script_cb(t->uac[branch].reply, cb_type);
		}

		LM_DBG("restoring previous environment");
		faked_env( t, 0, 1);

		/*lock transaction replies - will be unlocked when reply is relayed*/
		LOCK_REPLIES( t );
		if ( is_local(t) ) {
			LM_DBG("t is local - sending reply with status code: [%d]\n",
					t->uac[branch].reply->first_line.u.reply.statuscode);
			reply_status = local_reply( t, t->uac[branch].reply, branch,
					t->uac[branch].reply->first_line.u.reply.statuscode,
					&cancel_data );
			if (reply_status == RPS_COMPLETED) {
				/* no more UAC FR/RETR (if I received a 2xx, there may
				* be still pending branches ...
				*/
				cleanup_uac_timers( t );
				if (is_invite(t)) cancel_uacs(t, &cancel_data, F_CANCEL_B_KILL);
				/* There is no need to call set_final_timer because we know
				* that the transaction is local */
				put_on_wait(t);
			}else if (unlikely(cancel_data.cancel_bitmap)){
				/* cancel everything, even non-INVITEs (e.g in case of 6xx), use
				* cancel_b_method for canceling unreplied branches */
				cancel_uacs(t, &cancel_data, cfg_get(tm,tm_cfg, cancel_b_flags));
			}

		} else {
			LM_DBG("t is not local - relaying reply with status code: [%d]\n",
					t->uac[branch].reply->first_line.u.reply.statuscode);
			do_put_on_wait = 0;
			if(t->uac[branch].reply->first_line.u.reply.statuscode>=200){
				do_put_on_wait = 1;
			}
			reply_status=relay_reply( t, t->uac[branch].reply, branch,
					t->uac[branch].reply->first_line.u.reply.statuscode,
					&cancel_data, do_put_on_wait );
			if (reply_status == RPS_COMPLETED) {
				/* no more UAC FR/RETR (if I received a 2xx, there may
				be still pending branches ...
				*/
				cleanup_uac_timers( t );
				/* 2xx is a special case: we can have a COMPLETED request
				* with branches still open => we have to cancel them */
				if (is_invite(t) && cancel_data.cancel_bitmap) 
					cancel_uacs( t, &cancel_data,  F_CANCEL_B_KILL);
				/* FR for negative INVITES, WAIT anything else */
				/* Call to set_final_timer is embedded in relay_reply to avoid
				* race conditions when reply is sent out and an ACK to stop
				* retransmissions comes before retransmission timer is set.*/
			}else if (unlikely(cancel_data.cancel_bitmap)){
				/* cancel everything, even non-INVITEs (e.g in case of 6xx), use
				* cancel_b_method for canceling unreplied branches */
				cancel_uacs(t, &cancel_data, cfg_get(tm,tm_cfg, cancel_b_flags));
			}

		}
		t->uac[branch].request.flags|=F_RB_REPLIED;

		if (reply_status==RPS_ERROR){
			goto done;
		}

		/* update FR/RETR timers on provisional replies */

		msg_status=t->uac[branch].reply->REPLY_STATUS;
		last_uac_status=t->uac[branch].last_received;

		if (is_invite(t) && msg_status<200 &&
			( cfg_get(tm, tm_cfg, restart_fr_on_each_reply) ||
			( (last_uac_status<msg_status) &&
			((msg_status>=180) || (last_uac_status==0)) )
		) ) { /* provisional now */
			restart_rb_fr(& t->uac[branch].request, t->fr_inv_timeout);
			t->uac[branch].request.flags|=F_RB_FR_INV; /* mark fr_inv */
		}
            
	}

done:
	UNLOCK_ASYNC_CONTINUE(t);

	if(t->async_backup.backup_route != TM_ONREPLY_ROUTE){
		/* unref the transaction */
		t_unref(t->uas.request);
	} else {
		tm_ctx_set_branch_index(T_BR_UNDEFINED);        
		/* unref the transaction */
		t_unref(t->uac[branch].reply);
		LOG(L_DBG,"DEBUG: t_continue_reply: Freeing earlier cloned reply\n");

		/* free lumps that were added during reply processing */
		del_nonshm_lump( &(t->uac[branch].reply->add_rm) );
		del_nonshm_lump( &(t->uac[branch].reply->body_lumps) );
		del_nonshm_lump_rpl( &(t->uac[branch].reply->reply_lump) );

		/* free header's parsed structures that were added */
		for( hdr=t->uac[branch].reply->headers ; hdr ; hdr=hdr->next ) {
			if ( hdr->parsed && hdr_allocs_parse(hdr) &&
				(hdr->parsed<(void*)t->uac[branch].reply ||
				hdr->parsed>=(void*)t->uac[branch].end_reply)) {
				clean_hdr_field(hdr);
				hdr->parsed = 0;
			}
		}

		/* now go through hdr_fields themselves and remove the pkg allocated space */
		hdr = t->uac[branch].reply->headers;
		while (hdr) {
			if ( hdr && ((void*)hdr<(void*)t->uac[branch].reply ||
				(void*)hdr>=(void*)t->uac[branch].end_reply)) {
				//this header needs to be freed and removed form the list.
				if (!prev) {
					t->uac[branch].reply->headers = hdr->next;
				} else {
					prev->next = hdr->next;
				}
				tmp = hdr;
				hdr = hdr->next;
				pkg_free(tmp);
			} else {
				prev = hdr;
				hdr = hdr->next;
			}
		}
		sip_msg_free(t->uac[branch].reply);
		t->uac[branch].reply = 0;
	}

	/*This transaction is no longer suspended so unsetting the SUSPEND flag*/
	t->flags &= ~T_ASYNC_SUSPENDED;


	return 0;

kill_trans:
	t->flags &= ~T_ASYNC_SUSPENDED;
	/* The script has hopefully set the error code. If not,
	 * let us reply with a default error. */
	if ((kill_transaction_unsafe(t,
		tm_error ? tm_error : E_UNSPEC)) <=0
	) {
		LOG(L_ERR, "ERROR: t_continue: "
			"reply generation failed\n");
		/* The transaction must be explicitely released,
		 * no more timer is running */
		UNLOCK_ASYNC_CONTINUE(t);
		t_release_transaction(t);
	} else {
		UNLOCK_ASYNC_CONTINUE(t);
	}

	if(t->async_backup.backup_route != TM_ONREPLY_ROUTE){
		t_unref(t->uas.request);
	} else {
		/* unref the transaction */
		t_unref(t->uac[branch].reply);
	}
	return ret;
}