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; }
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; }
/* 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; }
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; }
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; }