/* 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; }
char *print_uac_request( struct cell *t, struct sip_msg *i_req, int branch, str *uri, unsigned int *len, struct socket_info *send_sock, enum sip_protos proto ) { char *buf, *shbuf; shbuf=0; /* ... we calculate branch ... */ if (!t_calc_branch(t, branch, i_req->add_to_branch_s, &i_req->add_to_branch_len )) { LOG(L_ERR, "ERROR: print_uac_request: branch computation failed\n"); goto error01; } /* ... update uri ... */ i_req->new_uri=*uri; /* run the specific callbacks for this transaction */ run_trans_callbacks( TMCB_REQUEST_FWDED , t, i_req, 0, -i_req->REQ_METHOD); /* ... and build it now */ buf=build_req_buf_from_sip_req( i_req, len, send_sock, proto ); #ifdef DBG_MSG_QA if (buf[*len-1]==0) { LOG(L_ERR, "ERROR: print_uac_request: sanity check failed\n"); abort(); } #endif if (!buf) { LOG(L_ERR, "ERROR: print_uac_request: no pkg_mem\n"); ser_error=E_OUT_OF_MEM; goto error01; } /* clean Via's we created now -- they would accumulate for other branches and for shmem i_req they would mix up shmem with pkg_mem */ free_via_clen_lump(&i_req->add_rm); shbuf=(char *)shm_malloc(*len); if (!shbuf) { ser_error=E_OUT_OF_MEM; LOG(L_ERR, "ERROR: print_uac_request: no shmem\n"); goto error02; } memcpy( shbuf, buf, *len ); error02: pkg_free( buf ); error01: return shbuf; }
void run_trans_callbacks_locked( int type , struct cell *trans, struct sip_msg *req, struct sip_msg *rpl, int code ) { if (trans->tmcb_hl.first==0 || ((trans->tmcb_hl.reg_types)&type)==0 ) return; LOCK_REPLIES(trans); /* run callbacks */ run_trans_callbacks( type , trans, req, rpl, code ); /* SHM message cleanup */ if (trans->uas.request && trans->uas.request->msg_flags&FL_SHM_CLONE) clean_msg_clone( trans->uas.request, trans->uas.request, trans->uas.end_request); UNLOCK_REPLIES(trans); }
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 ); }
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; }
/* Returns 0 - nothing found * 1 - T found */ int t_reply_matching( struct sip_msg *p_msg , int *p_branch ) { struct cell* p_cell; int hash_index = 0; int entry_label = 0; 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 ((hash_index=reverse_hex2int(hashi, hashl))<0 ||hash_index>=TM_TABLE_ENTRIES || (branch_id=reverse_hex2int(branchi, branchl))<0 ||branch_id>=MAX_BRANCHES || (syn_branch ? (entry_label=reverse_hex2int(syni, synl))<0 : loopl!=MD5_LEN ) ) { LM_DBG("poor reply labels %d label %d branch %d\n", hash_index, entry_label, branch_id ); goto nomatch2; } LM_DBG("hash %d label %d branch %d\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"); } } if (!is_local(p_cell) && !(cseq->method_id==METHOD_CANCEL && is_invite(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); 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 "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; }
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; }
static inline int pre_print_uac_request( struct cell *t, int branch, struct sip_msg *request, struct sip_msg_body **body_clone) { int backup_route_type; struct usr_avp **backup_list; char *p; /* ... we calculate branch ... */ if (!t_calc_branch(t, branch, request->add_to_branch_s, &request->add_to_branch_len )) { LM_ERR("branch computation failed\n"); goto error; } /* from now on, flag all new lumps with LUMPFLAG_BRANCH flag in order to * be able to remove them later --bogdan */ set_init_lump_flags(LUMPFLAG_BRANCH); /* copy path vector into branch */ if (request->path_vec.len) { t->uac[branch].path_vec.s = shm_realloc(t->uac[branch].path_vec.s, request->path_vec.len+1); if (t->uac[branch].path_vec.s==NULL) { LM_ERR("shm_realloc failed\n"); goto error; } t->uac[branch].path_vec.len = request->path_vec.len; memcpy( t->uac[branch].path_vec.s, request->path_vec.s, request->path_vec.len+1); } /* do the same for the advertised port & address */ if (request->set_global_address.len) { t->uac[branch].adv_address.s = shm_realloc(t->uac[branch].adv_address.s, request->set_global_address.len+1); if (t->uac[branch].adv_address.s==NULL) { LM_ERR("shm_realloc failed for storing the advertised address " "(len=%d)\n",request->set_global_address.len); goto error; } t->uac[branch].adv_address.len = request->set_global_address.len; memcpy( t->uac[branch].adv_address.s, request->set_global_address.s, request->set_global_address.len+1); } if (request->set_global_port.len) { t->uac[branch].adv_port.s = shm_realloc(t->uac[branch].adv_port.s, request->set_global_port.len+1); if (t->uac[branch].adv_port.s==NULL) { LM_ERR("shm_realloc failed for storing the advertised port " "(len=%d)\n",request->set_global_port.len); goto error; } t->uac[branch].adv_port.len = request->set_global_port.len; memcpy( t->uac[branch].adv_port.s, request->set_global_port.s, request->set_global_port.len+1); } /********** run route & callback ************/ /* run branch route, if any; run it before RURI's DNS lookup * to allow to be changed --bogdan */ if (t->on_branch) { /* need to pkg_malloc the dst_uri */ if ( request->dst_uri.s && request->dst_uri.len>0 ) { if ( (p=pkg_malloc(request->dst_uri.len))==0 ) { LM_ERR("no more pkg mem\n"); ser_error=E_OUT_OF_MEM; goto error; } memcpy( p, request->dst_uri.s, request->dst_uri.len); request->dst_uri.s = p; } /* need to pkg_malloc the new_uri */ if ( (p=pkg_malloc(request->new_uri.len))==0 ) { LM_ERR("no more pkg mem\n"); ser_error=E_OUT_OF_MEM; goto error; } memcpy( p, request->new_uri.s, request->new_uri.len); request->new_uri.s = p; request->parsed_uri_ok = 0; /* make a clone of the original body, to restore it later */ if (clone_sip_msg_body( request, NULL, body_clone, 0)!=0) { LM_ERR("faile to clone the body, branch route changes will be" " preserved\n"); } /* make available the avp list from transaction */ backup_list = set_avp_list( &t->user_avps ); /* run branch route */ swap_route_type( backup_route_type, BRANCH_ROUTE); _tm_branch_index = branch; if(run_top_route(sroutes->branch[t->on_branch].a,request)&ACT_FL_DROP){ LM_DBG("dropping branch <%.*s>\n", request->new_uri.len, request->new_uri.s); _tm_branch_index = 0; /* restore the route type */ set_route_type( backup_route_type ); /* restore original avp list */ set_avp_list( backup_list ); goto error; } _tm_branch_index = 0; /* restore the route type */ set_route_type( backup_route_type ); /* restore original avp list */ set_avp_list( backup_list ); } /* run the specific callbacks for this transaction */ run_trans_callbacks( TMCB_REQUEST_FWDED, t, request, 0, -request->REQ_METHOD); /* copy dst_uri into branch (after branch route possible updated it) */ if (request->dst_uri.len) { t->uac[branch].duri.s = shm_realloc(t->uac[branch].duri.s, request->dst_uri.len); if (t->uac[branch].duri.s==NULL) { LM_ERR("shm_realloc failed\n"); goto error; } t->uac[branch].duri.len = request->dst_uri.len; memcpy( t->uac[branch].duri.s,request->dst_uri.s,request->dst_uri.len); } return 0; error: return -1; }
/* 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; }
/* 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; }
/* 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; }
static inline int pre_print_uac_request( struct cell *t, int branch, struct sip_msg *request) { int backup_route_type; struct usr_avp **backup_list; char *p; /* ... we calculate branch ... */ if (!t_calc_branch(t, branch, request->add_to_branch_s, &request->add_to_branch_len )) { LOG(L_ERR, "ERROR:pre_print_uac_request: branch computation failed\n"); goto error; } /* from now on, flag all new lumps with LUMPFLAG_BRANCH flag in order to * be able to remove them later --bogdan */ set_init_lump_flags(LUMPFLAG_BRANCH); /********** run route & callback ************/ /* run branch route, if any; run it before RURI's DNS lookup * to allow to be changed --bogdan */ if (t->on_branch) { /* need to pkg_malloc the dst_uri */ if ( request->dst_uri.len ) { if ( (p=pkg_malloc(request->dst_uri.len))==0 ) { LOG(L_ERR,"ERROR:tm:pre_print_uac_request: no more pkg mem\n"); ser_error=E_OUT_OF_MEM; goto error; } memcpy( p, request->dst_uri.s, request->dst_uri.len); request->dst_uri.s = p; } /* need to pkg_malloc the new_uri */ if ( (p=pkg_malloc(request->new_uri.len))==0 ) { LOG(L_ERR,"ERROR:tm:pre_print_uac_request: no more pkg mem\n"); ser_error=E_OUT_OF_MEM; goto error; } memcpy( p, request->new_uri.s, request->new_uri.len); request->new_uri.s = p; /* make available the avp list from transaction */ backup_list = set_avp_list( &t->user_avps ); /* run branch route */ swap_route_type( backup_route_type, BRANCH_ROUTE); if (run_actions(branch_rlist[t->on_branch], request)==0 && (action_flags&ACT_FL_DROP) ) { DBG("DEBUG:tm:pre_print_uac_request: dropping branch <%.*s>\n", request->dst_uri.len, request->dst_uri.s); goto error; } set_route_type( backup_route_type ); /* restore original avp list */ set_avp_list( backup_list ); } /* run the specific callbacks for this transaction */ run_trans_callbacks( TMCB_REQUEST_FWDED, t, request, 0, -request->REQ_METHOD); return 0; error: 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; }
/* function lookups transaction being canceled by CANCEL in p_msg; * it returns: * 0 - transaction wasn't found * T - transaction found */ struct cell* t_lookupOriginalT( struct sip_msg* p_msg ) { struct cell *p_cell; unsigned int hash_index; struct sip_msg *t_msg; struct via_param *branch; int ret; /* already looked for it? */ if (cancelled_T!=T_UNDEFINED) return cancelled_T; /* start searching in the table */ hash_index = p_msg->hash_index; LM_DBG("searching on hash entry %d\n",hash_index ); /* 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"); cancelled_T = NULL; 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(hash_index); ret=matching_3261(p_msg, &p_cell, /* we are seeking the original transaction -- * skip CANCEL transactions during search */ METHOD_CANCEL); if (ret==1) goto found; else goto notfound; } /* no cookies --proceed to old-fashioned pre-3261 t-matching */ LOCK_HASH(hash_index); /* all the transactions from the entry are compared */ for (p_cell=get_tm_table()->entrys[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 */ /* we don't cancel CANCELs ;-) */ if (t_msg->REQ_METHOD==METHOD_CANCEL) continue; /* check lengths now */ if (!EQ_LEN(callid)) continue; if (get_cseq(t_msg)->number.len!=get_cseq(p_msg)->number.len) continue; if (!EQ_LEN(from)) continue; #ifdef CANCEL_TAG if (!EQ_LEN(to)) continue; #else /* relaxed matching -- we don't care about to-tags anymore, * many broken UACs screw them up and ignoring them does not * actually hurt */ if (get_to(t_msg)->uri.len!=get_to(p_msg)->uri.len) continue; #endif if (ruri_matching && !EQ_REQ_URI_LEN) continue; if (via1_matching && !EQ_VIA_LEN(via1)) continue; /* check the content now */ 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; #ifdef CANCEL_TAG if (!EQ_STR(to)) continue; #else if (memcmp(get_to(t_msg)->uri.s, get_to(p_msg)->uri.s, get_to(t_msg)->uri.len)!=0) continue; #endif if (ruri_matching && !EQ_REQ_URI_STR) continue; if (via1_matching && !EQ_VIA_STR(via1)) continue; /* found */ goto found; } notfound: /* no transaction found */ LM_DBG("no CANCEL matching found! \n" ); UNLOCK_HASH(hash_index); cancelled_T = NULL; LM_DBG("t_lookupOriginalT completed\n"); return 0; found: LM_DBG("canceled transaction found (%p)! \n",p_cell ); cancelled_T = p_cell; REF_UNSAFE( p_cell ); UNLOCK_HASH(hash_index); /* run callback */ run_trans_callbacks( TMCB_TRANS_CANCELLED, cancelled_T, p_msg, 0,0); LM_DBG("t_lookupOriginalT completed\n"); return p_cell; }
/* 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; }