/* 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"); } if (retransmit && (start_retr(request)!=0)) LOG(L_CRIT, "BUG: t_uac: failed to start retr. for %p\n", request); }
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); }
inline static void retransmission_handler( struct timer_link *retr_tl ) { struct retr_buf* r_buf ; enum lists id; r_buf = get_retr_timer_payload(retr_tl); #ifdef EXTRA_DEBUG if (r_buf->my_T->damocles) { LM_ERR("transaction %p scheduled for deletion and" " called from RETR timer\n",r_buf->my_T); abort(); } #endif /* the transaction is already removed from RETRANSMISSION_LIST by timer*/ /* re-transmission */ if ( r_buf->activ_type==TYPE_LOCAL_CANCEL || r_buf->activ_type==TYPE_REQUEST ) { LM_DBG("retransmission_handler : request resending" " (t=%p, %.9s ... )\n", r_buf->my_T, r_buf->buffer.s); set_t(r_buf->my_T); SEND_BUFFER( r_buf ); /*if (SEND_BUFFER( r_buf )==-1) { reset_timer( &r_buf->fr_timer ); fake_reply(r_buf->my_T, r_buf->branch, 503 ); return; }*/ set_t(T_UNDEFINED); } else { LM_DBG("retransmission_handler : reply resending " "(t=%p, %.9s ... )\n", r_buf->my_T, r_buf->buffer.s); set_t(r_buf->my_T); t_retransmit_reply(r_buf->my_T); set_t(T_UNDEFINED); } id = r_buf->retr_list; r_buf->retr_list = id < RT_T2 ? id + 1 : RT_T2; retr_tl->timer_list= NULL; /* set to NULL so that set_timer will work */ set_timer( retr_tl, id < RT_T2 ? id + 1 : RT_T2, 0 ); LM_DBG("retransmission_handler : done\n"); }
void test_sporadic_refined_model__the_proc_the_sender_entrypoint_impl( BUFFER_ID_TYPE * cnx_p_out, EVENT_ID_TYPE * cnx_p_out_entrypoint_barrier) { RETURN_CODE_TYPE test_sporadic_refined_model__the_proc_the_sender_entrypoint_impl_the_proc_the_sender_runtime_call_ret; common_pkg__Integer test_sporadic_refined_model__the_proc_the_sender_entrypoint_impl_p_out_localVariable; MESSAGE_SIZE_TYPE test_sporadic_refined_model__the_proc_the_sender_entrypoint_impl_p_out_Length = sizeof(common_pkg__Integer); SYSTEM_TIME_TYPE test_sporadic_refined_model__the_proc_the_sender_entrypoint_impl_p_out_TimeOut = 0; test_sporadic_refined_model__the_proc_the_sender_behaviorIdentifier_enum whichPortActivated = test_sporadic_refined_model__the_proc_the_sender_behaviorIdentifier_enum_default_behavior; while(1) { switch(the_proc_the_sender_entrypoint_impl_current_state) { case test_sporadic_refined_model__the_proc_the_sender_entrypoint_impl_BA_entrypoint_init_state: // Transition id: which_behavior_default_mode if(1) // no execution condition { the_proc_the_sender_entrypoint_impl_current_state = test_sporadic_refined_model__the_proc_the_sender_entrypoint_impl_BA_entrypoint_exec_state; whichPortActivated = test_sporadic_refined_model__the_proc_the_sender_behaviorIdentifier_enum_default_behavior; break; } case test_sporadic_refined_model__the_proc_the_sender_entrypoint_impl_BA_entrypoint_wait_dispatch_state: // Transition id: dispatch_transition if(1) // no execution condition { the_proc_the_sender_entrypoint_impl_current_state = test_sporadic_refined_model__the_proc_the_sender_entrypoint_impl_BA_entrypoint_exec_state; PERIODIC_WAIT (&test_sporadic_refined_model__the_proc_the_sender_entrypoint_impl_the_proc_the_sender_runtime_call_ret); break; } case test_sporadic_refined_model__the_proc_the_sender_entrypoint_impl_BA_entrypoint_exec_state: // Transition id: call -- Priority 0 if(whichPortActivated == test_sporadic_refined_model__the_proc_the_sender_behaviorIdentifier_enum_default_behavior) { the_proc_the_sender_entrypoint_impl_current_state = test_sporadic_refined_model__the_proc_the_sender_entrypoint_impl_BA_entrypoint_wait_dispatch_state; send (&test_sporadic_refined_model__the_proc_the_sender_entrypoint_impl_p_out_localVariable); SEND_BUFFER ((*cnx_p_out), &test_sporadic_refined_model__the_proc_the_sender_entrypoint_impl_p_out_localVariable, test_sporadic_refined_model__the_proc_the_sender_entrypoint_impl_p_out_Length, test_sporadic_refined_model__the_proc_the_sender_entrypoint_impl_p_out_TimeOut, &test_sporadic_refined_model__the_proc_the_sender_entrypoint_impl_the_proc_the_sender_runtime_call_ret); SET_EVENT ((*cnx_p_out_entrypoint_barrier), &test_sporadic_refined_model__the_proc_the_sender_entrypoint_impl_the_proc_the_sender_runtime_call_ret); break; } } } }
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 ); }
/* 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; }
/** * This function cancels a previously created local invite * transaction. The cancel parameter should NOT have any via (CANCEL is * hop by hop). returns 0 if error return >0 if OK (returns the LABEL of * the cancel).*/ unsigned int t_uac_cancel( str *headers, str *body, unsigned int cancelled_hashIdx, unsigned int cancelled_label, transaction_cb cb, void* cbp) { struct cell *t_invite,*cancel_cell; struct retr_buf *cancel,*invite; unsigned int len,ret; char *buf; ret=0; if(t_lookup_ident(&t_invite,cancelled_hashIdx,cancelled_label)<0){ LM_ERR("failed to t_lookup_ident hash_idx=%d," "label=%d\n", cancelled_hashIdx,cancelled_label); return 0; } /* <sanity_checks> */ if(! is_local(t_invite)) { LM_ERR("tried to cancel a non-local transaction\n"); goto error3; } if(t_invite->uac[0].last_received < 100) { LM_WARN("trying to cancel a transaction not in " "Proceeding state !\n"); goto error3; } if(t_invite->uac[0].last_received > 200) { LM_WARN("trying to cancel a completed transaction !\n"); goto error3; } /* </sanity_checks*/ /* <build_cell> */ if(!(cancel_cell = build_cell(0))){ ret=0; LM_ERR("no more shm memory!\n"); goto error3; } reset_avps(); if(cb && insert_tmcb(&(cancel_cell->tmcb_hl), TMCB_RESPONSE_IN|TMCB_LOCAL_COMPLETED,cb,cbp,0)!=1){ ret=0; LM_ERR("short of tmcb shmem !\n"); goto error2; } /* </build_cell> */ /* <insert_into_hashtable> */ cancel_cell->flags |= T_IS_LOCAL_FLAG; cancel_cell->hash_index=t_invite->hash_index; LOCK_HASH(cancel_cell->hash_index); insert_into_hash_table_unsafe(cancel_cell,cancel_cell->hash_index); ret=cancel_cell->label; cancel_cell->label=t_invite->label; UNLOCK_HASH(cancel_cell->hash_index); /* </insert_into_hashtable> */ /* <prepare_cancel> */ cancel=&cancel_cell->uac[0].request; invite=&t_invite->uac[0].request; cancel->dst.to = invite->dst.to; cancel->dst.send_sock = invite->dst.send_sock; cancel->dst.proto = invite->dst.proto; //cancel->dst.proto_reserved1 = invite->dst.proto_reserved1; if(!(buf = build_uac_cancel(headers,body,t_invite,0,&len, &(cancel->dst)))){ ret=0; LM_ERR("attempt to build a CANCEL failed\n"); goto error1; } cancel->buffer=buf; cancel->buffer_len=len; cancel_cell->method.s = buf; cancel_cell->method.len = 6 /*c-a-n-c-e-l*/; /* </prepare_cancel> */ /* <strart_sending> */ cancel_cell->nr_of_outgoings++; if (SEND_BUFFER(cancel)==-1) { ret=0; LM_ERR("send failed\n"); goto error1; } start_retr(cancel); /* </start_sending> */ return ret; error1: LOCK_HASH(cancel_cell->hash_index); remove_from_hash_table_unsafe(cancel_cell); UNLOCK_HASH(cancel_cell->hash_index); error2: free_cell(cancel_cell); error3: return ret; }
/* 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; }
/* 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; }
void e2e_cancel( struct sip_msg *cancel_msg, struct cell *t_cancel, struct cell *t_invite ) { branch_bm_t cancel_bm, tmp_bm; int i; int lowest_error; str backup_uri; int ret; cancel_bm=0; lowest_error=0; backup_uri=cancel_msg->new_uri; /* determine which branches to cancel ... */ which_cancel( t_invite, &cancel_bm ); t_cancel->nr_of_outgoings=t_invite->nr_of_outgoings; /* fix label -- it must be same for reply matching */ t_cancel->label=t_invite->label; /* ... and install CANCEL UACs */ for (i=0; i<t_invite->nr_of_outgoings; i++) if (cancel_bm & (1<<i)) { ret=e2e_cancel_branch(cancel_msg, t_cancel, t_invite, i); if (ret<0) cancel_bm &= ~(1<<i); if (ret<lowest_error) lowest_error=ret; } cancel_msg->new_uri=backup_uri; /* send them out */ for (i=0; i<t_cancel->nr_of_outgoings; i++) { if (cancel_bm & (1<<i)) { /* Provisional reply received on this branch, send CANCEL */ /* No need to stop timers as they have already been stopped by the reply */ if (SEND_BUFFER( &t_cancel->uac[i].request)==-1) { LOG(L_ERR, "ERROR: e2e_cancel: send failed\n"); } start_retr( &t_cancel->uac[i].request ); } else { if (t_invite->uac[i].last_received < 100) { /* No provisional response received, stop * retransmission timers */ reset_timer(&t_invite->uac[i].request.retr_timer); reset_timer(&t_invite->uac[i].request.fr_timer); /* Generate faked reply */ LOCK_REPLIES(t_invite); if (relay_reply(t_invite, FAKED_REPLY, i, 487, &tmp_bm) == RPS_ERROR) { lowest_error = -1; } } } } /* if error occurred, let it know upstream (final reply will also move the transaction on wait state */ if (lowest_error<0) { LOG(L_ERR, "ERROR: cancel error\n"); t_reply( t_cancel, cancel_msg, 500, "cancel error"); /* if there are pending branches, let upstream know we are working on it */ } else if (cancel_bm) { DBG("DEBUG: e2e_cancel: e2e cancel proceeding\n"); t_reply( t_cancel, cancel_msg, 200, CANCELING ); /* if the transaction exists, but there is no more pending branch, tell upstream we're done */ } else { DBG("DEBUG: e2e_cancel: e2e cancel -- no more pending branches\n"); t_reply( t_cancel, cancel_msg, 200, CANCEL_DONE ); } #ifdef LOCAL_487 /* local 487s have been deprecated -- it better handles * race conditions (UAS sending 200); hopefully there are * no longer UACs who go crazy waiting for the 487 whose * forwarding is being blocked by other unresponsive branch */ /* we could await downstream UAS's 487 replies; however, if some of the branches does not do that, we could wait long time and annoy upstream UAC which wants to see a result of CANCEL quickly */ DBG("DEBUG: e2e_cancel: sending 487\n"); /* in case that something in the meantime has been sent upstream (like if FR hit at the same time), don't try to send */ if (t_invite->uas.status>=200) return; /* there is still a race-condition -- the FR can hit now; that's not too bad -- we take care in t_reply's REPLY_LOCK; in the worst case, both this t_reply and other replier will try, and the later one will result in error message "can't reply twice" */ t_reply(t_invite, t_invite->uas.request, 487, CANCELED ); #endif }
/* 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 proto) { str backup_uri; int branch_ret, lowest_ret; str current_uri; branch_bm_t added_branches; int first_branch; int i, q; struct cell *t_invite; int success_branch; int try_new; str dst_uri; /* make -Wall happy */ current_uri.s=0; set_kr(REQ_FWDED); if (p_msg->REQ_METHOD==METHOD_CANCEL) { t_invite=t_lookupOriginalT( p_msg ); if (t_invite!=T_NULL_CELL) { e2e_cancel( p_msg, t, t_invite ); UNREF(t_invite); return 1; } } /* backup current uri ... add_uac changes it */ backup_uri = p_msg->new_uri; /* if no more specific error code is known, use this */ lowest_ret=E_BUG; /* branches added */ added_branches=0; /* branch to begin with */ first_branch=t->nr_of_outgoings; /* on first-time forwarding, use current uri, later only what is in additional branches (which may be continuously refilled */ if (first_branch==0) { try_new=1; branch_ret=add_uac( t, p_msg, GET_RURI(p_msg), GET_NEXT_HOP(p_msg), proxy, proto ); if (branch_ret>=0) added_branches |= 1<<branch_ret; else lowest_ret=branch_ret; } else try_new=0; init_branch_iterator(); while((current_uri.s=next_branch( ¤t_uri.len, &q, &dst_uri.s, &dst_uri.len))) { try_new++; branch_ret=add_uac( t, p_msg, ¤t_uri, (dst_uri.len) ? (&dst_uri) : ¤t_uri, proxy, proto); /* 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 URI */ p_msg->new_uri=backup_uri; /* don't forget to clear all branches processed so far */ /* things went wrong ... no new branch has been fwd-ed at all */ if (added_branches==0) { if (try_new==0) { LOG(L_ERR, "ERROR: t_forward_nonack: no branched for forwarding\n"); return -1; } LOG(L_ERR, "ERROR: t_forward_nonack: failure to add branches\n"); return lowest_ret; } /* send them out now */ success_branch=0; for (i=first_branch; i<t->nr_of_outgoings; i++) { if (added_branches & (1<<i)) { if (SEND_BUFFER( &t->uac[i].request)==-1) { LOG(L_ERR, "ERROR: t_forward_nonack: sending request failed\n"); if (proxy) { proxy->errors++; proxy->ok=0; } } else { success_branch++; } start_retr( &t->uac[i].request ); } } if (success_branch<=0) { ser_error=E_SEND; return -1; } return 1; }
void e2e_cancel( struct sip_msg *cancel_msg, struct cell *t_cancel, struct cell *t_invite ) { branch_bm_t cancel_bm; branch_bm_t dummy_bm; int i; int lowest_error; str backup_uri; int rurib_flags; int ret; cancel_bm=0; lowest_error=0; /* e2e_cancel_branch() makes no RURI parsing, so no need to * save the ->parse_uri_ok */ backup_uri = cancel_msg->new_uri; /* branch flags specific to RURI */ rurib_flags = cancel_msg->flags&(~gflags_mask); /* determine which branches to cancel ... */ which_cancel( t_invite, &cancel_bm ); t_cancel->nr_of_outgoings=t_invite->nr_of_outgoings; t_cancel->first_branch=t_invite->first_branch; /* fix label -- it must be same for reply matching */ t_cancel->label=t_invite->label; /* ... and install CANCEL UACs */ for (i=t_invite->first_branch; i<t_invite->nr_of_outgoings; i++) { if (cancel_bm & (1<<i)) { ret=e2e_cancel_branch(cancel_msg, t_cancel, t_invite, i); if (ret<0) cancel_bm &= ~(1<<i); if (ret<lowest_error) lowest_error=ret; } } /* restore new_uri */ cancel_msg->new_uri = backup_uri; cancel_msg->parsed_uri_ok = 0; /* set flags */ cancel_msg->flags = (cancel_msg->flags&gflags_mask)|rurib_flags; t_cancel->uas.request->flags = cancel_msg->flags&gflags_mask; /* send them out */ for (i=t_cancel->first_branch; i<t_cancel->nr_of_outgoings; i++) { if (cancel_bm & (1<<i)) { if (SEND_BUFFER( &t_cancel->uac[i].request)==-1) { LOG(L_ERR, "ERROR: e2e_cancel: send failed\n"); } start_retr( &t_cancel->uac[i].request ); } } /* internally cancel branches with no received reply */ for (i=t_invite->first_branch; i<t_invite->nr_of_outgoings; i++) { if (t_invite->uac[i].last_received==0){ /* mark as cancelled */ t_invite->uac[i].flags |= T_UAC_TO_CANCEL_FLAG; /* reset the "request" timers */ reset_timer(&t_invite->uac[i].request.retr_timer); reset_timer(&t_invite->uac[i].request.fr_timer); LOCK_REPLIES( t_invite ); if (RPS_ERROR==relay_reply(t_invite,FAKED_REPLY,i,487,&dummy_bm)) lowest_error = -1; /* force sending 500 error */ } } /* do not attmpt to send reply for CANCEL if we already did it once; * to work arround the race between receiveing reply and generating * local reply, we better check if we are in failure route (which means that * the reply to UAC is /to be/ sent) or if was actually sent out */ /* calling here t_relay from within failure route will lead to dead lock * on the transaction's reply lock -bogdan */ if (route_type==FAILURE_ROUTE || t_cancel->uas.status>=200) return; /* if error occurred, let it know upstream (final reply will also move the transaction on wait state */ if (lowest_error<0) { LOG(L_ERR, "ERROR: cancel error\n"); t_reply( t_cancel, cancel_msg, 500, "cancel error"); /* if there are pending branches, let upstream know we are working on it */ } else if (cancel_bm) { DBG("DEBUG: e2e_cancel: e2e cancel proceeding\n"); t_reply( t_cancel, cancel_msg, 200, CANCELING ); /* if the transaction exists, but there is no more pending branch, tell upstream we're done */ } else { DBG("DEBUG: e2e_cancel: e2e cancel -- no more pending branches\n"); t_reply( t_cancel, cancel_msg, 200, CANCEL_DONE ); } #ifdef LOCAL_487 /* local 487s have been deprecated -- it better handles * race conditions (UAS sending 200); hopefully there are * no longer UACs who go crazy waiting for the 487 whose * forwarding is being blocked by other unresponsive branch */ /* we could await downstream UAS's 487 replies; however, if some of the branches does not do that, we could wait long time and annoy upstream UAC which wants to see a result of CANCEL quickly */ DBG("DEBUG: e2e_cancel: sending 487\n"); /* in case that something in the meantime has been sent upstream (like if FR hit at the same time), don't try to send */ if (t_invite->uas.status>=200) return; /* there is still a race-condition -- the FR can hit now; that's not too bad -- we take care in t_reply's REPLY_LOCK; in the worst case, both this t_reply and other replier will try, and the later one will result in error message "can't reply twice" */ t_reply(t_invite, t_invite->uas.request, 487, CANCELED ); #endif }
/* * Send a request using data from the dialog structure */ int t_uac(str* method, str* headers, str* body, dlg_t* dialog, transaction_cb cb, void* cbp,release_tmcb_param release_func) { union sockaddr_union to_su, new_to_su; struct cell *new_cell; struct cell *backup_cell; struct retr_buf *request; static struct sip_msg *req; struct usr_avp **backup; char *buf, *buf1; int buf_len, buf_len1; int ret, flags, sflag_bk; int backup_route_type; int sip_msg_len; unsigned int hi; struct socket_info *new_send_sock; str h_to, h_from, h_cseq, h_callid; struct proxy_l *proxy, *new_proxy; unsigned short dst_changed; ret=-1; /*** added by dcm * - needed by external ua to send a request within a dlg */ if(!dialog->hooks.next_hop && w_calculate_hooks(dialog)<0) goto error3; if(dialog->obp.s) dialog->hooks.next_hop = &dialog->obp; LM_DBG("next_hop=<%.*s>\n",dialog->hooks.next_hop->len, dialog->hooks.next_hop->s); /* calculate the socket corresponding to next hop */ proxy = uri2proxy( dialog->hooks.next_hop, dialog->send_sock ? dialog->send_sock->proto : PROTO_NONE ); if (proxy==0) { ret=E_BAD_ADDRESS; goto error3; } /* use the first address */ hostent2su( &to_su, &proxy->host, proxy->addr_idx, proxy->port ? proxy->port:SIP_PORT); /* check/discover the send socket */ if (dialog->send_sock) { /* if already set, the protocol of send sock must have the the same type as the proto required by destination URI */ if (proxy->proto != dialog->send_sock->proto) dialog->send_sock = NULL; } if (dialog->send_sock==NULL) { /* get the send socket */ dialog->send_sock = get_send_socket( NULL/*msg*/, &to_su, proxy->proto); if (!dialog->send_sock) { LM_ERR("no corresponding socket for af %d\n", to_su.s.sa_family); ser_error = E_NO_SOCKET; goto error2; } } LM_DBG("sending socket is %.*s \n", dialog->send_sock->name.len,dialog->send_sock->name.s); /* ***** Create TRANSACTION and all related ***** */ new_cell = build_cell( NULL/*msg*/, 1/*full UAS clone*/); if (!new_cell) { ret=E_OUT_OF_MEM; LM_ERR("short of cell shmem\n"); goto error2; } /* pass the transaction flags from dialog to transaction */ new_cell->flags |= dialog->T_flags; /* add the callback the transaction for LOCAL_COMPLETED event */ flags = TMCB_LOCAL_COMPLETED; /* Add also TMCB_LOCAL_RESPONSE_OUT if provisional replies are desired */ if (pass_provisional_replies || pass_provisional(new_cell)) flags |= TMCB_LOCAL_RESPONSE_OUT; if(cb && insert_tmcb(&(new_cell->tmcb_hl),flags,cb,cbp,release_func)!=1){ ret=E_OUT_OF_MEM; LM_ERR("short of tmcb shmem\n"); goto error2; } if (method->len==INVITE_LEN && memcmp(method->s, INVITE, INVITE_LEN)==0) new_cell->flags |= T_IS_INVITE_FLAG; new_cell->flags |= T_IS_LOCAL_FLAG; request = &new_cell->uac[0].request; if (dialog->forced_to_su.s.sa_family == AF_UNSPEC) request->dst.to = to_su; else request->dst.to = dialog->forced_to_su; request->dst.send_sock = dialog->send_sock; request->dst.proto = dialog->send_sock->proto; request->dst.proto_reserved1 = 0; hi=dlg2hash(dialog); LOCK_HASH(hi); insert_into_hash_table_unsafe(new_cell, hi); UNLOCK_HASH(hi); /* copy AVPs into transaction */ new_cell->user_avps = dialog->avps; dialog->avps = NULL; /* ***** Create the message buffer ***** */ buf = build_uac_req(method, headers, body, dialog, 0, new_cell, &buf_len); if (!buf) { LM_ERR("failed to build message\n"); ret=E_OUT_OF_MEM; goto error1; } if (local_rlist.a) { LM_DBG("building sip_msg from buffer\n"); req = buf_to_sip_msg(buf, buf_len, dialog); if (req==NULL) { LM_ERR("failed to build sip_msg from buffer\n"); } else { /* set this transaction as active one */ backup_cell = get_t(); set_t( new_cell ); /* set transaction AVP list */ backup = set_avp_list( &new_cell->user_avps ); /* backup script flags */ sflag_bk = getsflags(); /* disable parallel forking */ set_dset_state( 0 /*disable*/); /* run the route */ swap_route_type( backup_route_type, LOCAL_ROUTE); run_top_route( local_rlist.a, req); set_route_type( backup_route_type ); /* transfer current message context back to t */ new_cell->uac[0].br_flags = getb0flags(req); /* restore the prevoius active transaction */ set_t( backup_cell ); set_dset_state( 1 /*enable*/); setsflagsval(sflag_bk); set_avp_list( backup ); /* check for changes - if none, do not regenerate the buffer */ dst_changed = 1; if (req->new_uri.s || req->force_send_socket!=dialog->send_sock || req->dst_uri.len != dialog->hooks.next_hop->len || memcmp(req->dst_uri.s,dialog->hooks.next_hop->s,req->dst_uri.len) || (dst_changed=0)==0 || req->add_rm || req->body_lumps){ new_send_sock = NULL; /* do we also need to change the destination? */ if (dst_changed) { /* calculate the socket corresponding to next hop */ new_proxy = uri2proxy( req->dst_uri.s ? &(req->dst_uri) : &req->new_uri, PROTO_NONE ); if (new_proxy==0) goto abort_update; /* use the first address */ hostent2su( &new_to_su, &new_proxy->host, new_proxy->addr_idx, new_proxy->port ? new_proxy->port:SIP_PORT); /* get the send socket */ new_send_sock = get_send_socket( req, &new_to_su, new_proxy->proto); if (!new_send_sock) { free_proxy( new_proxy ); pkg_free( new_proxy ); LM_ERR("no socket found for the new destination\n"); goto abort_update; } } /* if interface change, we need to re-build the via */ if (new_send_sock && new_send_sock != dialog->send_sock) { LM_DBG("Interface change in local route -> " "rebuilding via\n"); if (!del_lump(req,req->h_via1->name.s - req->buf, req->h_via1->len,0)) { LM_ERR("Failed to remove initial via \n"); goto abort_update; } memcpy(req->add_to_branch_s,req->via1->branch->value.s, req->via1->branch->value.len); req->add_to_branch_len = req->via1->branch->value.len; /* update also info about new destination and send sock */ dialog->send_sock = new_send_sock; free_proxy( proxy ); pkg_free( proxy ); proxy = new_proxy; request->dst.send_sock = new_send_sock; request->dst.proto = new_send_sock->proto; request->dst.proto_reserved1 = 0; /* build the shm buffer now */ set_init_lump_flags(LUMPFLAG_BRANCH); buf1 = build_req_buf_from_sip_req(req, (unsigned int*)&buf_len1, new_send_sock, new_send_sock->proto, MSG_TRANS_SHM_FLAG); reset_init_lump_flags(); del_flaged_lumps( &req->add_rm, LUMPFLAG_BRANCH); } else { LM_DBG("Change in local route -> rebuilding buffer\n"); /* build the shm buffer now */ buf1 = build_req_buf_from_sip_req(req, (unsigned int*)&buf_len1, dialog->send_sock, dialog->send_sock->proto, MSG_TRANS_SHM_FLAG|MSG_TRANS_NOVIA_FLAG); /* now as it used, hide the original VIA header */ del_lump(req,req->h_via1->name.s - req->buf, req->h_via1->len, 0); } if (!buf1) { LM_ERR("no more shm mem\n"); /* keep original buffer */ goto abort_update; } /* update shortcuts */ if(!req->add_rm && !req->new_uri.s) { /* headers are not affected, simply tranlate */ new_cell->from.s = new_cell->from.s - buf + buf1; new_cell->to.s = new_cell->to.s - buf + buf1; new_cell->callid.s = new_cell->callid.s - buf + buf1; new_cell->cseq_n.s = new_cell->cseq_n.s - buf + buf1; } else { /* use heavy artilery :D */ if (extract_ftc_hdrs( buf1, buf_len1, &h_from, &h_to, &h_cseq, &h_callid)!=0 ) { LM_ERR("failed to update shortcut pointers\n"); shm_free(buf1); goto abort_update; } new_cell->from = h_from; new_cell->to = h_to; new_cell->callid = h_callid; new_cell->cseq_n = h_cseq; } /* here we rely on how build_uac_req() builds the first line */ new_cell->uac[0].uri.s = buf1 + req->first_line.u.request.method.len + 1; new_cell->uac[0].uri.len = GET_RURI(req)->len; /* update also info about new destination and send sock */ if (new_send_sock) request->dst.to = new_to_su; shm_free(buf); buf = buf1; buf_len = buf_len1; /* use new buffer */ } else { /* no changes over the message, buffer is already generated, just hide the original VIA for potential further branches */ del_lump(req,req->h_via1->name.s-req->buf,req->h_via1->len,0); } abort_update: /* save the SIP message into transaction */ new_cell->uas.request = sip_msg_cloner( req, &sip_msg_len, 1); if (new_cell->uas.request==NULL) { /* reset any T triggering */ new_cell->on_negative = 0; new_cell->on_reply = 0; } else { new_cell->uas.end_request= ((char*)new_cell->uas.request)+sip_msg_len; } /* no parallel support in UAC transactions */ new_cell->on_branch = 0; free_sip_msg(req); } } /* for DNS based failover, copy the DNS proxy into transaction */ if (!disable_dns_failover) { new_cell->uac[0].proxy = shm_clone_proxy( proxy, 1/*do_free*/); if (new_cell->uac[0].proxy==NULL) LM_ERR("failed to store DNS info -> no DNS based failover\n"); } new_cell->method.s = buf; new_cell->method.len = method->len; request->buffer.s = buf; request->buffer.len = buf_len; new_cell->nr_of_outgoings++; if(last_localT) { *last_localT = new_cell; REF_UNSAFE(new_cell); } if (SEND_BUFFER(request) == -1) { LM_ERR("attempt to send to '%.*s' failed\n", dialog->hooks.next_hop->len, dialog->hooks.next_hop->s); } if (method->len==ACK_LEN && memcmp(method->s, ACK, ACK_LEN)==0 ) { t_release_transaction(new_cell); } else { start_retr(request); } free_proxy( proxy ); pkg_free( proxy ); return 1; error1: LOCK_HASH(hi); remove_from_hash_table_unsafe(new_cell); UNLOCK_HASH(hi); free_cell(new_cell); error2: free_proxy( proxy ); pkg_free( proxy ); error3: return ret; }
/* * Send a request using data from the dialog structure */ int t_uac(str* method, str* headers, str* body, dlg_t* dialog, transaction_cb cb, void* cbp) { struct socket_info* send_sock; union sockaddr_union to_su; struct cell *new_cell; struct retr_buf *request; char* buf; int buf_len; int ret; unsigned int hi; ret=-1; /*** added by dcm * - needed by external ua to send a request within a dlg */ if(!dialog->hooks.next_hop && w_calculate_hooks(dialog)<0) goto error2; DBG("DEBUG:tm:t_uac: next_hop=<%.*s>\n",dialog->hooks.next_hop->len, dialog->hooks.next_hop->s); /* it's a new message, so we will take the default socket */ send_sock = uri2sock(0, dialog->hooks.next_hop, &to_su, PROTO_NONE); if (!send_sock) { ret=ser_error; LOG(L_ERR, "t_uac: no socket found\n"); goto error2; } new_cell = build_cell(0); if (!new_cell) { ret=E_OUT_OF_MEM; LOG(L_ERR, "t_uac: short of cell shmem\n"); goto error2; } /* better reset avp list now - anyhow, it's useless from * this point (bogdan) */ reset_avps(); /* add the callback the the transaction for LOCAL_COMPLETED event */ if(cb && insert_tmcb(&(new_cell->tmcb_hl),TMCB_LOCAL_COMPLETED,cb,cbp)!=1){ ret=E_OUT_OF_MEM; LOG(L_ERR, "t_uac: short of tmcb shmem\n"); goto error2; } if (method->len==INVITE_LEN && memcmp(method->s, INVITE, INVITE_LEN)==0) new_cell->flags |= T_IS_INVITE_FLAG; new_cell->flags |= T_IS_LOCAL_FLAG; set_kr(REQ_FWDED); request = &new_cell->uac[0].request; request->dst.to = to_su; request->dst.send_sock = send_sock; request->dst.proto = send_sock->proto; request->dst.proto_reserved1 = 0; hi=dlg2hash(dialog); LOCK_HASH(hi); insert_into_hash_table_unsafe(new_cell, hi); UNLOCK_HASH(hi); buf = build_uac_req(method, headers, body, dialog, 0, new_cell, &buf_len, send_sock); if (!buf) { LOG(L_ERR, "t_uac: Error while building message\n"); ret=E_OUT_OF_MEM; goto error1; } new_cell->method.s = buf; new_cell->method.len = method->len; request->buffer = buf; request->buffer_len = buf_len; new_cell->nr_of_outgoings++; if (SEND_BUFFER(request) == -1) { LOG(L_ERR, "t_uac: Attempt to send to '%.*s' failed\n", dialog->hooks.next_hop->len, dialog->hooks.next_hop->s); } start_retr(request); return 1; error1: LOCK_HASH(hi); remove_from_hash_table_unsafe(new_cell); UNLOCK_HASH(hi); free_cell(new_cell); error2: 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 proto) { 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; int try_new; str dst_uri; struct socket_info *bk_sock; int rurib_flags; int br_flags; int idx; /* make -Wall happy */ current_uri.s=0; set_kr(REQ_FWDED); 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; e2e_cancel( p_msg, t, t_invite ); UNREF(t_invite); return 1; } } /* do not forward requests which were already cancelled*/ if (was_cancelled(t)) { LOG(L_ERR,"ERROR:tm:t_forward_nonack: discarding fwd for " "a cancelled transaction\n"); 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; rurib_flags = p_msg->flags&(~gflags_mask); /* 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; /* on first-time forwarding, use current uri, later only what is in additional branches (which may be continuously refilled) */ if (t->first_branch==0) { try_new=1; current_uri = *GET_RURI(p_msg); branch_ret = add_uac( t, p_msg, ¤t_uri, &backup_dst, proxy, proto ); if (branch_ret>=0) added_branches |= 1<<branch_ret; else lowest_ret=branch_ret; } else try_new=0; for( idx=0; (current_uri.s=get_branch( idx, ¤t_uri.len, &q, &dst_uri, &br_flags, &p_msg->force_send_socket))!=0 ; idx++ ) { try_new++; p_msg->flags = (p_msg->flags&gflags_mask) | br_flags; branch_ret=add_uac( t, p_msg, ¤t_uri, &dst_uri, proxy, proto); /* 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; /* update on_branch, if modified */ t->on_branch = get_on_branch(); /* set flags */ p_msg->flags = (p_msg->flags&gflags_mask)|rurib_flags; t->uas.request->flags = p_msg->flags&gflags_mask; /* don't forget to clear all branches processed so far */ /* things went wrong ... no new branch has been fwd-ed at all */ if (added_branches==0) { if (try_new==0) { LOG(L_ERR, "ERROR:tm:t_forward_nonack: no branch for " "forwarding\n"); return -1; } LOG(L_ERR, "ERROR:tm:t_forward_nonack: failure to add branches\n"); 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 (SEND_BUFFER( &t->uac[i].request)==-1) { LOG(L_ERR, "ERROR:tm:t_forward_nonack: sending request " "failed\n"); if (proxy) { proxy->errors++; proxy->ok=0; } } else { success_branch++; } start_retr( &t->uac[i].request ); } } if (success_branch<=0) { ser_error=E_SEND; return -1; } return 1; }
/* * Send a request using data from the dialog structure */ int t_uac(str* method, str* headers, str* body, dlg_t* dialog, transaction_cb cb, void* cbp,release_tmcb_param release_func) { union sockaddr_union to_su; struct cell *new_cell; struct retr_buf *request; static struct sip_msg *req; struct usr_avp **backup; char *buf, *buf1; int buf_len, buf_len1; int ret, flags, sflag_bk; int backup_route_type; unsigned int hi; struct socket_info* send_sock; ret=-1; /*** added by dcm * - needed by external ua to send a request within a dlg */ if(!dialog->hooks.next_hop && w_calculate_hooks(dialog)<0) goto error2; if(dialog->obp.s) dialog->hooks.next_hop = &dialog->obp; LM_DBG("next_hop=<%.*s>\n",dialog->hooks.next_hop->len, dialog->hooks.next_hop->s); /* calculate the socket corresponding to next hop */ send_sock = uri2sock(0, dialog->hooks.next_hop, &to_su, PROTO_NONE); if (send_sock==0) { ret=ser_error; LM_ERR("no socket found\n"); goto error2; } /* if a send socket defined verify if the same protocol */ if(dialog->send_sock) { if(send_sock->proto != dialog->send_sock->proto) { dialog->send_sock = send_sock; } } else { dialog->send_sock = send_sock; } new_cell = build_cell(0); if (!new_cell) { ret=E_OUT_OF_MEM; LM_ERR("short of cell shmem\n"); goto error2; } /* pass the transaction flags from dialog to transaction */ new_cell->flags |= dialog->T_flags; /* add the callback the transaction for LOCAL_COMPLETED event */ flags = TMCB_LOCAL_COMPLETED; /* Add also TMCB_LOCAL_RESPONSE_OUT if provisional replies are desired */ if (pass_provisional_replies || pass_provisional(new_cell)) flags |= TMCB_LOCAL_RESPONSE_OUT; if(cb && insert_tmcb(&(new_cell->tmcb_hl),flags,cb,cbp,release_func)!=1){ ret=E_OUT_OF_MEM; LM_ERR("short of tmcb shmem\n"); goto error2; } if (method->len==INVITE_LEN && memcmp(method->s, INVITE, INVITE_LEN)==0) new_cell->flags |= T_IS_INVITE_FLAG; new_cell->flags |= T_IS_LOCAL_FLAG; request = &new_cell->uac[0].request; request->dst.to = to_su; request->dst.send_sock = dialog->send_sock; request->dst.proto = dialog->send_sock->proto; request->dst.proto_reserved1 = 0; hi=dlg2hash(dialog); LOCK_HASH(hi); insert_into_hash_table_unsafe(new_cell, hi); UNLOCK_HASH(hi); buf = build_uac_req(method, headers, body, dialog, 0, new_cell, &buf_len); if (!buf) { LM_ERR("failed to build message\n"); ret=E_OUT_OF_MEM; goto error1; } if (local_rlist.a) { LM_DBG("building sip_msg from buffer\n"); req = buf_to_sip_msg(buf, buf_len, dialog); if (req==NULL) { LM_ERR("failed to build sip_msg from buffer"); } else { /* set transaction AVP list */ backup = set_avp_list( &new_cell->user_avps ); /* backup script flags */ sflag_bk = getsflags(); /* disable parallel forking */ set_dset_state( 0 /*disable*/); /* transfer current message context back to t */ new_cell->uac[0].br_flags = req->flags; /* run the route */ swap_route_type( backup_route_type, LOCAL_ROUTE); run_top_route( local_rlist.a, req); set_route_type( backup_route_type ); set_dset_state( 1 /*enable*/); setsflagsval(sflag_bk); set_avp_list( backup ); /* check for changes - if none, do not regenerate the buffer * we ignore any change on RURI and DSTURI and they should not * be changed -bogdan */ if (req->new_uri.s) { pkg_free(req->new_uri.s); req->new_uri.s=0; req->new_uri.len=0; } if (req->dst_uri.s) { pkg_free(req->dst_uri.s); req->dst_uri.s=0; req->dst_uri.len=0; } if (req->add_rm || req->body_lumps) { LM_DBG("re-building the buffer (sip_msg changed) - lumps are" "%p %p\n",req->add_rm, req->body_lumps); /* build the shm buffer now */ buf1 = build_req_buf_from_sip_req(req,(unsigned int*)&buf_len1, dialog->send_sock, dialog->send_sock->proto, MSG_TRANS_SHM_FLAG|MSG_TRANS_NOVIA_FLAG ); if (!buf1) { LM_ERR("no more shm mem\n"); /* keep original buffer */ } else { shm_free(buf); buf = buf1; buf_len = buf_len1; /* use new buffer */ } } free_sip_msg(req); } } new_cell->method.s = buf; new_cell->method.len = method->len; request->buffer.s = buf; request->buffer.len = buf_len; new_cell->nr_of_outgoings++; if(last_localT) { *last_localT = new_cell; REF_UNSAFE(new_cell); } if (SEND_BUFFER(request) == -1) { LM_ERR("attempt to send to '%.*s' failed\n", dialog->hooks.next_hop->len, dialog->hooks.next_hop->s); } if (method->len==ACK_LEN && memcmp(method->s, ACK, ACK_LEN)==0 ) { t_release_transaction(new_cell); } else { start_retr(request); } return 1; error1: LOCK_HASH(hi); remove_from_hash_table_unsafe(new_cell); UNLOCK_HASH(hi); free_cell(new_cell); error2: return ret; }
/* * Send a request using data from the dialog structure */ int t_uac(str* method, str* headers, str* body, dlg_t* dialog, transaction_cb cb, void* cbp) { struct socket_info* send_sock; union sockaddr_union to_su; struct cell *new_cell; struct retr_buf *request; char* buf; int buf_len; int ret; unsigned int hi; ret=-1; /*** added by dcm * - needed by external ua to send a request within a dlg */ if(!dialog->hooks.next_hop && w_calculate_hooks(dialog)<0) goto error2; send_sock = uri2sock(dialog->hooks.next_hop, &to_su, PROTO_NONE); if (!send_sock) { ret=ser_error; LOG(L_ERR, "t_uac: no socket found\n"); goto error2; } new_cell = build_cell(0); if (!new_cell) { ret=E_OUT_OF_MEM; LOG(L_ERR, "t_uac: short of cell shmem\n"); goto error2; } new_cell->completion_cb = cb; new_cell->cbp = cbp; /* cbp is installed -- tell error handling bellow not to free it */ cbp = 0; new_cell->is_invite = method->len == INVITE_LEN && memcmp(method->s, INVITE, INVITE_LEN) == 0; new_cell->local= 1; set_kr(REQ_FWDED); request = &new_cell->uac[0].request; request->dst.to = to_su; request->dst.send_sock = send_sock; request->dst.proto = send_sock->proto; request->dst.proto_reserved1 = 0; hi=dlg2hash(dialog); LOCK_HASH(hi); insert_into_hash_table_unsafe(new_cell, hi); UNLOCK_HASH(hi); buf = build_uac_req(method, headers, body, dialog, 0, new_cell, &buf_len, send_sock); if (!buf) { LOG(L_ERR, "t_uac: Error while building message\n"); ret=E_OUT_OF_MEM; goto error1; } new_cell->method.s = buf; new_cell->method.len = method->len; request->buffer = buf; request->buffer_len = buf_len; new_cell->nr_of_outgoings++; if (SEND_BUFFER(request) == -1) { LOG(L_ERR, "t_uac: Attempt to send to '%.*s' failed\n", dialog->hooks.next_hop->len, dialog->hooks.next_hop->s ); } start_retr(request); return 1; error1: LOCK_HASH(hi); remove_from_hash_table_unsafe(new_cell); UNLOCK_HASH(hi); free_cell(new_cell); error2: /* if we did not install cbp, release it now */ if (cbp) shm_free(cbp); return ret; }
void e2e_cancel( struct sip_msg *cancel_msg, struct cell *t_cancel, struct cell *t_invite ) { branch_bm_t cancel_bm; int i; int lowest_error; str backup_uri; int ret; cancel_bm=0; lowest_error=0; backup_uri=cancel_msg->new_uri; /* determine which branches to cancel ... */ which_cancel( t_invite, &cancel_bm ); t_cancel->nr_of_outgoings=t_invite->nr_of_outgoings; /* fix label -- it must be same for reply matching */ t_cancel->label=t_invite->label; /* ... and install CANCEL UACs */ for (i=0; i<t_invite->nr_of_outgoings; i++) if (cancel_bm & (1<<i)) { ret=e2e_cancel_branch(cancel_msg, t_cancel, t_invite, i); if (ret<0) cancel_bm &= ~(1<<i); if (ret<lowest_error) lowest_error=ret; } cancel_msg->new_uri=backup_uri; /* send them out */ for (i=0; i<t_cancel->nr_of_outgoings; i++) { if (cancel_bm & (1<<i)) { if (SEND_BUFFER( &t_cancel->uac[i].request)==-1) { LOG(L_ERR, "ERROR: e2e_cancel: send failed\n"); } start_retr( &t_cancel->uac[i].request ); } } /* if error occured, let it know upstream (final reply will also move the transaction on wait state */ if (lowest_error<0) { LOG(L_ERR, "ERROR: cancel error\n"); t_reply( t_cancel, cancel_msg, 500, "cancel error"); /* if there are pending branches, let upstream know we are working on it */ } else if (cancel_bm) { DBG("DEBUG: e2e_cancel: e2e cancel proceeding\n"); t_reply( t_cancel, cancel_msg, 200, CANCELLING ); /* if the transaction exists, but there is no more pending branch, tell usptream we're done */ } else { DBG("DEBUG: e2e_cancel: e2e cancel -- no more pending branches\n"); t_reply( t_cancel, cancel_msg, 200, CANCEL_DONE ); } /* we could await downstream UAS's 487 replies; however, if some of the branches does not do that, we could wait long time and annoy upstream UAC which wants to see a result of CANCEL quickly */ DBG("DEBUG: e2e_cancel: sending 487\n"); /* in case that something in the meantime has been sent upstream (like if FR hit at the same time), don't try to send */ if (t_invite->uas.status>=200) return; /* there is still a race-condition -- the FR can hit now; that's not too bad -- we take care in t_reply's REPLY_LOCK; in the worst case, both this t_reply and other replier will try, and the later one will result in error message "can't reply twice" */ t_reply(t_invite, t_invite->uas.request, 487, CANCELLED ); }