40a8d9dd |
/*
* $Id$
*
*/
#include "hash_func.h"
#include "t_funcs.h"
#include "../../dprint.h"
#include "../../config.h"
#include "../../parser_f.h"
#include "../../ut.h"
#include "../../timer.h"
|
62097a8f |
inline int check_for_no_response( struct cell *Trans ,int code, int relay)
{
if ( code/100>3 && Trans->uac[Trans->nr_of_outgoings].uri.s )
{
forward_serial_branch( Trans , Trans->nr_of_outgoings );
return -1;
}
return relay;
}
|
40a8d9dd |
/* Retransmits the last sent inbound reply.
|
549d5e1c |
* input: p_msg==request for which I want to retransmit an associated reply
* Returns -1 - error
* 1 - OK
*/
|
40a8d9dd |
int t_retransmit_reply( /* struct sip_msg* p_msg */ )
{
|
b25c055d |
static char b[BUF_SIZE];
|
40a8d9dd |
int len;
|
46931a4d |
if (!T->uas.response.buffer)
|
b25c055d |
return 0;
|
46931a4d |
if ( (len=T->uas.response.buffer_len)==0 || len>BUF_SIZE ) {
|
40a8d9dd |
UNLOCK_REPLIES( T );
return -1;
|
aa7ffe90 |
}
|
46931a4d |
memcpy( b, T->uas.response.buffer, len );
|
40a8d9dd |
UNLOCK_REPLIES( T );
|
46931a4d |
SEND_PR_BUFFER( & T->uas.response, b, len );
|
40a8d9dd |
return 1;
}
|
549d5e1c |
|
40a8d9dd |
/* Force a new response into inbound response buffer.
|
b908cdeb |
* returns 1 if everything was OK or -1 for error
|
40a8d9dd |
*/
|
46931a4d |
int t_send_reply( struct sip_msg* p_msg, unsigned int code, char * text,
unsigned int branch)
|
40a8d9dd |
{
|
46931a4d |
unsigned int len, buf_len=0;
|
6beabeca |
char * buf;
|
46931a4d |
struct retr_buf *rb;
int relay, save_clone;
|
40a8d9dd |
|
46931a4d |
buf = build_res_buf_from_sip_req(code,text,T->uas.tag->s,T->uas.tag->len,
T->uas.request,&len);
|
40a8d9dd |
DBG("DEBUG: t_send_reply: buffer computed\n");
if (!buf)
{
DBG("DEBUG: t_send_reply: response building failed\n");
goto error;
}
LOCK_REPLIES( T );
|
d75e3395 |
relay = t_should_relay_response(T, code, branch, &save_clone);
|
46931a4d |
if (save_clone)
{
T->uac[branch].status = code;
}
rb = & T->uas.response;
|
62097a8f |
if (relay >=0 && (relay=check_for_no_response(T,code,relay))>=0 )
|
46931a4d |
{
if (!rb->buffer) {
/* initialize retransmission structure */
if (update_sock_struct_from_via( &(rb->to), p_msg->via1 )==-1)
{
UNLOCK_REPLIES( T );
LOG(L_ERR,"ERROR: t_send_reply: cannot lookup reply dst: %s\n",
p_msg->via1->host.s );
goto error2;
}
rb->to.sin_family = AF_INET;
rb->activ_type = code;
buf_len = len + REPLY_OVERBUFFER_LEN;
}else{
buf_len = len;
}
/* puts the reply's buffer to uas.response */
if (! (rb->buffer = (char*)shm_resize( rb->buffer, buf_len )))
|
40a8d9dd |
{
UNLOCK_REPLIES( T );
|
46931a4d |
LOG(L_ERR, "ERROR: t_send_reply: cannot allocate shmem buffer\n");
|
40a8d9dd |
goto error2;
}
|
46931a4d |
rb->buffer_len = len ;
memcpy( rb->buffer , buf , len );
T->uas.status = code;
/* needs to be protected too because what timers are set depends
on current transactions status */
t_update_timers_after_sending_reply( rb );
} /* if realy */
|
40a8d9dd |
UNLOCK_REPLIES( T );
|
46931a4d |
if (relay>=0) SEND_PR_BUFFER( rb, buf, len );
|
e22bbdb8 |
pkg_free( buf ) ;
|
40a8d9dd |
DBG("DEBUG: t_send_reply: finished\n");
return 1;
error2:
|
e22bbdb8 |
pkg_free ( buf );
|
40a8d9dd |
error:
return -1;
}
|
e0a6ffa6 |
#if 0
|
40a8d9dd |
/* Push a previously stored reply from UA Client to UA Server
|
71b771fa |
* and send it out */
static int push_reply( struct cell* trans , unsigned int branch ,
char *buf, unsigned int len)
|
40a8d9dd |
{
unsigned int buf_len;
struct retrans_buff *rb;
DBG("DEBUG: push_reply_from_uac_to_uas: start\n");
rb= & trans->outbound_response;
/* if there is a reply, release the buffer (everything else stays same) */
if ( ! rb->retr_buffer ) {
/*init retrans buffer*/
memset( rb , 0 , sizeof (struct retrans_buff) );
if (update_sock_struct_from_via( &(rb->to),
trans->inbound_response[branch]->via2 )==-1) {
LOG(L_ERR, "ERROR: push_reply_from_uac_to_uas: "
"cannot lookup reply dst: %s\n",
trans->inbound_response[branch]->via2->host.s );
goto error;
}
rb->retr_timer.tg=TG_RT;
rb->fr_timer.tg=TG_FR;
rb->retr_timer.payload = rb;
rb->fr_timer.payload = rb;
rb->to.sin_family = AF_INET;
rb->my_T = trans;
|
549d5e1c |
rb->status = trans->inbound_response[branch]->REPLY_STATUS;
|
71b771fa |
};
|
40a8d9dd |
/* if this is a first reply (?100), longer replies will probably follow;
try avoiding shm_resize by higher buffer size */
buf_len = rb->retr_buffer ? len : len + REPLY_OVERBUFFER_LEN;
if (! (rb->retr_buffer = (char*)shm_resize( rb->retr_buffer, buf_len )))
{
LOG(L_ERR, "ERROR: t_push: cannot allocate shmem buffer\n");
goto error1;
}
rb->bufflen = len ;
memcpy( rb->retr_buffer , buf , len );
/* update the status*/
trans->status = trans->inbound_response[branch]->REPLY_STATUS;
if ( trans->inbound_response[branch]->REPLY_STATUS>=200 &&
trans->relaied_reply_branch==-1 ) {
memcpy( & trans->ack_to, & trans->outbound_request[ branch ]->to,
sizeof( struct sockaddr_in ) );
trans->relaied_reply_branch = branch;
}
/*send the reply*/
SEND_BUFFER( rb );
return 1;
error1:
error:
return -1;
}
|
e0a6ffa6 |
#endif
|
40a8d9dd |
/* This function is called whenever a reply for our module is received;
* we need to register this function on module initialization;
* Returns : 0 - core router stops
* 1 - core router relay statelessly
*/
int t_on_reply( struct sip_msg *p_msg )
{
|
e0a6ffa6 |
int branch, msg_status, msg_class, save_clone;
int local_cancel;
|
40a8d9dd |
int relay;
|
b908cdeb |
int start_fr = 0;
|
40a8d9dd |
int is_invite;
|
4cb961a8 |
/* retransmission structure of outbound reply and request */
|
46931a4d |
struct retr_buf *rb=0;
char *buf=0, *ack=0;
|
4cb961a8 |
/* length of outbound reply */
|
46931a4d |
unsigned int res_len, ack_len;
|
4cb961a8 |
/* buffer length (might be somewhat larger than message size */
unsigned int alloc_len;
|
76f8a73f |
str *str_foo;
|
40a8d9dd |
/* make sure we know the assosociated tranaction ... */
|
b908cdeb |
if (t_check( p_msg , &branch , &local_cancel)==-1)
return 1;
|
40a8d9dd |
/* ... if there is no such, tell the core router to forward statelessly */
if ( T<=0 ) return 1;
|
46931a4d |
DBG("DEBUG: t_on_reply: org. status uas=%d, uac[%d]=%d loca_cancel=%d)\n",
T->uas.status, branch, T->uac[branch].status, local_cancel);
|
aa7ffe90 |
|
46931a4d |
/* special cases (local cancel reply) -bogdan */
|
549d5e1c |
if (local_cancel==1)
{
|
46931a4d |
reset_timer( hash_table, &(T->uac[branch].request.retr_timer));
|
549d5e1c |
if ( p_msg->REPLY_STATUS>=200 )
|
46931a4d |
reset_timer(hash_table,&(T->uac[branch].request.fr_timer));
|
b908cdeb |
goto error;
|
549d5e1c |
}
|
40a8d9dd |
|
b908cdeb |
/* do we have via2 ? - maybe we'll need it for forwarding -bogdan*/
if ((p_msg->via2==0) || (p_msg->via2->error!=VIA_PARSE_OK)){
/* no second via => error */
LOG(L_ERR, "ERROR: t_on_reply: no 2nd via found in reply\n");
|
40a8d9dd |
goto error;
}
|
46931a4d |
|
40a8d9dd |
msg_status=p_msg->REPLY_STATUS;
msg_class=REPLY_CLASS(p_msg);
|
46931a4d |
is_invite= T->uas.request->REQ_METHOD==METHOD_INVITE;
|
40a8d9dd |
|
549d5e1c |
/* generate the retrans buffer, make a simplified
assumption everything but 100 will be fwd-ed;
sometimes it will result in useless CPU cycles
but mostly the assumption holds and allows the
work to be done out of criticial lock region */
|
40633485 |
if (msg_status==100 && T->uac[branch].status)
|
b908cdeb |
buf=0;
|
40a8d9dd |
else {
|
b908cdeb |
/* buf maybe allo'ed*/
|
46931a4d |
buf = build_res_buf_from_sip_res ( p_msg, &res_len);
|
40a8d9dd |
if (!buf) {
LOG(L_ERR, "ERROR: t_on_reply_received: "
"no mem for outbound reply buffer\n");
|
46931a4d |
goto error;
|
40a8d9dd |
}
}
/* *** stop timers *** */
/* stop retransmission */
|
46931a4d |
reset_timer( hash_table, &(T->uac[branch].request.retr_timer));
|
40a8d9dd |
/* stop final response timer only if I got a final response */
if ( msg_class>1 )
|
46931a4d |
reset_timer( hash_table, &(T->uac[branch].request.fr_timer));
|
40a8d9dd |
LOCK_REPLIES( T );
|
b908cdeb |
/* if a got the first prov. response for an INVITE ->
|
40a8d9dd |
change FR_TIME_OUT to INV_FR_TIME_UT */
|
46931a4d |
start_fr = !T->uac[branch].rpl_received && msg_class==1 && is_invite;
|
40a8d9dd |
/* *** store and relay message as needed *** */
relay = t_should_relay_response( T , msg_status, branch, &save_clone );
|
62097a8f |
DBG("DEBUG: t_on_reply: branch=%d, save=%d, relay=%d\n",
branch, save_clone, relay );
|
46931a4d |
if (save_clone)
{
|
76f8a73f |
str_foo = &(T->uac[branch].tag);
str_foo->s = shm_resize(str_foo->s, (str_foo?0:TAG_OVERBUFFER_LEN) +
get_to(p_msg)->tag_value.len);
if (!str_foo->s)
|
46931a4d |
{
LOG( L_ERR , "ERROR: t_on_reply: connot alocate memory!\n");
goto error1;
}
|
76f8a73f |
/* when forking, replies greater then 300 are saved */
|
62097a8f |
if ((T->nr_of_outgoings>1 || T->uac[T->nr_of_outgoings].uri.s)
&& msg_status>=300 )
|
76f8a73f |
{
|
62097a8f |
DBG("DEBUG: t_on_reply: saving reply! \n");
|
76f8a73f |
str_foo = &(T->uac[branch].rpl_buffer);
str_foo->s = shm_resize(str_foo->s, res_len+
(str_foo->s?0:REPLY_OVERBUFFER_LEN) );
if (!str_foo->s)
{
LOG( L_ERR , "ERROR: t_on_reply: connot alocate memory!\n");
goto error1;
}
memcpy(str_foo->s,buf,res_len);
str_foo->len = res_len;
}
/*copy the TO tag from reply*/
|
46931a4d |
T->uac[branch].tag.len = get_to(p_msg)->tag_value.len;
memcpy( T->uac[branch].tag.s, get_to(p_msg)->tag_value.s,
T->uac[branch].tag.len );
T->uac[branch].rpl_received = 1;
T->uac[branch].status = msg_status;
}
rb = & T->uas.response;
|
62097a8f |
if (relay >= 0 && (relay=check_for_no_response(T,msg_status,relay))>=0 ) {
|
76f8a73f |
if (relay!=branch)
{
str_foo = &(T->uac[relay].rpl_buffer);
if (buf) pkg_free(buf);
buf = (char*)pkg_malloc(str_foo->len);
if (!buf)
{
UNLOCK_REPLIES( T );
start_fr = 1;
LOG(L_ERR, "ERROR: t_on_reply: cannot alloc pkg mem\n");
goto error1;
}
memcpy( buf , str_foo->s , str_foo->len );
res_len = str_foo->len;
}
|
4cb961a8 |
/* if there is no reply yet, initialize the structure */
|
46931a4d |
if ( ! rb->buffer ) {
|
4cb961a8 |
/*init retrans buffer*/
|
46931a4d |
if (update_sock_struct_from_via( &(rb->to),p_msg->via2 )==-1) {
|
b908cdeb |
UNLOCK_REPLIES( T );
start_fr = 1;
|
46931a4d |
LOG(L_ERR, "ERROR: t_on_reply: cannot lookup reply dst: %s\n",
p_msg->via2->host.s );
goto error1;
|
4cb961a8 |
}
|
46931a4d |
rb->to.sin_family = AF_INET;
rb->activ_type = p_msg->REPLY_STATUS;
|
4cb961a8 |
/* allocate something more for the first message;
subsequent messages will be longer and buffer
reusing will save us a malloc lock */
|
46931a4d |
alloc_len = res_len + REPLY_OVERBUFFER_LEN ;
|
76f8a73f |
}else{
|
46931a4d |
alloc_len = res_len;
|
b908cdeb |
}
|
46931a4d |
/* puts the reply's buffer to uas.response */
|
76f8a73f |
if (! (rb->buffer = (char*)shm_resize( rb->buffer, alloc_len ))) {
|
4cb961a8 |
UNLOCK_REPLIES( T );
start_fr = 1;
LOG(L_ERR, "ERROR: t_on_reply: cannot alloc shmem\n");
|
46931a4d |
goto error1;
|
76f8a73f |
}
|
46931a4d |
rb->buffer_len = res_len;
memcpy( rb->buffer, buf, res_len );
|
aa7ffe90 |
/* update the status ... */
|
46931a4d |
T->uas.status = p_msg->REPLY_STATUS;
|
76f8a73f |
T->uas.tag=&(T->uac[relay].tag);
|
46931a4d |
if (T->uas.status >=200 && T->relaied_reply_branch==-1 )
|
76f8a73f |
T->relaied_reply_branch = relay;
|
4cb961a8 |
}; /* if relay ... */
UNLOCK_REPLIES( T );
|
46931a4d |
|
4cb961a8 |
if (relay >= 0) {
|
46931a4d |
SEND_PR_BUFFER( rb, buf, res_len );
t_update_timers_after_sending_reply( rb );
|
4cb961a8 |
}
|
40a8d9dd |
/* *** ACK handling *** */
|
b908cdeb |
if ( is_invite ) {
|
46931a4d |
if ( T->uac[branch].request.ack_len )
|
40a8d9dd |
{ /*retransmit*/
|
4cb961a8 |
/* I don't need any additional syncing here -- after ack
is introduced it's never changed */
|
46931a4d |
SEND_ACK_BUFFER( &(T->uac[branch].request) );
|
b908cdeb |
} else if (msg_class>2 ) {
/*on a non-200 reply to INVITE*/
DBG("DEBUG: t_on_reply_received: >=3xx reply to INVITE:"
"send ACK\n");
|
46931a4d |
ack = build_ack( p_msg, T, branch , &ack_len);
if (ack) {
SEND_PR_BUFFER( &(T->uac[branch].request), ack, ack_len );
|
b908cdeb |
/* append to transaction structure */
|
46931a4d |
attach_ack( T, branch, ack , ack_len );
|
b908cdeb |
} else {
/* restart FR */
start_fr=1;
DBG("ERROR: t_on_reply: build_ack failed\n");
}
|
549d5e1c |
}
|
4cb961a8 |
} /* is_invite */
|
46931a4d |
/* restart retransmission if a provisional response came for
|
40a8d9dd |
a non_INVITE -> retrasmit at RT_T2*/
if ( msg_class==1 && !is_invite )
{
|
46931a4d |
rb->retr_list = RT_T2;
set_timer( hash_table, &(rb->retr_timer), RT_T2 );
|
40a8d9dd |
}
|
4cb961a8 |
error1:
|
46931a4d |
if (start_fr)
set_timer( hash_table, &(rb->fr_timer), FR_INV_TIMER_LIST );
if (buf) pkg_free( buf );
|
40a8d9dd |
error:
T_UNREF( T );
/* don't try to relay statelessly on error; on troubles, simply do nothing;
|
40633485 |
that will make the other party to retransmit; hopefuly, we'll then
be better off */
|
40a8d9dd |
return 0;
}
|