ticks_t wait_handler(ticks_t ti, struct timer_ln *wait_tl, void *data) { struct cell *p_cell; ticks_t ret; p_cell = (struct cell *)data; #ifdef TIMER_DEBUG LM_DBG("WAIT timer hit @%d for %p (timer_lm %p)\n", ti, p_cell, wait_tl); #endif #ifdef TM_DEL_UNREF /* stop cancel timers if any running */ if(is_invite(p_cell)) cleanup_localcancel_timers(p_cell); /* remove the cell from the hash table */ LOCK_HASH(p_cell->hash_index); remove_from_hash_table_unsafe(p_cell); UNLOCK_HASH(p_cell->hash_index); p_cell->flags |= T_IN_AGONY; if(t_linked_timers(p_cell)) { UNREF_FREE(p_cell, 0); } else { UNREF_FREE(p_cell, 1); } ret = 0; #else /* TM_DEL_UNREF */ if(p_cell->flags & T_IN_AGONY) { /* delayed delete */ /* we call delete now without any locking on hash/ref_count; we can do that because delete_handler is only entered after the delete timer was installed from wait_handler, which removed transaction from hash table and did not destroy it because some processes were using it; that means that the processes currently using the transaction can unref and no new processes can ref -- we can wait until ref_count is zero safely without locking */ ret = delete_cell(p_cell, 0 /* don't unlock on return */); } else { /* stop cancel timers if any running */ if(is_invite(p_cell)) cleanup_localcancel_timers(p_cell); /* remove the cell from the hash table */ LOCK_HASH(p_cell->hash_index); remove_from_hash_table_unsafe(p_cell); p_cell->flags |= T_IN_AGONY; /* delete (returns with UNLOCK-ed_HASH) */ ret = delete_cell(p_cell, 1 /* unlock on return */); } #endif /* TM_DEL_UNREF */ return ret; }
inline static void wait_handler( struct timer_link *wait_tl ) { struct cell *p_cell; p_cell = get_wait_timer_payload( wait_tl ); #ifdef EXTRA_DEBUG if (p_cell->damocles) { LM_ERR("transaction %p scheduled for deletion and" " called from WAIT timer\n",p_cell); abort(); } LM_DBG("WAIT timer hit\n"); #endif /* stop cancel timers if any running */ if ( is_invite(p_cell) ) cleanup_localcancel_timers( p_cell ); /* the transaction is already removed from WT_LIST by the timer */ /* remove the cell from the hash table */ LM_DBG("removing %p from table \n", p_cell ); LOCK_HASH( p_cell->hash_index ); remove_from_hash_table_unsafe( p_cell ); /* jku: no more here -- we do it when we put a transaction on wait */ #ifdef EXTRA_DEBUG p_cell->damocles = 1; #endif /* delete (returns with UNLOCK-ed_HASH) */ delete_cell( p_cell, 1 /* unlock on return */ ); LM_DBG("done\n"); }
/* 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); }
/* parse incoming replies before cloning */ static inline void acc_onreply_in(struct cell *t, struct sip_msg *req, struct sip_msg *reply, int code) { /* don't parse replies in which we are not interested */ /* missed calls enabled ? */ if ( (reply && reply!=FAKED_REPLY) && (should_acc_reply(req,reply,code) || (is_invite(t) && code>=300 && is_mc_on(req))) ) { parse_headers(reply, HDR_TO_F, 0 ); } }
inline static int w_t_cancel_branch(struct sip_msg *msg, char *sflags) { branch_bm_t cancel_bitmap = 0; struct cell *t; unsigned int flags = (unsigned long)sflags; t=get_t(); if (t==NULL || t==T_UNDEFINED) { /* no transaction */ LM_ERR("cannot cancel a reply with no transaction"); return -1; } if (!is_invite(t)) return -1; if (flags&TM_CANCEL_BRANCH_ALL) { /* lock and get the branches to cancel */ if (!onreply_avp_mode) { LOCK_REPLIES(t); which_cancel( t, &cancel_bitmap ); UNLOCK_REPLIES(t); } else { which_cancel( t, &cancel_bitmap ); } if (msg->first_line.u.reply.statuscode>=200) /* do not cancel the current branch as we got * a final response here */ cancel_bitmap &= ~(1<<_tm_branch_index); } else if (flags&TM_CANCEL_BRANCH_OTHERS) { /* lock and get the branches to cancel */ if (!onreply_avp_mode) { LOCK_REPLIES(t); which_cancel( t, &cancel_bitmap ); UNLOCK_REPLIES(t); } else { which_cancel( t, &cancel_bitmap ); } /* ignore current branch */ cancel_bitmap &= ~(1<<_tm_branch_index); } else { /* cancel only local branch (only if still ongoing) */ if (msg->first_line.u.reply.statuscode<200) cancel_bitmap = 1<<_tm_branch_index; } /* send cancels out */ cancel_uacs(t, cancel_bitmap); return 1; }
/* initiate a report if we previously enabled MC accounting for this t */ static void failure_handler(struct cell *t, int type, struct tmcb_params* ps) { /* validation */ if (t->uas.request == 0) { DBG("DBG:acc:failure_handler: No uas.request, skipping local transaction\n"); return; } if (is_invite(t) && ps->code >= 300) { if (is_mc_on(t->uas.request)) { log_missed(t, ps->rpl, ps->code, (time_t)*(ps->param)); resetflag(t->uas.request, log_missed_flag); } } }
/* parse incoming replies before cloning */ static void replyin_handler(struct cell *t, int type, struct tmcb_params* ps) { /* validation */ if (t->uas.request == 0) { LOG(L_ERR, "ERROR:acc:replyin_handler:replyin_handler: 0 request\n"); return; } /* don't parse replies in which we are not interested */ /* missed calls enabled ? */ if (((is_invite(t) && ps->code >= 300 && is_mc_on(t->uas.request)) || should_acc_reply(t, ps->code)) && (ps->rpl && ps->rpl != FAKED_REPLY)) { parse_headers(ps->rpl, HDR_TO_F, 0); } }
char *build_cancel(struct cell *Trans,unsigned int branch, unsigned int *len ) { str method = str_init(CANCEL); str reason = str_init(CANCEL_REASON CRLF); str *extra = NULL; /* add the reason hdr, as per RFC 3326 */ if (is_invite(Trans) && Trans->uas.status==200) { extra = &reason; } else if (_extra_cancel_hdrs.s) { extra = &_extra_cancel_hdrs; } return build_local( Trans, branch, &method, extra, Trans->uac[branch].reply , len ); }
static int t_cancel_branches(sip_msg_t* msg, char *k, char *s2) { struct cancel_info cancel_data; tm_cell_t *t = 0; tm_ctx_t *tcx = 0; int n=0; int idx = 0; t=_tmx_tmb.t_gett(); if (t==NULL || t==T_UNDEFINED || !is_invite(t)) return -1; tcx = _tmx_tmb.tm_ctx_get(); if(tcx != NULL) idx = tcx->branch_index; n = (int)(long)k; init_cancel_info(&cancel_data); switch(n) { case 1: /* prepare cancel for every branch except idx (others) */ _tmx_tmb.prepare_to_cancel(t, &cancel_data.cancel_bitmap, 1<<idx); break; case 2: /* prepare cancel for current branch (idx) */ if(msg->first_line.u.reply.statuscode>=200) break; cancel_data.cancel_bitmap = 1<<idx; break; default: /* prepare cancel for all branches */ if (msg->first_line.u.reply.statuscode>=200) /* prepare cancel for every branch except idx */ _tmx_tmb.prepare_to_cancel(t, &cancel_data.cancel_bitmap, 1<<idx); else _tmx_tmb.prepare_to_cancel(t, &cancel_data.cancel_bitmap, 0); } LM_DBG("canceling %d/%d\n", n, (int)cancel_data.cancel_bitmap); if(cancel_data.cancel_bitmap==0) return -1; _tmx_tmb.cancel_uacs(t, &cancel_data, 0); return 1; }
char *build_cancel(struct cell *Trans,unsigned int branch, unsigned int *len ) { str method = str_init(CANCEL); str reason = str_init(CANCEL_REASON CRLF); str *extra = NULL; /* add the reason hdr, as per RFC 3326 */ if (is_invite(Trans) && Trans->uas.status==200) { extra = &reason; } else if (_extra_cancel_hdrs.s) { extra = &_extra_cancel_hdrs; } return build_local( Trans, branch, &method, extra, NULL /*reply*/ , len ); /* ^^^^ when CANCELing, there are 0 chances to have a reply stored into * transaction ; set it NULL to avoid using the temporary stored reply * (by t_should_relay_response) which may lead into races ( building the * cancel versus handling a final response in a different process )*/ }
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 ); } }
/* 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; }
/** * ac_reply: UAS transaction Reply action. It replies to an incoming request with a response. * @param the_as The App Server that sent this action. * @param action action * @param len length * * function description * * Returns: what */ int ac_reply(as_p the_as,unsigned char processor_id,unsigned int flags,char *action,int len) { unsigned int hash_index,label,contentlength; struct cell *c=NULL; struct sip_msg *my_msg; struct to_body *tb; str new_header,body,totag; char *ttag; int i,k,retval; static char headers[MAX_HEADER]; struct as_uac_param *the_param; contentlength=0; ttag=NULL; my_msg=NULL; i=k=0; the_param=NULL; net2hostL(hash_index,action,k); net2hostL(label,action,k); if(seas_f.tmb.t_lookup_ident(&c,hash_index,label)<0){ LM_ERR("Failed to t_lookup_ident hash_idx=%d,label=%d\n",hash_index,label); goto error; } if(use_stats) action_stat(c); if(c->uas.status>=200){ LM_ERR("ac_reply: trying to reply to a \"%.*s\" transaction" "that is already in completed state\n",REQ_LINE(c->uas.request).method.len,REQ_LINE(c->uas.request).method.s); goto error; } if (!(my_msg=parse_ac_msg(HDR_EOH_F,action+k,len-k))) { LM_ERR("Failed to parse_ac_msg hash_idx=%d,label=%d\n",hash_index,label); goto error; } tb=(struct to_body*)my_msg->to->parsed; if(tb->tag_value.s && tb->tag_value.len){ totag=tb->tag_value; }else{ totag.s=NULL; totag.len=0; /*if(!(ttag=pkg_malloc(TOTAG_VALUE_LEN))){ LM_ERR("Out of memory !!!\n"); goto error; } totag.s=ttag; calc_crc_suffix(c->uas.request,seas_tag_suffix); LM_DBG("seas_tags = %.*s\n",TOTAG_VALUE_LEN,seas_tags); memcpy(totag.s,seas_tags,TOTAG_VALUE_LEN); totag.len=TOTAG_VALUE_LEN;*/ } LM_DBG("Using totag=[%.*s]\n",totag.len,totag.s); if(my_msg->content_length) contentlength=(unsigned int)(long)my_msg->content_length->parsed; if(0>(i=recordroute_diff(c->uas.request,my_msg))){/*not likely..*/ LM_DBG("Seems that request had more RecordRoutes than response...\n"); /* This prevents host->proxy->host from working. TODO review recordroute_diff code. goto error; */ }else LM_DBG("Recordroute Diff = %d\n",i); if(0>(i=extract_allowed_headers(my_msg,0,i,HDR_VIA_F|HDR_TO_F|HDR_FROM_F|HDR_CSEQ_F|HDR_CALLID_F|HDR_CONTENTLENGTH_F,headers,MAX_HEADER))){ LM_ERR("ac_reply() filtering headers !\n"); goto error; } headers[i]=0; new_header.s=headers; new_header.len=i; /* If it is INVITE and response is success (>=200 && <300), we mark it as local so that * SER does NOT retransmit the final response (by default, SER retransmit local UAS final * responses...*/ if(is_invite(c) && my_msg->first_line.u.reply.statuscode>=200 && my_msg->first_line.u.reply.statuscode<300){ c->flags |= T_IS_LOCAL_FLAG; if(!(the_param=shm_malloc(sizeof(struct as_uac_param)))){ LM_ERR("no more share memory\n"); goto error; } the_param->processor_id=processor_id; the_param->who=my_as; the_param->destroy_cb_set=0; if (seas_f.tmb.register_tmcb( 0, c, TMCB_E2EACK_IN,uas_e2e_ack_cb, the_param,¶m_free)<=0) { LM_ERR("cannot register additional callbacks\n"); goto error; } } /*WARNING casting unsigned int to int*/ body.len=contentlength; body.s=get_body(my_msg); LM_DBG("Trying to construct a SipReply with: ReasonPhrase:[%.*s] body:[%.*s] headers:[%.*s] totag:[%.*s]\n",\ my_msg->first_line.u.reply.reason.len,my_msg->first_line.u.reply.reason.s,\ body.len,body.s,new_header.len,new_header.s,totag.len,totag.s); /* t_reply_with_body un-ref-counts the transaction, so dont use it anymore*/ if(seas_f.tmb.t_reply_with_body(c,my_msg->first_line.u.reply.statuscode,&(my_msg->first_line.u.reply.reason),&body,&new_header,&totag)<0){ LM_ERR("Failed to t_reply\n"); goto error; } retval=0; goto exit; error: retval = -1; if(c) seas_f.tmb.unref_cell(c); if(the_param) shm_free(the_param); exit: if(ttag) pkg_free(ttag); if(my_msg){ free_sip_msg_lite(my_msg); pkg_free(my_msg); } return retval; }
/* initiate a report if we previously enabled accounting for this t */ static inline void acc_onreply( struct cell* t, struct sip_msg *req, struct sip_msg *reply, int code) { str new_uri_bk; str dst_uri_bk; struct dlg_cell *dlg = NULL; str flags_s; int_str table; struct usr_avp *avp; /* acc_onreply is bound to TMCB_REPLY which may be called from _reply, like when FR hits; we should not miss this event for missed calls either */ if (is_invite(t) && code>=300 && is_mc_on(req) ) on_missed(t, req, reply, code); if (!should_acc_reply(req, reply, code)) return; /* for reply processing, set as new_uri the winning branch */ if (t->relaied_reply_branch>=0) { new_uri_bk = req->new_uri; dst_uri_bk = req->dst_uri; req->new_uri = t->uac[t->relaied_reply_branch].uri; req->dst_uri = t->uac[t->relaied_reply_branch].duri; req->parsed_uri_ok = 0; } else { new_uri_bk.len = dst_uri_bk.len = -1; new_uri_bk.s = dst_uri_bk.s = NULL; } /* set env variables */ env_set_to( get_rpl_to(t,reply) ); env_set_code_status( code, reply); /* search for table avp */ table.s = db_table_acc; if (db_table_name != -1 && is_db_acc_on(req)) { avp = search_first_avp(db_table_name_type, db_table_name, &table, 0); if (!avp) { LM_DBG("table not set: using default %.*s\n", db_table_acc.len, db_table_acc.s); } else { if (!(avp->flags & AVP_VAL_STR)) { LM_WARN("invalid integer table name: using default %.*s\n", db_table_acc.len, db_table_acc.s); table.s = db_table_acc; } } } if (is_invite(t) && is_cdr_acc_on(req) && code >= 200 && code < 300 && (dlg=dlg_api.get_dlg()) != NULL) { /* if dialog module loaded and INVITE and success reply */ if (store_core_leg_values(dlg, req) < 0) { LM_ERR("cannot store core and leg values\n"); return; } if(is_log_acc_on(req) && store_log_extra_values(dlg,req,reply)<0){ LM_ERR("cannot store string values\n"); return; } if(is_aaa_acc_on(req) && store_aaa_extra_values(dlg, req, reply)<0){ LM_ERR("cannot store aaa extra values\n"); return; } if (is_db_acc_on(req) && store_db_extra_values(dlg,req,reply)<0) { LM_ERR("cannot store database extra values\n"); return; } flags_s.s = (char*)&req->flags; flags_s.len = sizeof(unsigned int); /* store flags into dlg */ if ( dlg_api.store_dlg_value(dlg, &flags_str, &flags_s) < 0) { LM_ERR("cannot store flag value into dialog\n"); return; } /* store flags into dlg */ if ( dlg_api.store_dlg_value(dlg, &table_str, &table.s) < 0) { LM_ERR("cannot store the table name into dialog\n"); return; } /* register database callbacks */ if (dlg_api.register_dlgcb(dlg, DLGCB_TERMINATED | DLGCB_EXPIRED, acc_dlg_callback,(void *)(long)req->flags,0) != 0) { LM_ERR("cannot register callback for database accounting\n"); return; } } else { /* do old accounting */ if ( is_log_acc_on(req) ) { env_set_text( ACC_ANSWERED, ACC_ANSWERED_LEN); acc_log_request( req, reply ); } if (is_aaa_acc_on(req)) acc_aaa_request( req, reply ); if (is_db_acc_on(req)) { env_set_text( table.s.s, table.s.len); acc_db_request( req, reply, &acc_ins_list); } } /* DIAMETER */ #ifdef DIAM_ACC if (is_diam_acc_on(req)) acc_diam_request( req, reply ); #endif if (new_uri_bk.len>=0) { req->new_uri = new_uri_bk; req->dst_uri = dst_uri_bk; req->parsed_uri_ok = 0; } }
inline static void final_response_handler( struct retr_buf *r_buf, struct cell *t) { int silent; #ifdef USE_DNS_FAILOVER /*int i; int added_branches; */ int branch_ret; int prev_branch; ticks_t now; #endif #ifdef EXTRA_DEBUG if(t->flags & T_IN_AGONY) { LM_ERR("transaction %p scheduled for deletion and" " called from FR timer (flags %x)\n", t, t->flags); abort(); } #endif /* FR for local cancels.... */ if(r_buf->rbtype == TYPE_LOCAL_CANCEL) { #ifdef TIMER_DEBUG LM_DBG("stop retr for local cancel\n"); #endif return; } /* FR for replies (negative INVITE replies) */ if(r_buf->rbtype > 0) { #ifdef EXTRA_DEBUG if(t->uas.request->REQ_METHOD != METHOD_INVITE || t->uas.status < 200) { LM_CRIT("BUG - unknown type reply buffer\n"); abort(); } #endif put_on_wait(t); return; }; /* lock reply processing to determine how to proceed reliably */ LOCK_REPLIES(t); /* now it can be only a request retransmission buffer; try if you can simply discard the local transaction state without compellingly removing it from the world */ silent = /* don't go silent if disallowed globally ... */ cfg_get(tm, tm_cfg, noisy_ctimer) == 0 /* ... or for this particular transaction */ && has_noisy_ctimer(t) == 0 /* not for UACs */ && !is_local(t) /* invites only */ && is_invite(t) /* parallel forking does not allow silent state discarding */ && t->nr_of_outgoings == 1 /* on_negativ reply handler not installed -- serial forking * could occur otherwise */ && t->on_failure == 0 /* the same for FAILURE callbacks */ && !has_tran_tmcbs(t, TMCB_ON_FAILURE_RO | TMCB_ON_FAILURE) /* something received -- we will not be silent on error */ && t->uac[r_buf->branch].last_received == 0; if(silent) { UNLOCK_REPLIES(t); #ifdef EXTRA_DEBUG LM_DBG("transaction silently dropped (%p), branch %d, last_received %d\n", t, r_buf->branch, t->uac[r_buf->branch].last_received); #endif put_on_wait(t); return; } #ifdef EXTRA_DEBUG LM_DBG("stop retr. and send CANCEL (%p)\n", t); #endif if((r_buf->branch < sr_dst_max_branches) && /* r_buf->branch is always >=0 */ (t->uac[r_buf->branch].last_received == 0) && (t->uac[r_buf->branch].request.buffer != NULL) /* not a blind UAC */ ) { /* no reply received */ #ifdef USE_DST_BLACKLIST if(r_buf->my_T && r_buf->my_T->uas.request && (r_buf->my_T->uas.request->REQ_METHOD & cfg_get(tm, tm_cfg, tm_blst_methods_add))) dst_blacklist_add( BLST_ERR_TIMEOUT, &r_buf->dst, r_buf->my_T->uas.request); #endif #ifdef USE_DNS_FAILOVER /* if this is an invite, the destination resolves to more ips, and * it still hasn't passed more than fr_inv_timeout since we * started, add another branch/uac */ if(cfg_get(core, core_cfg, use_dns_failover)) { now = get_ticks_raw(); if((s_ticks_t)(t->end_of_life - now) > 0) { branch_ret = add_uac_dns_fallback( t, t->uas.request, &t->uac[r_buf->branch], 0); prev_branch = -1; while((branch_ret >= 0) && (branch_ret != prev_branch)) { prev_branch = branch_ret; branch_ret = t_send_branch(t, branch_ret, t->uas.request, 0, 0); } } } #endif } fake_reply(t, r_buf->branch, 408); }
/* this is the "UAC" above transaction layer; if a final reply is received, it triggers a callback; note well -- it assumes it is entered locked with REPLY_LOCK and it returns unlocked! */ enum rps local_reply( struct cell *t, struct sip_msg *p_msg, int branch, unsigned int msg_status, branch_bm_t *cancel_bitmap) { /* how to deal with replies for local transaction */ int local_store, local_winner; enum rps reply_status; struct sip_msg *winning_msg; int winning_code; int totag_retr; /* branch_bm_t cancel_bitmap; */ /* keep warning 'var might be used un-inited' silent */ winning_msg=0; winning_code=0; totag_retr=0; *cancel_bitmap=0; reply_status=t_should_relay_response( t, msg_status, branch, &local_store, &local_winner, cancel_bitmap, p_msg ); LM_DBG("branch=%d, save=%d, winner=%d\n", branch, local_store, local_winner ); if (local_store) { if (!store_reply(t, branch, p_msg)) goto error; } if (local_winner>=0) { winning_msg= branch==local_winner ? p_msg : t->uac[local_winner].reply; if (winning_msg==FAKED_REPLY) { winning_code = branch==local_winner ? msg_status : t->uac[local_winner].last_received; } else { winning_code=winning_msg->REPLY_STATUS; } t->uas.status = winning_code; stats_trans_rpl( winning_code, (winning_msg==FAKED_REPLY)?1:0 ); if (is_invite(t) && winning_msg!=FAKED_REPLY && winning_code>=200 && winning_code <300 && has_tran_tmcbs(t, TMCB_RESPONSE_OUT|TMCB_RESPONSE_PRE_OUT) ) { totag_retr=update_totag_set(t, winning_msg); } } UNLOCK_REPLIES(t); if ( local_winner >= 0 ) { if (winning_code < 200) { if (!totag_retr && has_tran_tmcbs(t,TMCB_LOCAL_RESPONSE_OUT)) { LM_DBG("Passing provisional reply %d to FIFO application\n", winning_code); run_trans_callbacks( TMCB_LOCAL_RESPONSE_OUT, t, 0, winning_msg, winning_code); } } else { LM_DBG("local transaction completed\n"); if (!totag_retr && has_tran_tmcbs(t,TMCB_LOCAL_COMPLETED) ) { run_trans_callbacks( TMCB_LOCAL_COMPLETED, t, 0, winning_msg, winning_code ); } } } return reply_status; error: which_cancel(t, cancel_bitmap); UNLOCK_REPLIES(t); cleanup_uac_timers(t); if ( get_cseq(p_msg)->method_id==METHOD_INVITE ) cancel_uacs( t, *cancel_bitmap ); put_on_wait(t); return RPS_ERROR; }
/* Returns 0 - nothing found * 1 - T found */ int t_reply_matching( struct sip_msg *p_msg , int *p_branch ) { struct cell* p_cell; unsigned int hash_index = 0; unsigned int entry_label = 0; unsigned int branch_id = 0; char *hashi, *branchi, *p, *n; int hashl, branchl; int scan_space; str cseq_method; str req_method; char *loopi; int loopl; char *syni; int synl; short is_cancel; /* make compiler warnings happy */ loopi=0; loopl=0; syni=0; synl=0; /* split the branch into pieces: loop_detection_check(ignored), hash_table_id, synonym_id, branch_id */ if (!(p_msg->via1 && p_msg->via1->branch && p_msg->via1->branch->value.s)) goto nomatch2; /* we do RFC 3261 tid matching and want to see first if there is * magic cookie in branch */ if (p_msg->via1->branch->value.len<=MCOOKIE_LEN) goto nomatch2; if (memcmp(p_msg->via1->branch->value.s, MCOOKIE, MCOOKIE_LEN)!=0) goto nomatch2; p=p_msg->via1->branch->value.s+MCOOKIE_LEN; scan_space=p_msg->via1->branch->value.len-MCOOKIE_LEN; /* hash_id */ n=eat_token2_end( p, p+scan_space, BRANCH_SEPARATOR); hashl=n-p; scan_space-=hashl; if (!hashl || scan_space<2 || *n!=BRANCH_SEPARATOR) goto nomatch2; hashi=p; p=n+1;scan_space--; if (!syn_branch) { /* md5 value */ n=eat_token2_end( p, p+scan_space, BRANCH_SEPARATOR ); loopl = n-p; scan_space-= loopl; if (n==p || scan_space<2 || *n!=BRANCH_SEPARATOR) goto nomatch2; loopi=p; p=n+1; scan_space--; } else { /* synonym id */ n=eat_token2_end( p, p+scan_space, BRANCH_SEPARATOR); synl=n-p; scan_space-=synl; if (!synl || scan_space<2 || *n!=BRANCH_SEPARATOR) goto nomatch2; syni=p; p=n+1;scan_space--; } /* branch id - should exceed the scan_space */ n=eat_token_end( p, p+scan_space ); branchl=n-p; if (!branchl ) goto nomatch2; branchi=p; /* sanity check */ if (reverse_hex2int(hashi, hashl, &hash_index)<0 ||hash_index>=TABLE_ENTRIES || reverse_hex2int(branchi, branchl, &branch_id)<0 ||branch_id>=MAX_BRANCHES || (syn_branch ? (reverse_hex2int(syni, synl, &entry_label))<0 : loopl!=MD5_LEN ) ) { DBG("DEBUG: t_reply_matching: poor reply labels %d label %d " "branch %d\n", hash_index, entry_label, branch_id ); goto nomatch2; } DBG("DEBUG: t_reply_matching: hash %d label %d branch %d\n", hash_index, entry_label, branch_id ); /* search the hash table list at entry 'hash_index'; lock the entry first */ cseq_method=get_cseq(p_msg)->method; is_cancel=cseq_method.len==CANCEL_LEN && memcmp(cseq_method.s, CANCEL, CANCEL_LEN)==0; LOCK_HASH(hash_index); for (p_cell = get_tm_table()->entrys[hash_index].first_cell; p_cell; p_cell=p_cell->next_cell) { /* first look if branch matches */ if (syn_branch) { if (p_cell->label != entry_label) continue; } else { if ( memcmp(p_cell->md5, loopi,MD5_LEN)!=0) continue; } /* sanity check ... too high branch ? */ if ( branch_id>=p_cell->nr_of_outgoings ) continue; /* does method match ? (remember -- CANCELs have the same branch as canceled transactions) */ req_method=p_cell->method; if ( /* method match */ ! ((cseq_method.len==req_method.len && memcmp( cseq_method.s, req_method.s, cseq_method.len )==0) /* or it is a local cancel */ || (is_cancel && is_invite(p_cell) /* commented out -- should_cancel_branch set it to BUSY_BUFFER to avoid collisions with replies; thus, we test here by buffer size */ /* && p_cell->uac[branch_id].local_cancel.buffer ))) */ && p_cell->uac[branch_id].local_cancel.buffer_len ))) continue; /* we passed all disqualifying factors .... the transaction has been matched ! */ set_t(p_cell); *p_branch =(int) branch_id; REF_UNSAFE( T ); UNLOCK_HASH(hash_index); DBG("DEBUG: t_reply_matching: reply matched (T=%p)!\n",T); /* if this is a 200 for INVITE, we will wish to store to-tags to be * able to distinguish retransmissions later and not to call * TMCB_RESPONSE_OUT uselessly; we do it only if callbacks are * enabled -- except callback customers, nobody cares about * retransmissions of multiple 200/INV or ACK/200s */ if (is_invite(p_cell) && p_msg->REPLY_STATUS>=200 && p_msg->REPLY_STATUS<300 && ( (!is_local(p_cell) && has_tran_tmcbs(p_cell,TMCB_RESPONSE_OUT|TMCB_E2EACK_IN) ) || (is_local(p_cell)&&has_tran_tmcbs(p_cell,TMCB_LOCAL_COMPLETED)) )) { if (parse_headers(p_msg, HDR_TO_F, 0)==-1) { LOG(L_ERR, "ERROR: t_reply_matching: to parsing failed\n"); } } if (!is_local(p_cell)) { run_trans_callbacks( TMCB_RESPONSE_IN, T, T->uas.request, p_msg, p_msg->REPLY_STATUS); } return 1; } /* for cycle */ /* nothing found */ UNLOCK_HASH(hash_index); DBG("DEBUG: t_reply_matching: no matching transaction exists\n"); nomatch2: DBG("DEBUG: t_reply_matching: failure to match a transaction\n"); *p_branch = -1; set_t(0); return -1; }
/* initiate a report if we previously enabled accounting for this t */ static inline void acc_onreply( struct cell* t, struct sip_msg *req, struct sip_msg *reply, int code) { str new_uri_bk; int br = -1; hdr_field_t *hdr; sip_msg_t tmsg; sip_msg_t *preq; /* acc_onreply is bound to TMCB_REPLY which may be called from _reply, like when FR hits; we should not miss this event for missed calls either */ if (is_invite(t) && code>=300 && is_mc_on(req) ) on_missed(t, req, reply, code); if (!should_acc_reply(req, reply, code)) return; if(_acc_clone_msg==1) { memcpy(&tmsg, req, sizeof(sip_msg_t)); preq = &tmsg; } else { preq = req; } /* get winning branch index, if set */ if (t->relayed_reply_branch>=0) { br = t->relayed_reply_branch; } else { if(code>=300) { br = tmb.t_get_picked_branch(); } } /* for reply processing, set as new_uri the one from selected branch */ if (br>=0) { new_uri_bk = preq->new_uri; preq->new_uri = t->uac[br].uri; preq->parsed_uri_ok = 0; } else { new_uri_bk.len = -1; new_uri_bk.s = 0; } /* set env variables */ env_set_to( get_rpl_to(t,reply) ); env_set_code_status( code, reply); if ( is_log_acc_on(preq) ) { env_set_text( ACC_ANSWERED, ACC_ANSWERED_LEN); acc_log_request(preq); } #ifdef SQL_ACC if (is_db_acc_on(preq)) { if(acc_db_set_table_name(preq, db_table_acc_data, &db_table_acc)<0) { LM_ERR("cannot set acc db table name\n"); } else { acc_db_request(preq); } } #endif #ifdef RAD_ACC if (is_rad_acc_on(preq)) acc_rad_request(preq); #endif /* DIAMETER */ #ifdef DIAM_ACC if (is_diam_acc_on(preq)) acc_diam_request(preq); #endif /* run extra acc engines */ acc_run_engines(preq, 0, NULL); if (new_uri_bk.len>=0) { req->new_uri = new_uri_bk; req->parsed_uri_ok = 0; } /* free header's parsed structures that were added by resolving acc attributes */ for( hdr=req->headers ; hdr ; hdr=hdr->next ) { if ( hdr->parsed && hdr_allocs_parse(hdr) && (hdr->parsed<(void*)t->uas.request || hdr->parsed>=(void*)t->uas.end_request)) { /* header parsed filed doesn't point inside uas.request memory * chunck -> it was added by resolving acc attributes -> free it as pkg */ DBG("removing hdr->parsed %d\n", hdr->type); clean_hdr_field(hdr); hdr->parsed = 0; } } }
static int _reply_light( struct cell *trans, char* buf, unsigned int len, unsigned int code, char *to_tag, unsigned int to_tag_len, int lock, struct bookmark *bm) { struct retr_buf *rb; unsigned int buf_len; branch_bm_t cancel_bitmap; str cb_s; if (!buf) { LM_DBG("response building failed\n"); /* determine if there are some branches to be canceled */ if ( is_invite(trans) ) { if (lock) LOCK_REPLIES( trans ); which_cancel(trans, &cancel_bitmap ); if (lock) UNLOCK_REPLIES( trans ); } /* and clean-up, including cancellations, if needed */ goto error; } cancel_bitmap=0; if (lock) LOCK_REPLIES( trans ); if ( is_invite(trans) ) which_cancel(trans, &cancel_bitmap ); if (trans->uas.status>=200) { LM_ERR("failed to generate %d reply when a final %d was sent out\n", code, trans->uas.status); goto error2; } rb = & trans->uas.response; rb->activ_type=code; trans->uas.status = code; buf_len = rb->buffer.s ? len : len + REPLY_OVERBUFFER_LEN; rb->buffer.s = (char*)shm_resize( rb->buffer.s, buf_len ); /* puts the reply's buffer to uas.response */ if (! rb->buffer.s ) { LM_ERR("failed to allocate shmem buffer\n"); goto error3; } update_local_tags(trans, bm, rb->buffer.s, buf); rb->buffer.len = len ; memcpy( rb->buffer.s , buf , len ); /* needs to be protected too because what timers are set depends on current transactions status */ /* t_update_timers_after_sending_reply( rb ); */ trans->relaied_reply_branch=-2; if (lock) UNLOCK_REPLIES( trans ); /* do UAC cleanup procedures in case we generated a final answer whereas there are pending UACs */ if (code>=200) { if ( is_local(trans) ) { LM_DBG("local transaction completed\n"); if ( has_tran_tmcbs(trans, TMCB_LOCAL_COMPLETED) ) { run_trans_callbacks( TMCB_LOCAL_COMPLETED, trans, 0, FAKED_REPLY, code); } } else { /* run the PRE send callbacks */ if ( has_tran_tmcbs(trans, TMCB_RESPONSE_PRE_OUT) ) { cb_s.s = buf; cb_s.len = len; set_extra_tmcb_params( &cb_s, &rb->dst); if (lock) run_trans_callbacks_locked( TMCB_RESPONSE_PRE_OUT, trans, trans->uas.request, FAKED_REPLY, code); else run_trans_callbacks( TMCB_RESPONSE_PRE_OUT, trans, trans->uas.request, FAKED_REPLY, code); } } if (!is_hopbyhop_cancel(trans)) { cleanup_uac_timers( trans ); if (is_invite(trans)) cancel_uacs( trans, cancel_bitmap ); /* for auth related replies, we do not do retransmission (via set_final_timer()), but only wait for a final reply (put_on_wait() ) - see RFC 3261 (26.3.2.4 DoS Protection) */ if ((code != 401) && (code != 407)) set_final_timer( trans ); else put_on_wait(trans); } } /* send it out : response.dst.send_sock is valid all the time now, * as it's taken from original request -bogdan */ if (!trans->uas.response.dst.send_sock) { LM_CRIT("send_sock is NULL\n"); } SEND_PR_BUFFER( rb, buf, len ); LM_DBG("reply sent out. buf=%p: %.9s..., " "shmem=%p: %.9s\n", buf, buf, rb->buffer.s, rb->buffer.s ); /* run the POST send callbacks */ if (code>=200&&!is_local(trans)&&has_tran_tmcbs(trans,TMCB_RESPONSE_OUT)) { cb_s.s = buf; cb_s.len = len; set_extra_tmcb_params( &cb_s, &rb->dst); if (lock) run_trans_callbacks_locked( TMCB_RESPONSE_OUT, trans, trans->uas.request, FAKED_REPLY, code); else run_trans_callbacks( TMCB_RESPONSE_OUT, trans, trans->uas.request, FAKED_REPLY, code); } pkg_free( buf ) ; stats_trans_rpl( code, 1 /*local*/ ); LM_DBG("finished\n"); return 1; error3: error2: if (lock) UNLOCK_REPLIES( trans ); pkg_free ( buf ); error: /* do UAC cleanup */ cleanup_uac_timers( trans ); if ( is_invite(trans) ) cancel_uacs( trans, cancel_bitmap ); /* we did not succeed -- put the transaction on wait */ put_on_wait(trans); return -1; }
/* initiate a report if we previously enabled accounting for this t */ static inline void acc_onreply( struct cell* t, struct sip_msg *req, struct sip_msg *reply, int code, acc_ctx_t* ctx) { str new_uri_bk; str dst_uri_bk; struct dlg_cell *dlg = NULL; str ctx_s; str table; unsigned long long* flags = &ctx->flags; /* acc_onreply is bound to TMCB_REPLY which may be called from _reply, like when FR hits; we should not miss this event for missed calls either */ if (is_invite(t) && code>=300 && is_mc_acc_on(*flags) ) { on_missed(t, req, reply, code, ctx); } if (!should_acc_reply(req, reply, code, flags)) return; /* for reply processing, set as new_uri the winning branch */ if (t->relaied_reply_branch>=0) { new_uri_bk = req->new_uri; dst_uri_bk = req->dst_uri; req->new_uri = t->uac[t->relaied_reply_branch].uri; req->dst_uri = t->uac[t->relaied_reply_branch].duri; req->parsed_uri_ok = 0; } else { new_uri_bk.len = dst_uri_bk.len = -1; new_uri_bk.s = dst_uri_bk.s = NULL; } /* set env variables */ env_set_to( get_rpl_to(t,reply) ); env_set_code_status( code, reply); /* search for table avp */ if (is_db_acc_on(ctx->flags)) table = ctx->acc_table; else { table.s = 0; table.len = 0; } if (is_invite(t) && !has_totag(req) && is_cdr_acc_on(ctx->flags) && code >= 200 && code < 300 && (dlg=dlg_api.get_dlg()) != NULL) { /* if dialog module loaded and INVITE and success reply */ if (store_core_leg_values(dlg, req) < 0) { LM_ERR("cannot store core and leg values\n"); return; } ctx_s.s = (char*)&ctx; ctx_s.len = sizeof(acc_ctx_t *); /* store context pointer into dialog */ if (dlg_api.store_dlg_value(dlg, &acc_ctx_str, &ctx_s) < 0) { LM_ERR("cannot store context pointer into dlg val!\n"); return; } /* report that flags shall be freed only by dialog module * tm must never free it */ set_dialog_context(*flags); /* register program shutdown callback * won't register free function since TERMINATED|EXPIRED callback * free function will be called to free */ if (dlg_api.register_dlgcb(dlg, DLGCB_DB_WRITE_VP, acc_dlg_onshutdown, ctx, NULL) != 0) { LM_ERR("cannot register callback for program shutdown!\n"); return; } /* register database callbacks */ if (dlg_api.register_dlgcb(dlg, DLGCB_TERMINATED|DLGCB_EXPIRED, acc_dlg_callback, ctx, dlg_free_acc_ctx) != 0) { LM_ERR("cannot register callback for database accounting\n"); return; } } else { /* do old accounting */ if ( is_evi_acc_on(*flags) ) { env_set_event(acc_event); acc_evi_request( req, reply, 0 ); } if ( is_log_acc_on(*flags) ) { env_set_text( ACC_ANSWERED, ACC_ANSWERED_LEN); acc_log_request( req, reply, 0 ); } if (is_aaa_acc_on(*flags)) acc_aaa_request( req, reply, 0 ); if (is_db_acc_on(*flags)) { env_set_text( table.s, table.len); acc_db_request( req, reply, &acc_ins_list, 0); } } if (new_uri_bk.len>=0) { req->new_uri = new_uri_bk; req->dst_uri = dst_uri_bk; req->parsed_uri_ok = 0; } }
inline static void final_response_handler( struct timer_link *fr_tl ) { #define CANCEL_REASON_SIP_480 \ "Reason: SIP;cause=480;text=\"NO_ANSWER\"" CRLF static context_p my_ctx = NULL; context_p old_ctx; struct retr_buf* r_buf; struct cell *t; if (fr_tl==0){ /* or BUG?, ignoring it for now */ LM_CRIT("final_response_handler(0) called\n"); return; } r_buf = get_fr_timer_payload(fr_tl); t=r_buf->my_T; # ifdef EXTRA_DEBUG if (t->damocles) { LM_ERR("transaction %p scheduled for deletion and" " called from FR timer\n",r_buf->my_T); abort(); } # endif reset_timer( &(r_buf->retr_timer) ); /* the transaction is already removed from FR_LIST by the timer */ /* FR for local cancels.... */ if (r_buf->activ_type==TYPE_LOCAL_CANCEL) { LM_DBG("stop retr for Local Cancel\n"); return; } /* FR for replies (negative INVITE replies) */ if (r_buf->activ_type>0) { # ifdef EXTRA_DEBUG if (t->uas.request->REQ_METHOD!=METHOD_INVITE || t->uas.status < 200 ) { LM_ERR("unknown type reply buffer\n"); abort(); } # endif put_on_wait( t ); return; }; /* as this processing is outside the scope of other messages (it is trigger from timer), a processing context must be attached to it */ old_ctx = current_processing_ctx; if (my_ctx==NULL) { my_ctx = context_alloc(CONTEXT_GLOBAL); if (my_ctx==NULL) { LM_ERR("failed to alloc new ctx in pkg\n"); } } memset( my_ctx, 0, context_size(CONTEXT_GLOBAL) ); current_processing_ctx = my_ctx; /* set the T context too */ set_t( t ); /* out-of-lock do the cancel I/O */ if (is_invite(t) && should_cancel_branch(t, r_buf->branch) ) { set_cancel_extra_hdrs( CANCEL_REASON_SIP_480, sizeof(CANCEL_REASON_SIP_480)-1); cancel_branch(t, r_buf->branch ); set_cancel_extra_hdrs( NULL, 0); } /* lock reply processing to determine how to proceed reliably */ LOCK_REPLIES( t ); LM_DBG("Cancel sent out, sending 408 (%p)\n", t); fake_reply(t, r_buf->branch, 408 ); /* flush the context */ if (current_processing_ctx==NULL) my_ctx=NULL; else context_destroy(CONTEXT_GLOBAL, my_ctx); /* switch back to the old context */ current_processing_ctx = old_ctx; /* reset the T context */ init_t(); LM_DBG("done\n"); }
/** * ac_reply: UAS transaction Reply action. It replies to an incoming request with a response. * @param the_as The App Server that sent this action. * @param action action * @param len length * * function description * * Returns: what */ int ac_reply(as_p the_as,char *action,int len) { unsigned int flags,hash_index,label; struct cell *c; struct sip_msg *my_msg; struct to_body *tb; str new_header,body,totag; char *ttag; int i,k,retval; static char headers[MAX_HEADER]; ttag=NULL; my_msg=NULL; i=k=0; net2hostL(flags,action,k); net2hostL(hash_index,action,k); net2hostL(label,action,k); if(seas_f.tmb.t_lookup_ident(&c,hash_index,label)<0){ LM_ERR("Failed to t_lookup_ident hash_idx=%d,label=%d\n",hash_index,label); goto error; } if(use_stats) action_stat(c); if(c->uas.status>=200){ LM_ERR("ac_reply: trying to reply to a \"%.*s\" transaction" "that is already in completed state\n",REQ_LINE(c->uas.request).method.len,REQ_LINE(c->uas.request).method.s); goto error; } if (!(my_msg=parse_ac_msg(HDR_EOH_F,action+k,len-k))) { LM_ERR("Failed to parse_ac_msg hash_idx=%d,label=%d\n",hash_index,label); goto error; } tb=(struct to_body*)my_msg->to->parsed; if(tb->tag_value.s && tb->tag_value.len){ totag=tb->tag_value; }else{ totag.s=NULL; totag.len=0; /*if(!(ttag=pkg_malloc(TOTAG_VALUE_LEN))){ LM_ERR("Out of memory !!!\n"); goto error; } totag.s=ttag; calc_crc_suffix(c->uas.request,seas_tag_suffix); LM_DBG("seas_tags = %.*s\n",TOTAG_VALUE_LEN,seas_tags); memcpy(totag.s,seas_tags,TOTAG_VALUE_LEN); totag.len=TOTAG_VALUE_LEN;*/ } LM_DBG("Using totag=[%.*s]\n",totag.len,totag.s); if(0>(i=recordroute_diff(c->uas.request,my_msg))){/*not likely..*/ LM_DBG("Seems that request had more RecordRoutes than response...\n"); goto error; }else LM_DBG("Recordroute Diff = %d\n",i); if(0>(i=extract_allowed_headers(my_msg,0,i,HDR_VIA_F|HDR_TO_F|HDR_FROM_F|HDR_CSEQ_F|HDR_CALLID_F|HDR_CONTENTLENGTH_F,headers,MAX_HEADER))){ LM_ERR("ac_reply() filtering headers !\n"); goto error; } headers[i]=0; new_header.s=headers; new_header.len=i; /* If it is INVITE and response is success (>=200 && <300), we mark it as local so that * SER does NOT retransmit the final response (by default, SER retransmit local UAS final * responses...*/ if(is_invite(c) && my_msg->first_line.u.reply.statuscode>=200 && my_msg->first_line.u.reply.statuscode<300) c->flags |= T_IS_LOCAL_FLAG; /*WARNING casting unsigned int to int*/ get_body(my_msg, &body); LM_DBG("Trying to construct a SipReply with: ReasonPhrase:[%.*s] body:[%.*s] headers:[%.*s] totag:[%.*s]\n",\ my_msg->first_line.u.reply.reason.len,my_msg->first_line.u.reply.reason.s,\ body.len,body.s,new_header.len,new_header.s,totag.len,totag.s); if(seas_f.tmb.t_reply_with_body(c,my_msg->first_line.u.reply.statuscode, &(my_msg->first_line.u.reply.reason),&body,&new_header,&totag)<0){ LM_ERR("Failed to t_reply\n"); goto error; } seas_f.tmb.unref_cell(c); retval=0; goto exit; error: retval = -1; seas_f.tmb.unref_cell(c); exit: if(ttag) pkg_free(ttag); if(my_msg){ free_sip_msg_lite(my_msg); pkg_free(my_msg); } return retval; }
/** * @return: * 0: success * -1: internal error * -2: insane call :) */ int ack_local_uac(struct cell *trans, str *hdrs, str *body) { struct retr_buf *local_ack, *old_lack; int ret; struct tmcb_params onsend_params; /* sanity checks */ #ifdef EXTRA_DEBUG if (! trans) { BUG("no transaction to ACK.\n"); abort(); } #endif #define RET_INVALID \ ret = -2; \ goto fin if (! is_local(trans)) { ERR("trying to ACK non local transaction (T@%p).\n", trans); RET_INVALID; } if (! is_invite(trans)) { ERR("trying to ACK non INVITE local transaction (T@%p).\n", trans); RET_INVALID; } if (! trans->uac[0].reply) { ERR("trying to ACK un-completed INVITE transaction (T@%p).\n", trans); RET_INVALID; } if (! (trans->flags & T_NO_AUTO_ACK)) { ERR("trying to ACK an auto-ACK transaction (T@%p).\n", trans); RET_INVALID; } if (trans->uac[0].local_ack) { ERR("trying to rebuild ACK retransmission buffer (T@%p).\n", trans); RET_INVALID; } /* looks sane: build the retransmission buffer */ if (! (local_ack = local_ack_rb(trans->uac[0].reply, trans, /*branch*/0, hdrs, body))) { ERR("failed to build ACK retransmission buffer"); RET_INVALID; } else { /* set the new buffer, but only if not already set (conc. invok.) */ if ((old_lack = (struct retr_buf *)atomic_cmpxchg_long( (void *)&trans->uac[0].local_ack, 0, (long)local_ack))) { /* buffer already set: deny current attempt */ ERR("concurrent ACKing for local INVITE detected (T@%p).\n",trans); free_local_ack(local_ack); RET_INVALID; } } if (msg_send(&local_ack->dst, local_ack->buffer, local_ack->buffer_len)<0){ /* hopefully will succeed on next 2xx retransmission */ ERR("failed to send local ACK (T@%p).\n", trans); ret = -1; goto fin; } else { INIT_TMCB_ONSEND_PARAMS(onsend_params, 0, 0, &trans->uac[0].request, &local_ack->dst, local_ack->buffer, local_ack->buffer_len, TMCB_LOCAL_F, 0 /* branch */, TYPE_LOCAL_ACK); run_trans_callbacks_off_params(TMCB_REQUEST_SENT, trans, &onsend_params); } ret = 0; fin: /* TODO: ugly! */ /* FIXME: the T had been obtain by t_lookup_ident()'ing for it, so, it is * ref-counted. The t_unref() can not be used, as it requests a valid SIP * message (all available might be the reply, but if AS goes wrong and * tries to ACK before the final reply is received, we still have to * lookup the T to find this out). */ UNREF( trans ); return ret; #undef RET_INVALID }
/* this is the code which decides what and when shall be relayed upstream; note well -- it assumes it is entered locked with REPLY_LOCK and it returns unlocked! */ enum rps relay_reply( struct cell *t, struct sip_msg *p_msg, int branch, unsigned int msg_status, branch_bm_t *cancel_bitmap ) { int relay; int save_clone; char *buf; /* length of outbound reply */ unsigned int res_len; int relayed_code; struct sip_msg *relayed_msg; struct bookmark bm; int totag_retr; enum rps reply_status; /* retransmission structure of outbound reply and request */ struct retr_buf *uas_rb; str cb_s; str text; /* keep compiler warnings about use of uninit vars silent */ res_len=0; buf=0; relayed_msg=0; relayed_code=0; totag_retr=0; /* remember, what was sent upstream to know whether we are * forwarding a first final reply or not */ /* *** store and relay message as needed *** */ reply_status = t_should_relay_response(t, msg_status, branch, &save_clone, &relay, cancel_bitmap, p_msg ); LM_DBG("branch=%d, save=%d, relay=%d\n", branch, save_clone, relay ); /* store the message if needed */ if (save_clone) /* save for later use, typically branch picking */ { if (!store_reply( t, branch, p_msg )) goto error01; } uas_rb = & t->uas.response; if (relay >= 0 ) { /* initialize sockets for outbound reply */ uas_rb->activ_type=msg_status; t->relaied_reply_branch = relay; /* try building the outbound reply from either the current * or a stored message */ relayed_msg = branch==relay ? p_msg : t->uac[relay].reply; if (relayed_msg==FAKED_REPLY) { relayed_code = branch==relay ? msg_status : t->uac[relay].last_received; text.s = error_text(relayed_code); text.len = strlen(text.s); /* FIXME - bogdan*/ if (relayed_code>=180 && t->uas.request->to && (get_to(t->uas.request)->tag_value.s==0 || get_to(t->uas.request)->tag_value.len==0)) { calc_crc_suffix( t->uas.request, tm_tag_suffix ); buf = build_res_buf_from_sip_req( relayed_code, &text, &tm_tag, t->uas.request, &res_len, &bm ); } else { buf = build_res_buf_from_sip_req( relayed_code, &text, 0/* no to-tag */, t->uas.request, &res_len, &bm ); } } else { /* run callbacks for all types of responses - * even if they are shmem-ed or not */ if (has_tran_tmcbs(t,TMCB_RESPONSE_FWDED) ) { run_trans_callbacks( TMCB_RESPONSE_FWDED, t, t->uas.request, relayed_msg, msg_status ); } relayed_code=relayed_msg->REPLY_STATUS; buf = build_res_buf_from_sip_res( relayed_msg, &res_len, uas_rb->dst.send_sock); /* remove all lumps which are not in shm * added either by build_res_buf_from_sip_res, or by * the callbacks that have been called with shmem-ed messages - vlad */ if (branch!=relay) { del_notflaged_lumps( &(relayed_msg->add_rm), LUMPFLAG_SHMEM); del_notflaged_lumps( &(relayed_msg->body_lumps), LUMPFLAG_SHMEM); } } if (!buf) { LM_ERR("no mem for outbound reply buffer\n"); goto error02; } /* attempt to copy the message to UAS's shmem: - copy to-tag for ACK matching as well - allocate little a bit more for provisional as larger messages are likely to follow and we will be able to reuse the memory frag */ uas_rb->buffer.s = (char*)shm_resize( uas_rb->buffer.s, res_len + (msg_status<200 ? REPLY_OVERBUFFER_LEN : 0)); if (!uas_rb->buffer.s) { LM_ERR("no more share memory\n"); goto error03; } uas_rb->buffer.len = res_len; memcpy( uas_rb->buffer.s, buf, res_len ); if (relayed_msg==FAKED_REPLY) { /* to-tags for local replies */ update_local_tags(t, &bm, uas_rb->buffer.s, buf); } stats_trans_rpl( relayed_code, (relayed_msg==FAKED_REPLY)?1:0 ); /* update the status ... */ t->uas.status = relayed_code; if (is_invite(t) && relayed_msg!=FAKED_REPLY && relayed_code>=200 && relayed_code < 300 && has_tran_tmcbs( t, TMCB_RESPONSE_OUT|TMCB_RESPONSE_PRE_OUT)) { totag_retr=update_totag_set(t, relayed_msg); } }; /* if relay ... */ UNLOCK_REPLIES( t ); /* Setup retransmission timer _before_ the reply is sent * to avoid race conditions */ if (reply_status == RPS_COMPLETED) { /* for auth related replies, we do not do retransmission (via set_final_timer()), but only wait for a final reply (put_on_wait() ) - see RFC 3261 (26.3.2.4 DoS Protection) */ if ((relayed_code != 401) && (relayed_code != 407)) set_final_timer(t); else put_on_wait(t); } /* send it now (from the private buffer) */ if (relay >= 0) { /* run the PRE sending out callback */ if (!totag_retr && has_tran_tmcbs(t, TMCB_RESPONSE_PRE_OUT) ) { cb_s.s = buf; cb_s.len = res_len; set_extra_tmcb_params( &cb_s, &uas_rb->dst); run_trans_callbacks_locked(TMCB_RESPONSE_PRE_OUT,t,t->uas.request, relayed_msg, relayed_code); } SEND_PR_BUFFER( uas_rb, buf, res_len ); LM_DBG("sent buf=%p: %.9s..., shmem=%p: %.9s\n", buf, buf, uas_rb->buffer.s, uas_rb->buffer.s ); /* run the POST sending out callback */ if (!totag_retr && has_tran_tmcbs(t, TMCB_RESPONSE_OUT) ) { cb_s.s = buf; cb_s.len = res_len; set_extra_tmcb_params( &cb_s, &uas_rb->dst); run_trans_callbacks_locked( TMCB_RESPONSE_OUT, t, t->uas.request, relayed_msg, relayed_code); } pkg_free( buf ); } /* success */ return reply_status; error03: pkg_free( buf ); error02: if (save_clone) { if (t->uac[branch].reply!=FAKED_REPLY) sip_msg_free( t->uac[branch].reply ); t->uac[branch].reply = NULL; } error01: text.s = "Reply processing error"; text.len = sizeof("Reply processing error")-1; t_reply_unsafe( t, t->uas.request, 500, &text ); UNLOCK_REPLIES(t); if (is_invite(t)) cancel_uacs( t, *cancel_bitmap ); /* a serious error occurred -- attempt to send an error reply; it will take care of clean-ups */ /* failure */ return RPS_ERROR; }
static void trace_onreq_out(struct cell* t, int type, struct tmcb_params *ps) { struct _siptrace_data sto; sip_msg_t *msg; struct ip_addr to_ip; int len; struct dest_info *dst; if(t==NULL || ps==NULL) { LM_DBG("very weird\n"); return; } if(ps->flags&TMCB_RETR_F) { LM_DBG("retransmission\n"); return; } msg=ps->req; if(msg==NULL) { /* check if it is outgoing cancel, t is INVITE * and send_buf starts with CANCEL */ if(is_invite(t) && ps->send_buf.len>7 && strncmp(ps->send_buf.s, "CANCEL ", 7)==0) { msg = t->uas.request; if(msg==NULL) { LM_DBG("no uas msg for INVITE transaction\n"); return; } else { LM_DBG("recording CANCEL based on INVITE transaction\n"); } } else { LM_DBG("no uas msg, local transaction\n"); return; } } memset(&sto, 0, sizeof(struct _siptrace_data)); if(traced_user_avp.n!=0) sto.avp=search_first_avp(traced_user_avp_type, traced_user_avp, &sto.avp_value, &sto.state); if((sto.avp==NULL) && trace_is_off(msg) ) { LM_DBG("trace off...\n"); return; } if(sip_trace_prepare(msg)<0) return; if(ps->send_buf.len>0) { sto.body = ps->send_buf; } else { sto.body.s = "No request buffer"; sto.body.len = sizeof("No request buffer")-1; } sto.callid = msg->callid->body; if(ps->send_buf.len>10) { sto.method.s = ps->send_buf.s; sto.method.len = 0; while(sto.method.len<ps->send_buf.len) { if(ps->send_buf.s[sto.method.len]==' ') break; sto.method.len++; } if(sto.method.len==ps->send_buf.len) sto.method.len = 10; } else { sto.method = t->method; } sto.status.s = ""; sto.status.len = 0; memset(&to_ip, 0, sizeof(struct ip_addr)); dst = ps->dst; if (trace_local_ip.s && trace_local_ip.len > 0) { sto.fromip = trace_local_ip; } else { if(dst==0 || dst->send_sock==0 || dst->send_sock->sock_str.s==0) { siptrace_copy_proto(msg->rcv.proto, sto.fromip_buff); strcat(sto.fromip_buff, ip_addr2a(&msg->rcv.dst_ip)); strcat(sto.fromip_buff,":"); strcat(sto.fromip_buff, int2str(msg->rcv.dst_port, NULL)); sto.fromip.s = sto.fromip_buff; sto.fromip.len = strlen(sto.fromip_buff); } else { sto.fromip = dst->send_sock->sock_str; } } if(dst==0) { sto.toip.s = "any:255.255.255.255"; sto.toip.len = 19; } else { su2ip_addr(&to_ip, &dst->to); siptrace_copy_proto(dst->proto, sto.toip_buff); strcat(sto.toip_buff, ip_addr2a(&to_ip)); strcat(sto.toip_buff, ":"); strcat(sto.toip_buff, int2str((unsigned long)su_getport(&dst->to), &len)); sto.toip.s = sto.toip_buff; sto.toip.len = strlen(sto.toip_buff); } sto.dir = "out"; sto.fromtag = get_from(msg)->tag_value; sto.totag = get_to(msg)->tag_value; #ifdef STATISTICS sto.stat = siptrace_req; #endif sip_trace_store(&sto, NULL); return; }
/* 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; }
/* Returns 0 - nothing found * 1 - T found */ int t_reply_matching( struct sip_msg *p_msg , int *p_branch ) { struct cell* p_cell; unsigned int hash_index = 0; unsigned int entry_label = 0; unsigned int branch_id = 0; char *hashi, *branchi, *p, *n; int hashl, branchl; int scan_space; struct cseq_body *cseq; char *loopi; int loopl; char *syni; int synl; /* make compiler warnings happy */ loopi=0; loopl=0; syni=0; synl=0; /* split the branch into pieces: loop_detection_check(ignored), hash_table_id, synonym_id, branch_id */ if (!(p_msg->via1 && p_msg->via1->branch && p_msg->via1->branch->value.s)) goto nomatch2; /* we do RFC 3261 tid matching and want to see first if there is * magic cookie in branch */ if (p_msg->via1->branch->value.len<=MCOOKIE_LEN) goto nomatch2; if (memcmp(p_msg->via1->branch->value.s, MCOOKIE, MCOOKIE_LEN)!=0) goto nomatch2; p=p_msg->via1->branch->value.s+MCOOKIE_LEN; scan_space=p_msg->via1->branch->value.len-MCOOKIE_LEN; /* hash_id */ n=eat_token2_end( p, p+scan_space, BRANCH_SEPARATOR); hashl=n-p; scan_space-=hashl; if (!hashl || scan_space<2 || *n!=BRANCH_SEPARATOR) goto nomatch2; hashi=p; p=n+1;scan_space--; if (!syn_branch) { /* md5 value */ n=eat_token2_end( p, p+scan_space, BRANCH_SEPARATOR ); loopl = n-p; scan_space-= loopl; if (n==p || scan_space<2 || *n!=BRANCH_SEPARATOR) goto nomatch2; loopi=p; p=n+1; scan_space--; } else { /* synonym id */ n=eat_token2_end( p, p+scan_space, BRANCH_SEPARATOR); synl=n-p; scan_space-=synl; if (!synl || scan_space<2 || *n!=BRANCH_SEPARATOR) goto nomatch2; syni=p; p=n+1;scan_space--; } /* branch id - should exceed the scan_space */ n=eat_token_end( p, p+scan_space ); branchl=n-p; if (!branchl ) goto nomatch2; branchi=p; /* sanity check */ if (reverse_hex2int(hashi, hashl, &hash_index)<0 ||hash_index>=TM_TABLE_ENTRIES || reverse_hex2int(branchi, branchl, &branch_id)<0 ||branch_id>=MAX_BRANCHES || (syn_branch ? reverse_hex2int(syni, synl, &entry_label)<0 : loopl!=MD5_LEN ) ) { LM_DBG("poor reply labels %u label %u branch %u\n", hash_index, entry_label, branch_id ); goto nomatch2; } LM_DBG("hash %u label %d branch %u\n",hash_index, entry_label, branch_id); cseq = get_cseq(p_msg); /* search the hash table list at entry 'hash_index'; lock the entry first */ LOCK_HASH(hash_index); for (p_cell = get_tm_table()->entrys[hash_index].first_cell; p_cell; p_cell=p_cell->next_cell) { /* first look if branch matches */ if (syn_branch) { if (p_cell->label != entry_label) continue; } else { if ( memcmp(p_cell->md5, loopi,MD5_LEN)!=0) continue; } /* sanity check ... too high branch ? */ if ( branch_id>=p_cell->nr_of_outgoings ) continue; /* does method match ? (remember -- CANCELs have the same branch as canceled transactions) */ if (!( /* it's a local cancel */ (cseq->method_id==METHOD_CANCEL && is_invite(p_cell) && p_cell->uac[branch_id].local_cancel.buffer.len ) /* method match */ || ((cseq->method_id!=METHOD_OTHER && p_cell->uas.request)? (cseq->method_id==REQ_LINE(p_cell->uas.request).method_value) :(EQ_STRS(cseq->method,p_cell->method))) )) continue; /* we passed all disqualifying factors .... the transaction has been matched ! */ set_t(p_cell); *p_branch = branch_id; REF_UNSAFE( T ); UNLOCK_HASH(hash_index); LM_DBG("reply matched (T=%p)!\n",T); /* if this is a 200 for INVITE, we will wish to store to-tags to be * able to distinguish retransmissions later and not to call * TMCB_RESPONSE_OUT uselessly; we do it only if callbacks are * enabled -- except callback customers, nobody cares about * retransmissions of multiple 200/INV or ACK/200s */ if (is_invite(p_cell) && p_msg->REPLY_STATUS>=200 && p_msg->REPLY_STATUS<300 && ( (!is_local(p_cell) && has_tran_tmcbs(p_cell, TMCB_RESPONSE_OUT|TMCB_RESPONSE_PRE_OUT) ) || (is_local(p_cell)&&has_tran_tmcbs(p_cell,TMCB_LOCAL_COMPLETED)) )) { if (parse_headers(p_msg, HDR_TO_F, 0)==-1) { LM_ERR("to parsing failed\n"); } } return 1; } /* for cycle */ /* nothing found */ UNLOCK_HASH(hash_index); LM_DBG("no matching transaction exists\n"); nomatch2: LM_DBG("failure to match a transaction\n"); *p_branch = -1; set_t(0); 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; }
/* initiate a report if we previously enabled accounting for this t */ static void acc_onreply(tm_cell_t *t, sip_msg_t *req, sip_msg_t *reply, int code) { str new_uri_bk; int br = -1; hdr_field_t *hdr; sip_msg_t *cmsg = 0; int cmsg_len = 0; sip_msg_t *preq = 0; void *mstart; void *mend; /* acc_onreply is bound to TMCB_REPLY which may be called from _reply, like when FR hits; we should not miss this event for missed calls either */ if (is_invite(t) && code>=300 && is_mc_on(req) ) on_missed(t, req, reply, code); if (!should_acc_reply(req, reply, code)) return; if(_acc_clone_msg==1) { /* make a clone so eventual new parsed headers in pkg are not visible * to other processes -- other attributes should be already parsed, * available in the req structure and propagated by cloning */ cmsg = sip_msg_shm_clone(req, &cmsg_len, 1); if(cmsg==NULL) { LM_ERR("failed to clone the request - acc aborted\n"); return; } mstart = cmsg; mend = ((char*)cmsg) + cmsg_len; preq = cmsg; } else { mstart = t->uas.request; mend = t->uas.end_request; preq = req; } /* get winning branch index, if set */ if (t->relayed_reply_branch>=0) { br = t->relayed_reply_branch; } else { if(code>=300) { br = tmb.t_get_picked_branch(); } } /* for reply processing, set as new_uri the one from selected branch */ if (br>=0) { new_uri_bk = preq->new_uri; preq->new_uri = t->uac[br].uri; preq->parsed_uri_ok = 0; } else { new_uri_bk.len = -1; new_uri_bk.s = 0; } /* set env variables */ env_set_to( get_rpl_to(t,reply) ); env_set_code_status( code, reply); if ( is_log_acc_on(preq) ) { env_set_text( ACC_ANSWERED, ACC_ANSWERED_LEN); acc_log_request(preq); } #ifdef SQL_ACC if (is_db_acc_on(preq)) { if(acc_db_set_table_name(preq, db_table_acc_data, &db_table_acc)<0) { LM_ERR("cannot set acc db table name\n"); } else { acc_db_request(preq); } } #endif /* DIAMETER */ #ifdef DIAM_ACC if (is_diam_acc_on(preq)) acc_diam_request(preq); #endif /* run extra acc engines */ acc_run_engines(preq, 0, NULL); if (new_uri_bk.len>=0) { preq->new_uri = new_uri_bk; preq->parsed_uri_ok = 0; } /* free header's parsed structures that were added by resolving acc attributes */ for( hdr=preq->headers ; hdr ; hdr=hdr->next ) { if (hdr->parsed && hdr_allocs_parse(hdr) && (hdr->parsed<mstart || hdr->parsed>=mend)) { /* header parsed filed doesn't point inside cloned request memory * chunck -> it was added by resolving acc attributes -> free it as pkg */ DBG("removing hdr->parsed %d\n", hdr->type); clean_hdr_field(hdr); hdr->parsed = 0; } } if(cmsg!=NULL) { shm_free(cmsg); } }