Beispiel #1
0
int t_unref( struct sip_msg* p_msg  )
{
	enum kill_reason kr;

	if (T==T_UNDEFINED)
		return -1;
	if (T!=T_NULL_CELL) {
		if (p_msg->first_line.type==SIP_REQUEST){
			kr=get_kr();
			if (kr==0 ||(p_msg->REQ_METHOD==METHOD_ACK && !(kr & REQ_RLSD)))
				t_release_transaction(T);
		}
		UNREF( T );
	}
	set_t(T_UNDEFINED);
	return 1;
}
Beispiel #2
0
int t_unref( struct sip_msg* p_msg  )
{
	enum kill_reason kr;

	if (T==T_UNDEFINED || T==T_NULL_CELL)
		return -1;
	if (p_msg->first_line.type==SIP_REQUEST){
		kr=get_kr();
		if (kr==0 
				||(p_msg->REQ_METHOD==METHOD_ACK && !(kr & REQ_RLSD))) {
			LOG(L_WARN, "WARNING: script writer didn't release transaction\n");
			t_release_transaction(T);
		}
	}
	UNREF( T );
	set_t(T_UNDEFINED);
	return 1;
}
Beispiel #3
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;
}
Beispiel #4
0
int t_handle_async(struct sip_msg *msg, struct action* a , int resume_route)
{
    async_ctx *ctx = NULL;
    async_resume_module *ctx_f;
    void *ctx_p;
    struct cell *t;
    int r;
    int fd;

    /* create transaction and save everything into transaction */
    t=get_t();
    if ( t==0 || t==T_UNDEFINED ) {
        /* create transaction */
        r = t_newtran( msg , 1 /*full uas clone*/ );
        if (r==0) {
            /* retransmission -> no follow up; we return a negative
             * code to indicate do_action that the top route is
             * is completed (there no resume route to follow) */
            return -1;
        } else if (r<0) {
            LM_ERR("could not create a new transaction\n");
            goto failure;
        }
        t=get_t();
    } else {
        /* update the cloned UAS (from transaction)
         * with data from current msg */
        if (t->uas.request)
            update_cloned_msg_from_msg( t->uas.request, msg);
    }

    /* run the function (the action) and get back from it the FD,
     * resume function and param */
    if ( a->type!=AMODULE_T || a->elem[0].type!=ACMD_ST ||
            a->elem[0].u.data==NULL ) {
        LM_CRIT("BUG - invalid action for async I/O - it must be"
                " a MODULE_T ACMD_ST \n");
        goto failure;
    }

    async_status = ASYNC_NO_IO; /*assume defauly status "no IO done" */
    return_code = ((acmd_export_t*)(a->elem[0].u.data))->function(msg,
                  &ctx_f, &ctx_p,
                  (char*)a->elem[1].u.data, (char*)a->elem[2].u.data,
                  (char*)a->elem[3].u.data, (char*)a->elem[4].u.data,
                  (char*)a->elem[5].u.data, (char*)a->elem[6].u.data );
    /* what to do now ? */
    if (async_status>=0) {
        /* async I/O was successfully launched */
        fd = async_status;
        if (msg->REQ_METHOD==METHOD_ACK ||
                /* ^^^ end2end ACK, there is no actual transaction here */
                t->uas.request==NULL
                /* ^^^ local requests do not support async in local route */
           ) {
            goto sync;
        }
    } else if (async_status==ASYNC_NO_IO) {
        /* no IO, so simply go for resume route */
        goto resume;
    } else if (async_status==ASYNC_SYNC) {
        /* IO already done in SYNC'ed way */
        goto resume;
    } else if (async_status==ASYNC_CHANGE_FD) {
        LM_ERR("Incorrect ASYNC_CHANGE_FD status usage!"
               "You should use this status only from the"
               "resume function in case something went wrong"
               "and you have other alternatives!\n");
        /*FIXME should we go to resume or exit?it's quite an invalid scenario */
        goto resume;
    } else {
        /* generic error, go for resume route */
        goto resume;
    }

    /* do we have a reactor in this process, to handle this
       asyn I/O ? */
    if ( 0/*reactor_exists()*/ ) {
        /* no reactor, so we directly call the resume function
           which will block waiting for data */
        goto sync;
    }

    if ( (ctx=shm_malloc(sizeof(async_ctx)))==NULL) {
        LM_ERR("failed to allocate new ctx\n");
        goto sync;
    }

    ctx->resume_f = ctx_f;
    ctx->resume_param = ctx_p;
    ctx->resume_route = resume_route;
    ctx->route_type = route_type;
    ctx->msg_ctx = current_processing_ctx;
    ctx->t = t;
    ctx->kr = get_kr();

    ctx->cancelled_t = get_cancelled_t();
    ctx->e2eack_t = get_e2eack_t();

    current_processing_ctx = NULL;
    set_t(T_UNDEFINED);
    reset_cancelled_t();
    reset_e2eack_t();

    /* place the FD + resume function (as param) into reactor */
    if (reactor_add_reader( fd, F_SCRIPT_ASYNC, RCT_PRIO_ASYNC, (void*)ctx)<0 ) {
        LM_ERR("failed to add async FD to reactor -> act in sync mode\n");
        goto sync;
    }

    /* done, break the script */
    return 0;

sync:
    if (ctx) {
        /*
         * the context was moved in reactor, but the reactor could not
         * fullfil the request - we have to restore the environment -- razvanc
         */
        current_processing_ctx = ctx->msg_ctx;
        set_t(t);
        set_cancelled_t(ctx->cancelled_t);
        set_e2eack_t(ctx->e2eack_t);
        shm_free(ctx);
    }
    /* run the resume function */
    do {
        return_code = ctx_f( fd, msg, ctx_p );
        if (async_status == ASYNC_CHANGE_FD)
            fd = return_code;
    } while(async_status==ASYNC_CONTINUE||async_status==ASYNC_CHANGE_FD);
    /* run the resume route in sync mode */
    run_resume_route( resume_route, msg);

    /* break original script */
    return 0;

failure:
    /* execute here the resume route with failure indication */
    return_code = -1;
resume:
    /* run the resume route */
    run_resume_route( resume_route, msg);
    /* the triggering route is terminated and whole script ended */
    return 0;
}
Beispiel #5
0
int t_handle_async(struct sip_msg *msg, struct action* a , int resume_route)
{
	async_ctx *ctx;
	async_resume_module *ctx_f;
	void *ctx_p;
	struct cell *t;
	int r;
	int fd;

	/* create transaction and save everything into transaction */
	t=get_t();
	if ( t==0 || t==T_UNDEFINED ) {
		/* create transaction */
		r = t_newtran( msg , 1 /*full uas clone*/ );
		if (r==0) {
			/* retransmission -> break the script, no follow up */
			return 0;
		} else if (r<0) {
			LM_ERR("could not create a new transaction\n");
			goto failure;
		}
		t=get_t();
	} else {
		/* update the cloned UAS (from transaction)
		 * with data from current msg */
		update_cloned_msg_from_msg( t->uas.request, msg);
	}

	/* run the function (the action) and get back from it the FD,
	 * resume function and param */
	if ( a->type!=AMODULE_T || a->elem[0].type!=ACMD_ST ||
	a->elem[0].u.data==NULL ) {
		LM_CRIT("BUG - invalid action for async I/O - it must be"
			" a MODULE_T ACMD_ST \n");
		goto failure;
	}

	async_status = ASYNC_NO_IO; /*assume defauly status "no IO done" */
	return_code = ((acmd_export_t*)(a->elem[0].u.data))->function(msg,
			&ctx_f, &ctx_p,
			(char*)a->elem[1].u.data, (char*)a->elem[2].u.data,
			(char*)a->elem[3].u.data, (char*)a->elem[4].u.data,
			(char*)a->elem[5].u.data, (char*)a->elem[6].u.data );
	/* what to do now ? */
	if (async_status>=0) {
		/* async I/O was succesfully launched */
		fd = async_status;
	} else if (async_status==ASYNC_NO_IO) {
		/* no IO, so simply go for resume route */
		goto resume;
	} else if (async_status==ASYNC_SYNC) {
		/* IO already done in SYNC'ed way */
		goto resume;
	} else {
		/* generic error, go for resume route */
		goto resume;
	}

	/* do we have a reactor in this process, to handle this 
	   asyn I/O ? */
	if ( 0/*reactor_exists()*/ ) {
		/* no reactor, so we directly call the resume function
		   which will block waiting for data */
		goto sync;
	}

	if ( (ctx=shm_malloc(sizeof(async_ctx)))==NULL) {
		LM_ERR("failed to allocate new ctx\n");
		goto sync;
	}

	ctx->resume_f = ctx_f;
	ctx->resume_param = ctx_p;
	ctx->resume_route = resume_route;
	ctx->route_type = route_type;
	ctx->msg_ctx = current_processing_ctx;
	ctx->t = t;
	ctx->kr = get_kr();

	current_processing_ctx = NULL;
	set_t(T_UNDEFINED);

	/* place the FD + resume function (as param) into reactor */
	if (reactor_add_reader( fd, F_SCRIPT_ASYNC, RCT_PRIO_ASYNC, (void*)ctx)<0 ) {
		LM_ERR("failed to add async FD to reactor -> act in sync mode\n");
		shm_free(ctx);
		goto sync;
	}

	/* done, break the script */
	return 0;

sync:
	/* run the resume function */
	do {
		return_code = ctx_f( fd, msg, ctx_p );
	} while(async_status!=ASYNC_CONTINUE);
	/* run the resume route in sync mode */
	run_resume_route( resume_route, msg);
	/* break original script */
	return 0;

failure:
	/* execute here the resume route with failure indication */
	return_code = -1;
resume:
	/* run the resume route */
	run_resume_route( resume_route, msg);
	/* the triggering route is terminated and whole script ended */
	return 0;
}