modules/tm/t_reply.c
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;
 }