📄 t_reply.c
字号:
static int _reply( struct cell *trans, struct sip_msg* p_msg, unsigned int code, char * text, int lock ){ unsigned int len; char * buf, *dset; struct bookmark bm; int dset_len; if (code>=200) set_kr(REQ_RPLD); /* compute the buffer in private memory prior to entering lock; * create to-tag if needed */ /* if that is a redirection message, dump current message set to it */ if (code>=300 && code<400) { dset=print_dset(p_msg, &dset_len); if (dset) { add_lump_rpl(p_msg, dset, dset_len, LUMP_RPL_HDR); } } if (code>=180 && p_msg->to && (get_to(p_msg)->tag_value.s==0 || get_to(p_msg)->tag_value.len==0)) { calc_crc_suffix( p_msg, tm_tag_suffix ); buf = build_res_buf_from_sip_req(code,text, &tm_tag, p_msg, &len, &bm); return _reply_light( trans, buf, len, code, text, tm_tag.s, TOTAG_VALUE_LEN, lock, &bm); } else { buf = build_res_buf_from_sip_req(code,text, 0 /*no to-tag*/, p_msg, &len, &bm); return _reply_light(trans,buf,len,code,text, 0, 0, /* no to-tag */lock, &bm); }}/*if msg is set -> it will fake the env. vars conforming with the msg; if NULL * the env. will be restore to original */static inline void faked_env( struct cell *t,struct sip_msg *msg){ static enum route_mode backup_mode; static struct cell *backup_t; static unsigned int backup_msgid; static struct usr_avp **backup_list; static struct socket_info* backup_si; if (msg) { /* remember we are back in request processing, but process * a shmem-ed replica of the request; advertise it in rmode; * for example t_reply needs to know that */ backup_mode=rmode; rmode=MODE_ONFAILURE; /* also, tm actions look in beginning whether transaction is * set -- whether we are called from a reply-processing * or a timer process, we need to set current transaction; * otherwise the actions would attempt to look the transaction * up (unnecessary overhead, refcounting) */ /* backup */ backup_t=get_t(); backup_msgid=global_msg_id; /* fake transaction and message id */ global_msg_id=msg->id; set_t(t); /* make available the avp list from transaction */ backup_list = set_avp_list( &t->user_avps ); /* set default send address to the saved value */ backup_si=bind_address; bind_address=t->uac[0].request.dst.send_sock; } else { /* restore original environment */ set_t(backup_t); global_msg_id=backup_msgid; rmode=backup_mode; /* restore original avp list */ set_avp_list( backup_list ); bind_address=backup_si; }}static inline int fake_req(struct sip_msg *faked_req, struct sip_msg *shmem_msg){ /* on_negative_reply faked msg now copied from shmem msg (as opposed * to zero-ing) -- more "read-only" actions (exec in particular) will * work from reply_route as they will see msg->from, etc.; caution, * rw actions may append some pkg stuff to msg, which will possibly be * never released (shmem is released in a single block) */ memcpy( faked_req, shmem_msg, sizeof(struct sip_msg)); /* if we set msg_id to something different from current's message * id, the first t_fork will properly clean new branch URIs */ faked_req->id=shmem_msg->id-1; /* msg->parsed_uri_ok must be reset since msg_parsed_uri is * not cloned (and cannot be cloned) */ faked_req->parsed_uri_ok = 0; /* new_uri can change -- make a private copy */ if (shmem_msg->new_uri.s!=0 && shmem_msg->new_uri.len!=0) { faked_req->new_uri.s=pkg_malloc(shmem_msg->new_uri.len+1); if (!faked_req->new_uri.s) { LOG(L_ERR, "ERROR: fake_req: no uri/pkg mem\n"); goto error00; } faked_req->new_uri.len=shmem_msg->new_uri.len; memcpy( faked_req->new_uri.s, shmem_msg->new_uri.s, faked_req->new_uri.len); faked_req->new_uri.s[faked_req->new_uri.len]=0; } /* dst_uri can change ALSO!!! -- make a private copy */ if (shmem_msg->dst_uri.s!=0 && shmem_msg->dst_uri.len!=0) { faked_req->dst_uri.s=pkg_malloc(shmem_msg->dst_uri.len+1); if (!faked_req->dst_uri.s) { LOG(L_ERR, "ERROR: fake_req: no uri/pkg mem\n"); goto error00; } faked_req->dst_uri.len=shmem_msg->dst_uri.len; memcpy( faked_req->dst_uri.s, shmem_msg->dst_uri.s, faked_req->dst_uri.len); faked_req->dst_uri.s[faked_req->dst_uri.len]=0; } return 1;error00: return 0;}void inline static free_faked_req(struct sip_msg *faked_req, struct cell *t){ struct hdr_field *hdr; if (faked_req->new_uri.s) { pkg_free(faked_req->new_uri.s); faked_req->new_uri.s = 0; } if (faked_req->dst_uri.s) { pkg_free(faked_req->dst_uri.s); faked_req->dst_uri.s = 0; } /* free all types of lump that were added in failure handlers */ del_nonshm_lump( &(faked_req->add_rm) ); del_nonshm_lump( &(faked_req->body_lumps) ); del_nonshm_lump_rpl( &(faked_req->reply_lump) ); /* free header's parsed structures that were added by failure handlers */ for( hdr=faked_req->headers ; hdr ; hdr=hdr->next ) { if ( hdr->parsed && hdr_allocs_parse(hdr) && (hdr->parsed<(void*)t->uas.request || hdr->parsed>=(void*)t->uas.end_request)) { /* header parsed filed doesn't point inside uas.request memory * chunck -> it was added by failure funcs.-> free it as pkg */ DBG("DBG:free_faked_req: removing hdr->parsed %d\n", hdr->type); clean_hdr_field(hdr); hdr->parsed = 0; } }}/* return 1 if a failure_route processes */static inline int run_failure_handlers(struct cell *t, struct sip_msg *rpl, int code){ static struct sip_msg faked_req; struct sip_msg *shmem_msg = t->uas.request; int on_failure; /* failure_route for a local UAC? */ if (!shmem_msg) { LOG(L_WARN,"Warning: run_failure_handlers: no UAC 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 ) { LOG(L_WARN, "Warning: run_failure_handlers: no negative handler (%d, %d)\n", t->on_negative, t->tmcb_hl.reg_types); return 1; } if (!fake_req(&faked_req, shmem_msg)) { LOG(L_ERR, "ERROR: run_failure_handlers: 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, rpl, code); } if (t->on_negative) { /* 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 */ if (run_actions(failure_rlist[on_failure], &faked_req)<0) LOG(L_ERR, "ERROR: run_failure_handlers: Error in do_action\n"); } /* 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; return 1;}/* select a branch for forwarding; returns: * 0..X ... branch number * -1 ... error * -2 ... can't decide yet -- incomplete branches present */int t_pick_branch(int inc_branch, int inc_code, struct cell *t, int *res_code){ int lowest_b, lowest_s, b; lowest_b=-1; lowest_s=999; for ( b=0; b<t->nr_of_outgoings ; b++ ) { /* "fake" for the currently processed branch */ if (b==inc_branch) { if (inc_code<lowest_s) { lowest_b=b; lowest_s=inc_code; } continue; } /* skip 'empty branches' */ if (!t->uac[b].request.buffer) continue; /* there is still an unfinished UAC transaction; wait now! */ if ( t->uac[b].last_received<200 ) return -2; if ( t->uac[b].last_received<lowest_s ) { lowest_b =b; lowest_s = t->uac[b].last_received; } } /* find lowest branch */ *res_code=lowest_s; return lowest_b;}/* This is the neurological point of reply processing -- called * from within a REPLY_LOCK, t_should_relay_response decides * how a reply shall be processed and how transaction state is * affected. * * Checks if the new reply (with new_code status) should be sent or not * based on the current * transaction status. * Returns - branch number (0,1,...) which should be relayed * -1 if nothing to be relayed */static enum rps t_should_relay_response( struct cell *Trans , int new_code, int branch , int *should_store, int *should_relay, branch_bm_t *cancel_bitmap, struct sip_msg *reply ){ int branch_cnt; int picked_branch; int picked_code; int inv_through; /* note: this code never lets replies to CANCEL go through; we generate always a local 200 for CANCEL; 200s are not relayed because it's not an INVITE transaction; >= 300 are not relayed because 200 was already sent out */ DBG("->>>>>>>>> T_code=%d, new_code=%d\n",Trans->uas.status,new_code); inv_through=new_code>=200 && new_code<300 && is_invite(Trans); /* if final response sent out, allow only INVITE 2xx */ if ( Trans->uas.status >= 200 ) { if (inv_through) { DBG("DBG: t_should_relay_response: 200 INV after final sent\n"); *should_store=0; Trans->uac[branch].last_received=new_code; *should_relay=branch; return RPS_PUSHED_AFTER_COMPLETION; } /* except the exception above, too late messages will be discarded */ goto discard; } /* if final response received at this branch, allow only INVITE 2xx */ if (Trans->uac[branch].last_received>=200 && !(inv_through && Trans->uac[branch].last_received<300)) { /* don't report on retransmissions */ if (Trans->uac[branch].last_received==new_code) { DBG("DEBUG: final reply retransmission\n"); goto discard; } /* if you FR-timed-out, faked a local 408 and 487 came, don't * report on it either */ if (Trans->uac[branch].last_received==408 && new_code==487) { DBG("DEBUG: 487 came for a timed-out branch\n"); goto discard; } /* this looks however how a very strange status rewrite attempt; * report on it */ LOG(L_ERR, "ERROR: t_should_relay_response: status rewrite by UAS: " "stored: %d, received: %d\n", Trans->uac[branch].last_received, new_code ); goto discard; } /* no final response sent yet */ /* negative replies subject to fork picking */ if (new_code >=300 ) { Trans->uac[branch].last_received=new_code; /* if all_final return lowest */ picked_branch=t_pick_branch(branch,new_code, Trans, &picked_code); if (picked_branch==-2) { /* branches open yet */ *should_store=1; *should_relay=-1; return RPS_STORE; } if (picked_branch==-1) { LOG(L_CRIT, "ERROR: t_should_relay_response: lowest==-1\n"); goto error; } /* no more pending branches -- try if that changes after a callback; save branch count to be able to determine later if new branches were initiated */ branch_cnt=Trans->nr_of_outgoings; /* run ON_FAILURE handlers ( route and callbacks) */ if ( has_tran_tmcbs( Trans, TMCB_ON_FAILURE_RO|TMCB_ON_FAILURE) || Trans->on_negative ) { run_failure_handlers( Trans, picked_branch==branch?reply:Trans->uac[picked_branch].reply, picked_code); } /* look if the callback perhaps replied transaction; it also covers the case in which a transaction is replied localy on CANCEL -- then it would make no sense to proceed to new branches bellow */ if (Trans->uas.status >= 200) { *should_store=0; *should_relay=-1; /* this might deserve an improvement -- if something was already replied, it was put on wait and then, returning RPS_COMPLETED will make t_on_reply put it on wait again; perhaps splitting put_on_wait from send_reply or a new RPS_ code would be healthy */ return RPS_COMPLETED; } /* look if the callback/failure_route introduced new branches ... */ if (branch_cnt<Trans->nr_of_outgoings) { /* await then result of new branches */ *should_store=1; *should_relay=-1; return RPS_STORE; } /* really no more pending branches -- return lowest code */ *should_store=0; *should_relay=picked_branch; /* we dont need 'which_cancel' here -- all branches known to have completed */ /* which_cancel( Trans, cancel_bitmap ); */ return RPS_COMPLETED; } /* not >=300 ... it must be 2xx or provisional 1xx */ if (new_code>=100) { /* 1xx and 2xx except 100 will be relayed */ Trans->uac[branch].last_received=new_code; *should_store=0; *should_relay= new_code==100? -1 : branch; if (new_code>=200 ) { which_cancel( Trans, cancel_bitmap ); return RPS_COMPLETED; } else return RPS_PROVISIONAL; }error: /* reply_status didn't match -- it must be something weird */ LOG(L_CRIT, "ERROR: Oh my gooosh! We don't know whether to relay %d\n", new_code);discard: *should_store=0; *should_relay=-1; return RPS_DISCARDED;}/* Retransmits the last sent inbound reply. * input: p_msg==request for which I want to retransmit an associated reply * Returns -1 - error * 1 - OK */int t_retransmit_reply( struct cell *t ){ static char b[BUF_SIZE]; int len; /* first check if we managed to resolve topmost Via -- if not yet, don't try to retransmit */ /* response.dst.send_sock might be unset if the process that created the original transaction has not finished initialising the retransmission buffer (see t_newtran/ init_rb). If reply_to_via is set and via contains a host name (and not an ip) the chances for this increase a lot. */ if (!t->uas.response.dst.send_sock) { LOG(L_WARN, "WARNING: t_retransmit_reply: " "no resolved dst to retransmit\n"); return -1; } /* we need to lock the transaction as messages from upstream may change it continuously */ LOCK_REPLIES( t ); if (!t->uas.response.buffer) { DBG("DBG: t_retransmit_reply: nothing to retransmit\n"); goto error; } len=t->uas.response.buffer_len; if ( len==0 || len>BUF_SIZE ) { DBG("DBG: t_retransmit_reply: " "zero length or too big to retransmit: %d\n", len); goto error; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -