/* return 1 if a failure_route processes */ static inline int run_failure_handlers(struct cell *t) { static struct sip_msg faked_req; struct sip_msg *shmem_msg; struct ua_client *uac; int on_failure; shmem_msg = t->uas.request; uac = &t->uac[picked_branch]; /* failure_route for a local UAC? */ if (!shmem_msg || REQ_LINE(shmem_msg).method_value==METHOD_CANCEL ) { LM_WARN("no UAC or CANCEL support (%d, %d) \n", t->on_negative, t->tmcb_hl.reg_types); return 0; } /* don't start faking anything if we don't have to */ if ( !has_tran_tmcbs( t, TMCB_ON_FAILURE) && !t->on_negative ) { LM_WARN("no negative handler (%d, %d)\n",t->on_negative, t->tmcb_hl.reg_types); return 1; } if (!fake_req(&faked_req, shmem_msg, &t->uas, uac)) { LM_ERR("fake_req failed\n"); return 0; } /* fake also the env. conforming to the fake msg */ faked_env( t, &faked_req); /* DONE with faking ;-) -> run the failure handlers */ if ( has_tran_tmcbs( t, TMCB_ON_FAILURE) ) { run_trans_callbacks( TMCB_ON_FAILURE, t, &faked_req, uac->reply, uac->last_received); } if (t->on_negative) { /* update flags in transaction if changed by callbacks */ shmem_msg->flags = faked_req.flags; /* avoid recursion -- if failure_route forwards, and does not * set next failure route, failure_route will not be reentered * on failure */ on_failure = t->on_negative; t->on_negative=0; /* run a reply_route action if some was marked */ run_top_route(failure_rlist[on_failure].a, &faked_req); } /* restore original environment and free the fake msg */ faked_env( t, 0); free_faked_req(&faked_req,t); /* if failure handler changed flag, update transaction context */ shmem_msg->flags = faked_req.flags; /* branch flags do not need to be updated as this branch will be never * used again */ return 1; }
/* return (ticks_t)-1 on error/disable and 0 on success */ inline static ticks_t retransmission_handler(struct retr_buf *r_buf) { #ifdef EXTRA_DEBUG if(r_buf->my_T->flags & T_IN_AGONY) { LM_ERR("transaction %p scheduled for deletion and" " called from RETR timer (flags %x)\n", r_buf->my_T, r_buf->my_T->flags); abort(); } #endif if(r_buf->rbtype == TYPE_LOCAL_CANCEL || r_buf->rbtype == TYPE_REQUEST) { #ifdef EXTRA_DEBUG LM_DBG("request resending (t=%p, %.9s ... )\n", r_buf->my_T, r_buf->buffer); #endif if(SEND_BUFFER(r_buf) == -1) { /* disable retr. timers => return -1 */ fake_reply(r_buf->my_T, r_buf->branch, 503); return (ticks_t)-1; } if(unlikely(has_tran_tmcbs(r_buf->my_T, TMCB_REQUEST_SENT))) run_trans_callbacks_with_buf( TMCB_REQUEST_SENT, r_buf, 0, 0, TMCB_RETR_F); } else { #ifdef EXTRA_DEBUG LM_DBG("reply resending (t=%p, %.9s ... )\n", r_buf->my_T, r_buf->buffer); #endif t_retransmit_reply(r_buf->my_T); } return 0; }
static inline void send_prepared_request_impl(struct retr_buf *request, int retransmit) { if (SEND_BUFFER(request) == -1) { LOG(L_ERR, "t_uac: Attempt to send to precreated request failed\n"); } else if (unlikely(has_tran_tmcbs(request->my_T, TMCB_REQUEST_SENT))) /* we don't know the method here */ run_trans_callbacks_with_buf(TMCB_REQUEST_SENT, request, 0, 0, TMCB_LOCAL_F); if (retransmit && (start_retr(request)!=0)) LOG(L_CRIT, "BUG: t_uac: failed to start retr. for %p\n", request); }
void cancel_branch( struct cell *t, int branch ) { char *cancel; unsigned int len; struct retr_buf *crb, *irb; crb=&t->uac[branch].local_cancel; irb=&t->uac[branch].request; # ifdef EXTRA_DEBUG if (crb->buffer.s!=0 && crb->buffer.s!=BUSY_BUFFER) { LM_CRIT("attempt to rewrite cancel buffer failed\n"); abort(); } # endif cancel=build_cancel(t, branch, &len); if (!cancel) { LM_ERR("attempt to build a CANCEL failed\n"); return; } /* install cancel now */ crb->buffer.s=cancel; crb->buffer.len=len; crb->dst=irb->dst; crb->branch=branch; /* label it as cancel so that FR timer can better now how * to deal with it */ crb->activ_type=TYPE_LOCAL_CANCEL; if ( has_tran_tmcbs( t, TMCB_REQUEST_BUILT) ) { set_extra_tmcb_params( &crb->buffer, &crb->dst); run_trans_callbacks( TMCB_REQUEST_BUILT, t, t->uas.request,0, -t->uas.request->REQ_METHOD); } LM_DBG("sending cancel...\n"); SEND_BUFFER( crb ); /*sets and starts the FINAL RESPONSE timer */ start_retr( crb ); }
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; }
/* 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; }
/* 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; }
int t_append_branches(void) { struct cell *t = NULL; struct sip_msg *orig_msg = NULL; short outgoings; int success_branch; str current_uri; str dst_uri, path, instance, ruid, location_ua; struct socket_info* si; int q, i, found; flag_t backup_bflags = 0; flag_t bflags = 0; int new_branch, branch_ret, lowest_ret; branch_bm_t added_branches; int replies_locked = 0; t = get_t(); if(t == NULL) { LM_ERR("cannot get transaction\n"); return -1; } LM_DBG("transaction %u:%u in status %d\n", t->hash_index, t->label, t->uas.status); /* test if transaction has already been canceled */ if (t->flags & T_CANCELED) { ser_error=E_CANCELED; return -1; } if ((t->uas.status >= 200 && t->uas.status<=399) || ((t->uas.status >= 600 && t->uas.status) && !(t->flags & (T_6xx | T_DISABLE_6xx))) ) { LM_DBG("transaction %u:%u in status %d: cannot append new branch\n", t->hash_index, t->label, t->uas.status); return -1; } /* set the lock on the transaction here */ LOCK_REPLIES(t); replies_locked = 1; outgoings = t->nr_of_outgoings; orig_msg = t->uas.request; LM_DBG("Call %.*s: %d (%d) outgoing branches\n",orig_msg->callid->body.len, orig_msg->callid->body.s,outgoings, nr_branches); lowest_ret=E_UNSPEC; added_branches=0; /* it's a "late" branch so the on_branch variable has already been reset by previous execution of t_forward_nonack: we use the saved value */ if (t->on_branch_delayed) { /* tell add_uac that it should run branch route actions */ set_branch_route(t->on_branch_delayed); } outgoings = t->nr_of_outgoings; /* not really sure that the following is needed */ set_branch_iterator(nr_branches-1); found = 0; while((current_uri.s=next_branch( ¤t_uri.len, &q, &dst_uri, &path, &bflags, &si, &ruid, &instance, &location_ua))) { LM_DBG("Current uri %.*s\n",current_uri.len, current_uri.s); for (i=0; i<nr_branches; i++) { if (t->uac[i].ruid.len == ruid.len && !memcmp(t->uac[i].ruid.s, ruid.s, ruid.len)) { LM_DBG("branch already added [%.*s]\n", ruid.len, ruid.s); found = 1; break; } } if (found) continue; setbflagsval(0, bflags); new_branch=add_uac( t, orig_msg, ¤t_uri, (dst_uri.len) ? (&dst_uri) : ¤t_uri, &path, 0, si, orig_msg->fwd_send_flags, orig_msg->rcv.proto, (dst_uri.len)?-1:UAC_SKIP_BR_DST_F, &instance, &ruid, &location_ua); /* test if cancel was received meanwhile */ if (t->flags & T_CANCELED) goto canceled; if (new_branch>=0) added_branches |= 1<<new_branch; else lowest_ret=MIN_int(lowest_ret, new_branch); } clear_branches(); LM_DBG("Call %.*s: %d (%d) outgoing branches after clear_branches()\n", orig_msg->callid->body.len, orig_msg->callid->body.s,outgoings, nr_branches); setbflagsval(0, backup_bflags); /* update message flags, if changed in branch route */ t->uas.request->flags = orig_msg->flags; if (added_branches==0) { if(lowest_ret!=E_CFG) LOG(L_ERR, "ERROR: t_append_branch: failure to add branches\n"); ser_error=lowest_ret; replies_locked = 0; UNLOCK_REPLIES(t); return lowest_ret; } ser_error=0; /* clear branch adding errors */ /* send them out now */ success_branch=0; /* since t_append_branch can only be called from REQUEST_ROUTE, always lock replies */ for (i=outgoings; i<t->nr_of_outgoings; i++) { if (added_branches & (1<<i)) { branch_ret=t_send_branch(t, i, orig_msg , 0, 0 /* replies are already locked */ ); if (branch_ret>=0){ /* some kind of success */ if (branch_ret==i) { /* success */ success_branch++; if (unlikely(has_tran_tmcbs(t, TMCB_REQUEST_OUT))) run_trans_callbacks_with_buf( TMCB_REQUEST_OUT, &t->uac[nr_branches].request, orig_msg, 0, -orig_msg->REQ_METHOD); } else /* new branch added */ added_branches |= 1<<branch_ret; } } } if (success_branch<=0) { /* return always E_SEND for now * (the real reason could be: denied by onsend routes, blacklisted, * send failed or any of the errors listed before + dns failed * when attempting dns failover) */ ser_error=E_SEND; /* else return the last error (?) */ /* the caller should take care and delete the transaction */ replies_locked = 0; UNLOCK_REPLIES(t); return -1; } ser_error=0; /* clear branch send errors, we have overall success */ set_kr(REQ_FWDED); replies_locked = 0; UNLOCK_REPLIES(t); return 1; canceled: DBG("t_append_branches: cannot append branches to a canceled transaction\n"); /* reset processed branches */ clear_branches(); /* restore backup flags from initial env */ setbflagsval(0, backup_bflags); /* update message flags, if changed in branch route */ t->uas.request->flags = orig_msg->flags; /* if needed unlock transaction's replies */ if (likely(replies_locked)) { /* restore the number of outgoing branches * since new branches have not been completed */ t->nr_of_outgoings = outgoings; replies_locked = 0; UNLOCK_REPLIES(t); } ser_error=E_CANCELED; return -1; }
/* function returns: * 1 - forward successful * -1 - error during forward */ int t_forward_nonack( struct cell *t, struct sip_msg* p_msg , struct proxy_l * proxy, int reset_bcounter, int locked) { str reply_reason_487 = str_init("Request Terminated"); str backup_uri; str backup_dst; int branch_ret, lowest_ret; str current_uri; branch_bm_t added_branches; int i, q; struct cell *t_invite; int success_branch; str dst_uri; struct socket_info *bk_sock; unsigned int br_flags, bk_bflags; int idx; str path; str bk_path; /* make -Wall happy */ current_uri.s=0; /* before doing enything, update the t flags from msg */ t->uas.request->flags = p_msg->flags; if (p_msg->REQ_METHOD==METHOD_CANCEL) { t_invite=t_lookupOriginalT( p_msg ); if (t_invite!=T_NULL_CELL) { t_invite->flags |= T_WAS_CANCELLED_FLAG; cancel_invite( p_msg, t, t_invite, locked ); return 1; } } /* do not forward requests which were already cancelled*/ if (no_new_branches(t)) { LM_INFO("discarding fwd for a 6xx transaction\n"); ser_error = E_NO_DESTINATION; return -1; } if (was_cancelled(t)) { /* is this the first attempt of sending a branch out ? */ if (t->nr_of_outgoings==0) { /* if no other signalling was performed on the transaction * and the transaction was already canceled, better * internally generate the 487 reply here */ if (locked) t_reply_unsafe( t, p_msg , 487 , &reply_reason_487); else t_reply( t, p_msg , 487 , &reply_reason_487); } LM_INFO("discarding fwd for a cancelled transaction\n"); ser_error = E_NO_DESTINATION; return -1; } /* backup current uri, sock and flags... add_uac changes it */ backup_uri = p_msg->new_uri; backup_dst = p_msg->dst_uri; bk_sock = p_msg->force_send_socket; bk_path = p_msg->path_vec; bk_bflags = p_msg->ruri_bflags; /* advertised address/port are not changed */ /* check if the UAS retranmission port needs to be updated */ if ( (p_msg->msg_flags ^ t->uas.request->msg_flags) & FL_FORCE_RPORT ) su_setport( &t->uas.response.dst.to, p_msg->rcv.src_port ); /* if no more specific error code is known, use this */ lowest_ret=E_BUG; /* branches added */ added_branches=0; /* branch to begin with */ if (reset_bcounter) { t->first_branch=t->nr_of_outgoings; /* check if the previous branch is a PHONY one and if yes * keep it in the set of active branches; that means the * transaction had a t_wait_for_new_branches() call prior to relay() */ if ( t->first_branch>0 && (t->uac[t->first_branch-1].flags & T_UAC_IS_PHONY) ) t->first_branch--; } /* as first branch, use current uri */ current_uri = *GET_RURI(p_msg); branch_ret = add_uac( t, p_msg, ¤t_uri, &backup_dst, getb0flags(p_msg), &p_msg->path_vec, proxy); if (branch_ret>=0) added_branches |= 1<<branch_ret; else lowest_ret=branch_ret; /* ....and now add the remaining additional branches */ for( idx=0; (current_uri.s=get_branch( idx, ¤t_uri.len, &q, &dst_uri, &path, &br_flags, &p_msg->force_send_socket))!=0 ; idx++ ) { branch_ret = add_uac( t, p_msg, ¤t_uri, &dst_uri, br_flags, &path, proxy); /* pick some of the errors in case things go wrong; note that picking lowest error is just as good as any other algorithm which picks any other negative branch result */ if (branch_ret>=0) added_branches |= 1<<branch_ret; else lowest_ret=branch_ret; } /* consume processed branches */ clear_branches(); /* restore original stuff */ p_msg->new_uri=backup_uri; p_msg->parsed_uri_ok = 0;/* just to be sure; add_uac may parse other uris*/ p_msg->dst_uri = backup_dst; p_msg->force_send_socket = bk_sock; p_msg->path_vec = bk_path; p_msg->ruri_bflags = bk_bflags; /* update on_branch, _only_ if modified, otherwise it overwrites * whatever it is already in the transaction */ if (get_on_branch()) t->on_branch = get_on_branch(); /* update flags, if changed in branch route */ t->uas.request->flags = p_msg->flags; /* things went wrong ... no new branch has been fwd-ed at all */ if (added_branches==0) { LM_ERR("failure to add branches\n"); ser_error = lowest_ret; return lowest_ret; } /* send them out now */ success_branch=0; for (i=t->first_branch; i<t->nr_of_outgoings; i++) { if (added_branches & (1<<i)) { if (t->uac[i].br_flags & tcp_no_new_conn_bflag) tcp_no_new_conn = 1; do { if (check_blacklists( t->uac[i].request.dst.proto, &t->uac[i].request.dst.to, t->uac[i].request.buffer.s, t->uac[i].request.buffer.len)) { LM_DBG("blocked by blacklists\n"); ser_error=E_IP_BLOCKED; } else { run_trans_callbacks(TMCB_PRE_SEND_BUFFER, t, p_msg, 0, i); if (SEND_BUFFER( &t->uac[i].request)==0) { ser_error = 0; break; } LM_ERR("sending request failed\n"); ser_error=E_SEND; } /* get next dns entry */ if ( t->uac[i].proxy==0 || get_next_su( t->uac[i].proxy, &t->uac[i].request.dst.to, (ser_error==E_IP_BLOCKED)?0:1)!=0 ) break; t->uac[i].request.dst.proto = t->uac[i].proxy->proto; /* update branch */ if ( update_uac_dst( p_msg, &t->uac[i] )!=0) break; }while(1); tcp_no_new_conn = 0; if (ser_error) { shm_free(t->uac[i].request.buffer.s); t->uac[i].request.buffer.s = NULL; t->uac[i].request.buffer.len = 0; continue; } success_branch++; start_retr( &t->uac[i].request ); set_kr(REQ_FWDED); /* successfully sent out -> run callbacks */ if ( has_tran_tmcbs( t, TMCB_REQUEST_BUILT|TMCB_MSG_SENT_OUT) ) { set_extra_tmcb_params( &t->uac[i].request.buffer, &t->uac[i].request.dst); run_trans_callbacks( TMCB_REQUEST_BUILT|TMCB_MSG_SENT_OUT, t, p_msg, 0, 0); } } } return (success_branch>0)?1:-1; }
/* WARNING: doesn't work from failure route (deadlock, uses t_reply => tries * to get the reply lock again */ int t_relay_to( struct sip_msg *p_msg , struct proxy_l *proxy, int proto, int replicate) { int ret; int new_tran; /* struct hdr_field *hdr; */ struct cell *t; struct dest_info dst; unsigned short port; str host; short comp; #ifndef TM_DELAYED_REPLY int reply_ret; #endif ret=0; /* special case for CANCEL */ if ( p_msg->REQ_METHOD==METHOD_CANCEL){ ret=t_forward_cancel(p_msg, proxy, proto, &t); if (t) goto handle_ret; goto done; } new_tran = t_newtran( p_msg ); /* parsing error, memory alloc, whatever ... if via is bad and we are forced to reply there, return with 0 (->break), pass error status otherwise MMA: return value E_SCRIPT means that transaction was already started from the script so continue with that transaction */ if (likely(new_tran!=E_SCRIPT)) { if (new_tran<0) { ret = (ser_error==E_BAD_VIA && reply_to_via) ? 0 : new_tran; goto done; } /* if that was a retransmission, return we are happily done */ if (new_tran==0) { ret = 1; goto done; } }else if (unlikely(p_msg->REQ_METHOD==METHOD_ACK)) { /* transaction previously found (E_SCRIPT) and msg==ACK => ack to neg. reply or ack to local trans. => process it and exit */ /* FIXME: there's no way to distinguish here between acks to local trans. and neg. acks */ /* in normal operation we should never reach this point, if we do WARN(), it might hide some real bug (apart from possibly hiding a bug the most harm done is calling the TMCB_ACK_NEG callbacks twice) */ WARN("negative or local ACK caught, please report\n"); t=get_t(); if (unlikely(has_tran_tmcbs(t, TMCB_ACK_NEG_IN))) run_trans_callbacks(TMCB_ACK_NEG_IN, t, p_msg, 0, p_msg->REQ_METHOD); t_release_transaction(t); ret=1; goto done; } /* new transaction */ /* at this point if the msg is an ACK it is an e2e ACK and e2e ACKs do not establish a transaction and are fwd-ed statelessly */ if ( p_msg->REQ_METHOD==METHOD_ACK) { DBG( "SER: forwarding ACK statelessly \n"); if (proxy==0) { init_dest_info(&dst); dst.proto=proto; if (get_uri_send_info(GET_NEXT_HOP(p_msg), &host, &port, &dst.proto, &comp)!=0){ ret=E_BAD_ADDRESS; goto done; } #ifdef USE_COMP dst.comp=comp; #endif /* dst->send_sock not set, but forward_request will take care * of it */ ret=forward_request(p_msg, &host, port, &dst); } else { init_dest_info(&dst); dst.proto=get_proto(proto, proxy->proto); proxy2su(&dst.to, proxy); /* dst->send_sock not set, but forward_request will take care * of it */ ret=forward_request( p_msg , 0, 0, &dst) ; } goto done; } /* if replication flag is set, mark the transaction as local so that replies will not be relayed */ t=get_t(); if (replicate) t->flags|=T_IS_LOCAL_FLAG; /* INVITE processing might take long, particularly because of DNS look-ups -- let upstream know we're working on it */ if (p_msg->REQ_METHOD==METHOD_INVITE && (t->flags&T_AUTO_INV_100) && (t->uas.status < 100) ) { DBG( "SER: new INVITE\n"); if (!t_reply( t, p_msg , 100 , cfg_get(tm, tm_cfg, tm_auto_inv_100_r))) DBG("SER: ERROR: t_reply (100)\n"); } /* now go ahead and forward ... */ ret=t_forward_nonack(t, p_msg, proxy, proto); handle_ret: if (ret<=0) { DBG( "t_forward_nonack returned error %d (%d)\n", ret, ser_error); /* we don't want to pass upstream any reply regarding replicating * a request; replicated branch must stop at us*/ if (likely(!replicate)) { if(t->flags&T_DISABLE_INTERNAL_REPLY) { /* flag set to don't generate the internal negative reply * - let the transaction live further, processing should * continue in config */ DBG("not generating immediate reply for error %d\n", ser_error); tm_error=ser_error; ret = -4; goto done; } #ifdef TM_DELAYED_REPLY /* current error in tm_error */ tm_error=ser_error; set_kr(REQ_ERR_DELAYED); DBG("%d error reply generation delayed \n", ser_error); #else reply_ret=kill_transaction( t, ser_error ); if (reply_ret>0) { /* we have taken care of all -- do nothing in script */ DBG("ERROR: generation of a stateful reply " "on error succeeded\n"); /*ret=0; -- we don't want to stop the script */ } else { DBG("ERROR: generation of a stateful reply " "on error failed\n"); t_release_transaction(t); } #endif /* TM_DELAYED_REPLY */ }else{ t_release_transaction(t); /* kill it silently */ } } else { DBG( "SER: new transaction fwd'ed\n"); } done: return ret; }
/* 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; }
/* function returns: * 1 - forward successful * -1 - error during forward */ int t_forward_nonack( struct cell *t, struct sip_msg* p_msg , struct proxy_l * proxy) { str backup_uri; str backup_dst; int branch_ret, lowest_ret; str current_uri; branch_bm_t added_branches; int i, q; struct cell *t_invite; int success_branch; str dst_uri; struct socket_info *bk_sock; unsigned int br_flags; unsigned int bk_br_flags; int idx; str path; str bk_path; /* make -Wall happy */ current_uri.s=0; /* before doing enything, update the t falgs from msg */ t->uas.request->flags = p_msg->flags; if (p_msg->REQ_METHOD==METHOD_CANCEL) { t_invite=t_lookupOriginalT( p_msg ); if (t_invite!=T_NULL_CELL) { t_invite->flags |= T_WAS_CANCELLED_FLAG; cancel_invite( p_msg, t, t_invite ); return 1; } } /* do not forward requests which were already cancelled*/ if (was_cancelled(t) || no_new_branches(t)) { LM_ERR("discarding fwd for a cancelled/6xx transaction\n"); ser_error = E_NO_DESTINATION; return -1; } /* backup current uri, sock and flags... add_uac changes it */ backup_uri = p_msg->new_uri; backup_dst = p_msg->dst_uri; bk_sock = p_msg->force_send_socket; bk_br_flags = getb0flags(); bk_path = p_msg->path_vec; /* check if the UAS retranmission port needs to be updated */ if ( (p_msg->msg_flags ^ t->uas.request->msg_flags) & FL_FORCE_RPORT ) su_setport( &t->uas.response.dst.to, p_msg->rcv.src_port ); /* if no more specific error code is known, use this */ lowest_ret=E_BUG; /* branches added */ added_branches=0; /* branch to begin with */ t->first_branch=t->nr_of_outgoings; /* as first branch, use current uri */ current_uri = *GET_RURI(p_msg); branch_ret = add_uac( t, p_msg, ¤t_uri, &backup_dst, &p_msg->path_vec, proxy); if (branch_ret>=0) added_branches |= 1<<branch_ret; else lowest_ret=branch_ret; /* ....and now add the remaining additional branches */ for( idx=0; (current_uri.s=get_branch( idx, ¤t_uri.len, &q, &dst_uri, &path, &br_flags, &p_msg->force_send_socket))!=0 ; idx++ ) { setb0flags(br_flags); branch_ret = add_uac( t, p_msg, ¤t_uri, &dst_uri, &path, proxy); /* pick some of the errors in case things go wrong; note that picking lowest error is just as good as any other algorithm which picks any other negative branch result */ if (branch_ret>=0) added_branches |= 1<<branch_ret; else lowest_ret=branch_ret; } /* consume processed branches */ clear_branches(); /* restore original stuff */ p_msg->new_uri=backup_uri; p_msg->parsed_uri_ok = 0;/* just to be sure; add_uac may parse other uris*/ p_msg->dst_uri = backup_dst; p_msg->force_send_socket = bk_sock; p_msg->path_vec = bk_path; setb0flags(bk_br_flags); /* update on_branch, if modified */ t->on_branch = get_on_branch(); /* update flags, if changed in branch route */ t->uas.request->flags = p_msg->flags; /* things went wrong ... no new branch has been fwd-ed at all */ if (added_branches==0) { LM_ERR("failure to add branches\n"); ser_error = lowest_ret; return lowest_ret; } /* send them out now */ success_branch=0; for (i=t->first_branch; i<t->nr_of_outgoings; i++) { if (added_branches & (1<<i)) { #ifdef USE_TCP if (t->uac[i].br_flags & tcp_no_new_conn_bflag) tcp_no_new_conn = 1; #endif do { if (check_blacklists( t->uac[i].request.dst.proto, &t->uac[i].request.dst.to, t->uac[i].request.buffer.s, t->uac[i].request.buffer.len)) { LM_DBG("blocked by blacklists\n"); ser_error=E_IP_BLOCKED; } else { if (SEND_BUFFER( &t->uac[i].request)==0) { ser_error = 0; break; } LM_ERR("sending request failed\n"); ser_error=E_SEND; } /* get next dns entry */ if ( t->uac[i].proxy==0 || get_next_su( t->uac[i].proxy, &t->uac[i].request.dst.to, (ser_error==E_IP_BLOCKED)?0:1)!=0 ) break; t->uac[i].request.dst.proto = t->uac[i].proxy->proto; /* update branch */ if ( update_uac_dst( p_msg, &t->uac[i] )!=0) break; }while(1); #ifdef USE_TCP tcp_no_new_conn = 0; #endif if (ser_error) { shm_free(t->uac[i].request.buffer.s); t->uac[i].request.buffer.s = NULL; t->uac[i].request.buffer.len = 0; continue; } success_branch++; start_retr( &t->uac[i].request ); set_kr(REQ_FWDED); /* successfully sent out -> run callbacks */ if ( has_tran_tmcbs( t, TMCB_REQUEST_BUILT) ) { set_extra_tmcb_params( &t->uac[i].request.buffer, &t->uac[i].request.dst); run_trans_callbacks( TMCB_REQUEST_BUILT, t, p_msg,0, -p_msg->REQ_METHOD); } } } return (success_branch>0)?1:-1; }
/* 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; }
/* atomic "new_tran" construct; it returns: <0 on error +1 if a request did not match a transaction - it that was an ack, the calling function shall forward statelessly - otherwise it means, a new transaction was introduced and the calling function shall reply/relay/whatever_appropriate 0 on retransmission */ int t_newtran( struct sip_msg* p_msg ) { int lret, my_err; int canceled; /* is T still up-to-date ? */ DBG("DEBUG: t_newtran: msg id=%d , global msg id=%d ," " T on entrance=%p\n",p_msg->id,global_msg_id,T); if ( T && T!=T_UNDEFINED ) { /* ERROR message moved to w_t_newtran */ DBG("DEBUG: t_newtran: " "transaction already in process %p\n", T ); return E_SCRIPT; } global_msg_id = p_msg->id; T = T_UNDEFINED; /* first of all, parse everything -- we will store in shared memory and need to have all headers ready for generating potential replies later; parsing later on demand is not an option since the request will be in shmem and applying parse_headers to it would intermix shmem with pkg_mem */ if (parse_headers(p_msg, HDR_EOH_F, 0 )) { LOG(L_ERR, "ERROR: t_newtran: parse_headers failed\n"); return E_BAD_REQ; } if ((p_msg->parsed_flag & HDR_EOH_F)!=HDR_EOH_F) { LOG(L_ERR, "ERROR: t_newtran: EoH not parsed\n"); return E_OUT_OF_MEM; } /* t_lookup_requests attempts to find the transaction; it also calls check_transaction_quadruple -> it is safe to assume we have from/callid/cseq/to */ lret = t_lookup_request( p_msg, 1 /* leave locked if not found */, &canceled ); /* on error, pass the error in the stack ... nothing is locked yet if 0 is returned */ if (lret==0) return E_BAD_TUPEL; /* transaction found, it's a retransmission */ if (lret>0) { if (p_msg->REQ_METHOD==METHOD_ACK) { t_release_transaction(T); } else { t_retransmit_reply(T); } /* things are done -- return from script */ return 0; } /* from now on, be careful -- hash table is locked */ if (lret==-2) { /* was it an e2e ACK ? if so, trigger a callback */ /* no callbacks? complete quickly */ if ( !has_tran_tmcbs(t_ack,TMCB_E2EACK_IN) ) { UNLOCK_HASH(p_msg->hash_index); return 1; } REF_UNSAFE(t_ack); UNLOCK_HASH(p_msg->hash_index); /* we don't call from within REPLY_LOCK -- that introduces * a race condition; however, it is so unlikely and the * impact is so small (callback called multiple times of * multiple ACK/200s received in parallel), that we do not * better waste time in locks */ if (unmatched_totag(t_ack, p_msg)) { run_trans_callbacks( TMCB_E2EACK_IN , t_ack, p_msg, 0, -p_msg->REQ_METHOD ); } UNREF(t_ack); return 1; } /* transaction not found, it's a new request (lret<0, lret!=-2); establish a new transaction ... */ if (p_msg->REQ_METHOD==METHOD_ACK) { /* ... unless it is in ACK */ my_err=1; goto new_err; } my_err=new_t(p_msg); if (my_err<0) { LOG(L_ERR, "ERROR: t_newtran: new_t failed\n"); goto new_err; } if (canceled) T->flags|=T_CANCELED; /* mark it for future ref. */ UNLOCK_HASH(p_msg->hash_index); /* now, when the transaction state exists, check if there is a meaningful Via and calculate it; better do it now than later: state is established so that subsequent retransmissions will be absorbed and will not possibly block during Via DNS resolution; doing it later would only burn more CPU as if there is an error, we cannot relay later whatever comes out of the the transaction */ if (!init_rb( &T->uas.response, p_msg)) { LOG(L_ERR, "ERROR: t_newtran: unresolvable via1\n"); put_on_wait( T ); t_unref(p_msg); return E_BAD_VIA; } return 1; new_err: UNLOCK_HASH(p_msg->hash_index); return my_err; }
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); }
/* should be called directly only if one of the condition bellow is true: * - prepare_cancel_branch or prepare_to_cancel returned true for this branch * - buffer value was 0 and then set to BUSY in an atomic op.: * if (atomic_cmpxchg_long(&buffer, 0, BUSY_BUFFER)==0). * * params: t - transaction * branch - branch number to be canceled * reason - cancel reason structure * flags - howto cancel: * F_CANCEL_B_KILL - will completely stop the * branch (stops the timers), use with care * F_CANCEL_B_FAKE_REPLY - will send a fake 487 * to all branches that haven't received any response * (>=100). It assumes the REPLY_LOCK is not held * (if it is => deadlock) * F_CANCEL_B_FORCE_C - will send a cancel (and create the * corresp. local cancel rb) even if no reply was * received; F_CANCEL_B_FAKE_REPLY will be ignored. * F_CANCEL_B_FORCE_RETR - don't stop retransmission if no * reply was received on the branch; incompatible * with F_CANCEL_B_FAKE_REPLY, F_CANCEL_B_FORCE_C and * F_CANCEL_B_KILL (all of them take precedence) a * default: stop only the retransmissions for the branch * and leave it to timeout if it doesn't receive any * response to the CANCEL * returns: 0 - branch inactive after running cancel_branch() * 1 - branch still active (fr_timer) * -1 - error * WARNING: * - F_CANCEL_B_KILL should be used only if the transaction is killed * explicitly afterwards (since it might kill all the timers * the transaction won't be able to "kill" itself => if not * explicitly "put_on_wait" it might live forever) * - F_CANCEL_B_FAKE_REPLY must be used only if the REPLY_LOCK is not * held * - checking for buffer==0 under REPLY_LOCK is no enough, an * atomic_cmpxhcg or atomic_get_and_set _must_ be used. */ int cancel_branch( struct cell *t, int branch, #ifdef CANCEL_REASON_SUPPORT struct cancel_reason* reason, #endif /* CANCEL_REASON_SUPPORT */ int flags ) { char *cancel; unsigned int len; struct retr_buf *crb, *irb; int ret; struct cancel_info tmp_cd; void* pcbuf; crb=&t->uac[branch].local_cancel; irb=&t->uac[branch].request; irb->flags|=F_RB_CANCELED; ret=1; init_cancel_info(&tmp_cd); # ifdef EXTRA_DEBUG if (crb->buffer!=BUSY_BUFFER) { LOG(L_CRIT, "ERROR: attempt to rewrite cancel buffer: %p\n", crb->buffer); abort(); } # endif if (flags & F_CANCEL_B_KILL){ stop_rb_timers( irb ); ret=0; if ((t->uac[branch].last_received < 100) && !(flags & F_CANCEL_B_FORCE_C)) { DBG("DEBUG: cancel_branch: no response ever received: " "giving up on cancel\n"); /* remove BUSY_BUFFER -- mark cancel buffer as not used */ pcbuf=&crb->buffer; /* workaround for type punning warnings */ atomic_set_long(pcbuf, 0); /* try to relay auto-generated 487 canceling response only when * another one is not under relaying on the branch and there is * no forced response per transaction from script */ if((flags & F_CANCEL_B_FAKE_REPLY) && !(irb->flags&F_RB_RELAYREPLY) && !(t->flags&T_ADMIN_REPLY)) { LOCK_REPLIES(t); if (relay_reply(t, FAKED_REPLY, branch, 487, &tmp_cd, 1) == RPS_ERROR){ return -1; } } /* do nothing, hope that the caller will clean up */ return ret; } }else{ if (t->uac[branch].last_received < 100){ if (!(flags & F_CANCEL_B_FORCE_C)) { /* no response received => don't send a cancel on this branch, * just drop it */ if (!(flags & F_CANCEL_B_FORCE_RETR)) stop_rb_retr(irb); /* stop retransmissions */ /* remove BUSY_BUFFER -- mark cancel buffer as not used */ pcbuf=&crb->buffer; /* workaround for type punning warnings */ atomic_set_long(pcbuf, 0); if (flags & F_CANCEL_B_FAKE_REPLY){ stop_rb_timers( irb ); /* stop even the fr timer */ LOCK_REPLIES(t); if (relay_reply(t, FAKED_REPLY, branch, 487, &tmp_cd, 1)== RPS_ERROR){ return -1; } return 0; /* should be inactive after the 487 */ } /* do nothing, just wait for the final timeout */ return 1; } } stop_rb_retr(irb); /* stop retransmissions */ } if (cfg_get(tm, tm_cfg, reparse_invite) || (t->uas.request && t->uas.request->msg_flags&(FL_USE_UAC_FROM|FL_USE_UAC_TO))) { /* build the CANCEL from the INVITE which was sent out */ cancel = build_local_reparse(t, branch, &len, CANCEL, CANCEL_LEN, (t->uas.request && t->uas.request->msg_flags&FL_USE_UAC_TO)?0:&t->to #ifdef CANCEL_REASON_SUPPORT , reason #endif /* CANCEL_REASON_SUPPORT */ ); } else { /* build the CANCEL from the received INVITE */ cancel = build_local(t, branch, &len, CANCEL, CANCEL_LEN, &t->to #ifdef CANCEL_REASON_SUPPORT , reason #endif /* CANCEL_REASON_SUPPORT */ ); } if (!cancel) { LOG(L_ERR, "ERROR: attempt to build a CANCEL failed\n"); /* remove BUSY_BUFFER -- mark cancel buffer as not used */ pcbuf=&crb->buffer; /* workaround for type punning warnings */ atomic_set_long(pcbuf, 0); return -1; } /* install cancel now */ crb->dst = irb->dst; crb->branch = branch; /* label it as cancel so that FR timer can better know how to deal with it */ crb->activ_type = TYPE_LOCAL_CANCEL; /* be extra carefully and check for bugs (the below if could be replaced * by an atomic_set((void*)&crb->buffer, cancel) */ if (unlikely(atomic_cmpxchg_long((void*)&crb->buffer, (long)BUSY_BUFFER, (long)cancel)!= (long)BUSY_BUFFER)){ BUG("tm: cancel_branch: local_cancel buffer=%p != BUSY_BUFFER" " (trying to continue)\n", crb->buffer); shm_free(cancel); return -1; } membar_write_atomic_op(); /* cancel retr. can be called from reply_received w/o the reply lock held => they check for buffer_len to see if a valid reply exists */ crb->buffer_len = len; DBG("DEBUG: cancel_branch: sending cancel...\n"); if (SEND_BUFFER( crb )>=0){ if (unlikely (has_tran_tmcbs(t, TMCB_REQUEST_OUT))) run_trans_callbacks_with_buf(TMCB_REQUEST_OUT, crb, t->uas.request, 0, TMCB_LOCAL_F); if (unlikely (has_tran_tmcbs(t, TMCB_REQUEST_SENT))) run_trans_callbacks_with_buf(TMCB_REQUEST_SENT, crb, t->uas.request, 0, TMCB_LOCAL_F); } /*sets and starts the FINAL RESPONSE timer */ if (start_retr( crb )!=0) LOG(L_CRIT, "BUG: cancel_branch: failed to start retransmission" " for %p\n", crb); return ret; }
/* 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_lookup_request( struct sip_msg* p_msg , int leave_new_locked ) { struct cell *p_cell; unsigned int isACK; struct sip_msg *t_msg; struct via_param *branch; int match_status; isACK = p_msg->REQ_METHOD==METHOD_ACK; if (isACK) { if (e2eack_T==NULL) return -1; if (e2eack_T!=T_UNDEFINED) return -2; } /* parse all*/ if (check_transaction_quadruple(p_msg)==0) { LM_ERR("too few headers\n"); set_t(0); /* stop processing */ return 0; } /* start searching into the table */ if (!p_msg->hash_index) p_msg->hash_index=tm_hash( p_msg->callid->body , get_cseq(p_msg)->number ) ; LM_DBG("start searching: hash=%d, isACK=%d\n", p_msg->hash_index,isACK); /* first of all, look if there is RFC3261 magic cookie in branch; if * so, we can do very quick matching and skip the old-RFC bizzar * comparison of many header fields */ if (!p_msg->via1) { LM_ERR("no via\n"); set_t(0); return 0; } branch=p_msg->via1->branch; if (branch && branch->value.s && branch->value.len>MCOOKIE_LEN && memcmp(branch->value.s,MCOOKIE,MCOOKIE_LEN)==0) { /* huhuhu! the cookie is there -- let's proceed fast */ LOCK_HASH(p_msg->hash_index); match_status=matching_3261(p_msg,&p_cell, /* skip transactions with different method; otherwise CANCEL * would match the previous INVITE trans. */ isACK ? ~METHOD_INVITE: ~p_msg->REQ_METHOD); switch(match_status) { case 0: goto notfound; /* no match */ case 1: goto found; /* match */ case 2: goto e2e_ack; /* e2e proxy ACK */ } } /* ok -- it's ugly old-fashioned transaction matching -- it is * a bit simplified to be fast -- we don't do all the comparisons * of parsed uri, which was simply too bloated */ LM_DBG("proceeding to pre-RFC3261 transaction matching\n"); /* lock the whole entry*/ LOCK_HASH(p_msg->hash_index); /* all the transactions from the entry are compared */ for ( p_cell = get_tm_table()->entrys[p_msg->hash_index].first_cell; p_cell; p_cell = p_cell->next_cell ) { t_msg = p_cell->uas.request; if (!t_msg) continue; /* skip UAC transactions */ if (!isACK) { /* compare lengths first */ if (!EQ_LEN(callid)) continue; if (!EQ_LEN(cseq)) continue; if (!EQ_LEN(from)) continue; if (!EQ_LEN(to)) continue; if (ruri_matching && !EQ_REQ_URI_LEN) continue; if (via1_matching && !EQ_VIA_LEN(via1)) continue; /* length ok -- move on */ if (!EQ_STR(callid)) continue; if (!EQ_STR(cseq)) continue; if (!EQ_STR(from)) continue; if (!EQ_STR(to)) continue; if (ruri_matching && !EQ_REQ_URI_STR) continue; if (via1_matching && !EQ_VIA_STR(via1)) continue; /* request matched ! */ LM_DBG("non-ACK matched\n"); goto found; } else { /* it's an ACK request*/ /* ACK's relate only to INVITEs - and we look only for 2 types of * INVITEs : (a) negative INVITEs or (b) pozitive UAS INVITEs */ if ( t_msg->REQ_METHOD!=METHOD_INVITE || !(p_cell->uas.status>=300 || (p_cell->nr_of_outgoings==0 && p_cell->uas.status>=200)) ) continue; /* From|To URI , CallID, CSeq # must be always there */ /* compare lengths now */ if (!EQ_LEN(callid)) continue; /* CSeq only the number without method ! */ if (get_cseq(t_msg)->number.len!=get_cseq(p_msg)->number.len) continue; if (! EQ_LEN(from)) continue; /* To only the uri -- to many UACs screw up tags */ if (get_to(t_msg)->uri.len!=get_to(p_msg)->uri.len) continue; if (!EQ_STR(callid)) continue; if (memcmp(get_cseq(t_msg)->number.s, get_cseq(p_msg)->number.s, get_cseq(p_msg)->number.len)!=0) continue; if (!EQ_STR(from)) continue; if (memcmp(get_to(t_msg)->uri.s, get_to(p_msg)->uri.s, get_to(t_msg)->uri.len)!=0) continue; if (p_cell->uas.status<300) { /* it's a 2xx local UAS transaction */ if (dlg_matching(p_cell, p_msg)) goto e2e_ack; continue; } /* it is not an e2e ACK/200 -- perhaps it is * local negative case; in which case we will want * more elements to match: r-uri and via; allow * mismatching r-uri as an config option for broken * UACs */ if (ruri_matching && !EQ_REQ_URI_LEN ) continue; if (via1_matching && !EQ_VIA_LEN(via1)) continue; if (ruri_matching && !EQ_REQ_URI_STR) continue; if (via1_matching && !EQ_VIA_STR(via1)) continue; /* wow -- we survived all the check! we matched! */ LM_DBG("non-2xx ACK matched\n"); goto found; } /* ACK */ } /* synonym loop */ notfound: /* no transaction found */ set_t(0); e2eack_T = NULL; if (!leave_new_locked || isACK) { UNLOCK_HASH(p_msg->hash_index); } LM_DBG("no transaction found\n"); return -1; e2e_ack: REF_UNSAFE( p_cell ); UNLOCK_HASH(p_msg->hash_index); e2eack_T = p_cell; set_t(0); LM_DBG("e2e proxy ACK found\n"); return -2; found: set_t(p_cell); REF_UNSAFE( T ); set_kr(REQ_EXIST); UNLOCK_HASH( p_msg->hash_index ); LM_DBG("transaction found (T=%p)\n",T); if (has_tran_tmcbs( T, TMCB_MSG_MATCHED_IN) ) run_trans_callbacks( TMCB_MSG_MATCHED_IN, T, p_msg, 0,0); return 1; }