|
@@ -4,204 +4,479 @@
|
|
*/
|
|
*/
|
|
|
|
|
|
|
|
|
|
-#include "hash_func.h"
|
|
|
|
|
|
+#include "../../hash_func.h"
|
|
#include "t_funcs.h"
|
|
#include "t_funcs.h"
|
|
#include "../../dprint.h"
|
|
#include "../../dprint.h"
|
|
#include "../../config.h"
|
|
#include "../../config.h"
|
|
#include "../../parser/parser_f.h"
|
|
#include "../../parser/parser_f.h"
|
|
#include "../../ut.h"
|
|
#include "../../ut.h"
|
|
#include "../../timer.h"
|
|
#include "../../timer.h"
|
|
|
|
+#include "../../error.h"
|
|
|
|
+#include "../../action.h"
|
|
|
|
+#include "../../dset.h"
|
|
|
|
|
|
#include "t_hooks.h"
|
|
#include "t_hooks.h"
|
|
|
|
+#include "t_funcs.h"
|
|
|
|
+#include "t_reply.h"
|
|
|
|
+#include "t_cancel.h"
|
|
|
|
+#include "t_msgbuilder.h"
|
|
|
|
+#include "t_lookup.h"
|
|
|
|
+#include "t_fwd.h"
|
|
|
|
+#include "fix_lumps.h"
|
|
|
|
+
|
|
|
|
+/* where to go if there is no positive reply */
|
|
|
|
+static int goto_on_negative=0;
|
|
|
|
+
|
|
|
|
+/* we store the reply_route # in private memory which is
|
|
|
|
+ then processed during t_relay; we cannot set this value
|
|
|
|
+ before t_relay creates transaction context or after
|
|
|
|
+ t_relay when a reply may arrive after we set this
|
|
|
|
+ value; that's why we do it how we do it, i.e.,
|
|
|
|
+ *inside* t_relay using hints stored in private memory
|
|
|
|
+ before t_reay is called
|
|
|
|
+*/
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+int t_on_negative( unsigned int go_to )
|
|
|
|
+{
|
|
|
|
+ goto_on_negative=go_to;
|
|
|
|
+ return 1;
|
|
|
|
+}
|
|
|
|
|
|
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-inline int check_for_no_response( struct cell *Trans ,int code, int relay)
|
|
|
|
|
|
+unsigned int get_on_negative()
|
|
{
|
|
{
|
|
- if ( code/100>=3 && Trans->uac[Trans->nr_of_outgoings].uri.s )
|
|
|
|
- {
|
|
|
|
- forward_serial_branch( Trans , Trans->nr_of_outgoings );
|
|
|
|
- return -1;
|
|
|
|
- }
|
|
|
|
- return relay;
|
|
|
|
|
|
+ return goto_on_negative;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
+/* the main code of stateful replying */
|
|
|
|
+static int _reply( struct cell *t, struct sip_msg* p_msg, unsigned int code,
|
|
|
|
+ char * text, int lock );
|
|
|
|
|
|
/* Retransmits the last sent inbound reply.
|
|
/* Retransmits the last sent inbound reply.
|
|
* input: p_msg==request for which I want to retransmit an associated reply
|
|
* input: p_msg==request for which I want to retransmit an associated reply
|
|
* Returns -1 - error
|
|
* Returns -1 - error
|
|
* 1 - OK
|
|
* 1 - OK
|
|
*/
|
|
*/
|
|
-int t_retransmit_reply( /* struct sip_msg* p_msg */ )
|
|
|
|
|
|
+int t_retransmit_reply( struct cell *t )
|
|
{
|
|
{
|
|
static char b[BUF_SIZE];
|
|
static char b[BUF_SIZE];
|
|
int len;
|
|
int len;
|
|
|
|
|
|
- if (!T->uas.response.buffer)
|
|
|
|
|
|
+ /* first check if we managed to resolve topmost Via -- if
|
|
|
|
+ not yet, don't try to retransmit
|
|
|
|
+ */
|
|
|
|
+ if (!t->uas.response.send_sock) {
|
|
|
|
+ LOG(L_ERR, "ERROR: no resolved dst to retransmit\n");
|
|
return -1;
|
|
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;
|
|
|
|
+ }
|
|
|
|
|
|
- if ( (len=T->uas.response.buffer_len)==0 || len>BUF_SIZE ) {
|
|
|
|
- UNLOCK_REPLIES( T );
|
|
|
|
- return -2;
|
|
|
|
|
|
+ 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;
|
|
}
|
|
}
|
|
- memcpy( b, T->uas.response.buffer, len );
|
|
|
|
- UNLOCK_REPLIES( T );
|
|
|
|
- SEND_PR_BUFFER( & T->uas.response, b, len );
|
|
|
|
|
|
+ memcpy( b, t->uas.response.buffer, len );
|
|
|
|
+ UNLOCK_REPLIES( t );
|
|
|
|
+ SEND_PR_BUFFER( & t->uas.response, b, len );
|
|
|
|
+ DBG("DEBUG: reply retransmitted. buf=%p: %.9s..., shmem=%p: %.9s\n",
|
|
|
|
+ b, b, t->uas.response.buffer, t->uas.response.buffer );
|
|
return 1;
|
|
return 1;
|
|
|
|
+
|
|
|
|
+error:
|
|
|
|
+ UNLOCK_REPLIES(t);
|
|
|
|
+ return -1;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
+int t_reply( struct cell *t, struct sip_msg* p_msg, unsigned int code,
|
|
|
|
+ char * text )
|
|
|
|
+{
|
|
|
|
+ return _reply( t, p_msg, code, text, 1 /* lock replies */ );
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+int t_reply_unsafe( struct cell *t, struct sip_msg* p_msg, unsigned int code,
|
|
|
|
+ char * text )
|
|
|
|
+{
|
|
|
|
+ return _reply( t, p_msg, code, text, 0 /* don't lock replies */ );
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
|
|
|
|
|
|
-/* Force a new response into inbound response buffer.
|
|
|
|
- * returns 1 if everything was OK or -1 for error
|
|
|
|
- */
|
|
|
|
-int t_send_reply( struct sip_msg* p_msg, unsigned int code,
|
|
|
|
- char * text, unsigned int branch)
|
|
|
|
|
|
+/* send a UAS reply
|
|
|
|
+ * returns 1 if everything was OK or -1 for error
|
|
|
|
+ */
|
|
|
|
+static int _reply( struct cell *trans, struct sip_msg* p_msg,
|
|
|
|
+ unsigned int code, char * text, int lock )
|
|
{
|
|
{
|
|
unsigned int len, buf_len=0;
|
|
unsigned int len, buf_len=0;
|
|
char * buf;
|
|
char * buf;
|
|
struct retr_buf *rb;
|
|
struct retr_buf *rb;
|
|
- int relay, save_clone;
|
|
|
|
- struct socket_info* send_sock;
|
|
|
|
|
|
|
|
- buf = build_res_buf_from_sip_req(code,text,T->uas.tag->s,
|
|
|
|
- T->uas.tag->len, T->uas.request,&len);
|
|
|
|
- DBG("DEBUG: t_send_reply: buffer computed\n");
|
|
|
|
|
|
+ branch_bm_t cancel_bitmap;
|
|
|
|
+
|
|
|
|
+ if (code>=200) trans->kr|=REQ_RPLD;
|
|
|
|
+ /*
|
|
|
|
+ buf = build_res_buf_from_sip_req(code,text,trans->uas.tag->s,
|
|
|
|
+ trans->uas.tag->len, trans->uas.request,&len);
|
|
|
|
+ */
|
|
|
|
+ cancel_bitmap=0;
|
|
|
|
+ /* compute the buffer in private memory prior to entering lock */
|
|
|
|
+ buf = build_res_buf_from_sip_req(code,text, 0,0, /* no to-tag */
|
|
|
|
+ p_msg,&len);
|
|
|
|
+ DBG("DEBUG: t_reply: buffer computed\n");
|
|
if (!buf)
|
|
if (!buf)
|
|
{
|
|
{
|
|
- DBG("DEBUG: t_send_reply: response building failed\n");
|
|
|
|
|
|
+ DBG("DEBUG: t_reply: response building failed\n");
|
|
|
|
+ /* determine if there are some branches to be cancelled */
|
|
|
|
+ if (trans->is_invite) {
|
|
|
|
+ if (lock) LOCK_REPLIES( trans );
|
|
|
|
+ which_cancel(trans, &cancel_bitmap );
|
|
|
|
+ if (lock) UNLOCK_REPLIES( trans );
|
|
|
|
+ }
|
|
|
|
+ /* and clean-up, including cancellations, if needed */
|
|
goto error;
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
|
|
- LOCK_REPLIES( T );
|
|
|
|
- relay = t_should_relay_response(T, code, branch, &save_clone);
|
|
|
|
-
|
|
|
|
- if (save_clone)
|
|
|
|
- {
|
|
|
|
- T->uac[branch].status = code;
|
|
|
|
|
|
+ if (lock) LOCK_REPLIES( trans );
|
|
|
|
+ if (trans->is_invite) which_cancel(trans, &cancel_bitmap );
|
|
|
|
+ if (trans->uas.status>=200) {
|
|
|
|
+ LOG( L_ERR, "ERROR: t_reply: can't generate replies"
|
|
|
|
+ "when a final was sent out\n");
|
|
|
|
+ goto error2;
|
|
}
|
|
}
|
|
-
|
|
|
|
- rb = & T->uas.response;
|
|
|
|
- if (relay >=0 && (relay=check_for_no_response(T,code,relay))>=0 )
|
|
|
|
- {
|
|
|
|
- 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;
|
|
|
|
- }
|
|
|
|
- send_sock=get_send_socket(&rb->to);
|
|
|
|
- if (send_sock==0) {
|
|
|
|
- LOG(L_ERR, "ERROR: t_send_reply: cannot fwd to af %d "
|
|
|
|
- "no socket\n", rb->to.s.sa_family);
|
|
|
|
- ser_error=E_NO_SOCKET;
|
|
|
|
- goto error2;
|
|
|
|
- }
|
|
|
|
- rb->send_sock=send_sock;
|
|
|
|
- /* 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 )))
|
|
|
|
- {
|
|
|
|
- UNLOCK_REPLIES( T );
|
|
|
|
- LOG(L_ERR, "ERROR: t_send_reply: cannot allocate shmem buffer\n");
|
|
|
|
|
|
+ rb = & trans->uas.response;
|
|
|
|
+ rb->activ_type=code;
|
|
|
|
+
|
|
|
|
+ buf_len = rb->buffer ? len : len + REPLY_OVERBUFFER_LEN;
|
|
|
|
+ rb->buffer = (char*)shm_resize( rb->buffer, buf_len );
|
|
|
|
+ /* puts the reply's buffer to uas.response */
|
|
|
|
+ if (! rb->buffer ) {
|
|
|
|
+ LOG(L_ERR, "ERROR: t_reply: cannot allocate shmem buffer\n");
|
|
goto error2;
|
|
goto error2;
|
|
- }
|
|
|
|
- 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 */
|
|
|
|
-
|
|
|
|
- UNLOCK_REPLIES( T );
|
|
|
|
|
|
+ }
|
|
|
|
+ rb->buffer_len = len ;
|
|
|
|
+ memcpy( rb->buffer , buf , len );
|
|
|
|
+ trans->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 (lock) UNLOCK_REPLIES( trans );
|
|
|
|
+
|
|
|
|
+ /* do UAC cleanup procedures in case we generated
|
|
|
|
+ a final answer whereas there are pending UACs */
|
|
|
|
+ if (code>=200) {
|
|
|
|
+ cleanup_uac_timers( trans );
|
|
|
|
+ if (trans->is_invite) cancel_uacs( trans, cancel_bitmap );
|
|
|
|
+ set_final_timer( /* hash_table, */ trans );
|
|
|
|
+ }
|
|
|
|
|
|
- if (relay>=0) SEND_PR_BUFFER( rb, buf, len );
|
|
|
|
|
|
+ /* send it out */
|
|
|
|
+ /* first check if we managed to resolve topmost Via -- if
|
|
|
|
+ not yet, don't try to retransmit
|
|
|
|
+ */
|
|
|
|
+ if (!trans->uas.response.send_sock) {
|
|
|
|
+ LOG(L_ERR, "ERROR: _reply: no resolved dst to send reply to\n");
|
|
|
|
+ } else {
|
|
|
|
+ SEND_PR_BUFFER( rb, buf, len );
|
|
|
|
+ DBG("DEBUG: reply sent out. buf=%p: %.9s..., shmem=%p: %.9s\n",
|
|
|
|
+ buf, buf, rb->buffer, rb->buffer );
|
|
|
|
+ }
|
|
pkg_free( buf ) ;
|
|
pkg_free( buf ) ;
|
|
- DBG("DEBUG: t_send_reply: finished\n");
|
|
|
|
|
|
+ DBG("DEBUG: t_reply: finished\n");
|
|
return 1;
|
|
return 1;
|
|
|
|
|
|
error2:
|
|
error2:
|
|
|
|
+ if (lock) UNLOCK_REPLIES( trans );
|
|
pkg_free ( buf );
|
|
pkg_free ( buf );
|
|
error:
|
|
error:
|
|
|
|
+ /* do UAC cleanup */
|
|
|
|
+ cleanup_uac_timers( trans );
|
|
|
|
+ if (trans->is_invite) cancel_uacs( trans, cancel_bitmap );
|
|
|
|
+ /* we did not succeed -- put the transaction on wait */
|
|
|
|
+ put_on_wait(trans);
|
|
return -1;
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+void set_final_timer( /* struct s_table *h_table, */ struct cell *t )
|
|
|
|
+{
|
|
|
|
+ if ( !t->local
|
|
|
|
+ && t->uas.request->REQ_METHOD==METHOD_INVITE
|
|
|
|
+ && t->uas.status>=300 ) {
|
|
|
|
+ /* crank timers for negative replies */
|
|
|
|
+ start_retr( &t->uas.response );
|
|
|
|
+ } else put_on_wait(t);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+void cleanup_uac_timers( struct cell *t )
|
|
|
|
+{
|
|
|
|
+ int i;
|
|
|
|
|
|
|
|
+ /* reset FR/retransmission timers */
|
|
|
|
+ for (i=0; i<t->nr_of_outgoings; i++ ) {
|
|
|
|
+ reset_timer( hash_table, &t->uac[i].request.retr_timer );
|
|
|
|
+ reset_timer( hash_table, &t->uac[i].request.fr_timer );
|
|
|
|
+ }
|
|
|
|
+ DBG("DEBUG: cleanup_uacs: RETR/FR timers reset\n");
|
|
|
|
+}
|
|
|
|
|
|
-#if 0
|
|
|
|
-/* Push a previously stored reply from UA Client to UA Server
|
|
|
|
- * and send it out */
|
|
|
|
-static int push_reply( struct cell* trans , unsigned int branch ,
|
|
|
|
- char *buf, unsigned int len)
|
|
|
|
|
|
+int store_reply( struct cell *trans, int branch, struct sip_msg *rpl)
|
|
{
|
|
{
|
|
- 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;
|
|
|
|
|
|
+# ifdef EXTRA_DEBUG
|
|
|
|
+ if (trans->uac[branch].reply) {
|
|
|
|
+ LOG(L_ERR, "ERROR: replacing stored reply; aborting\n");
|
|
|
|
+ abort();
|
|
}
|
|
}
|
|
- 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;
|
|
|
|
- rb->status = trans->inbound_response[branch]->REPLY_STATUS;
|
|
|
|
- };
|
|
|
|
-
|
|
|
|
- /* 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 )))
|
|
|
|
|
|
+# endif
|
|
|
|
+
|
|
|
|
+ /* when we later do things such as challenge aggregation,
|
|
|
|
+ we should parse the message here before we conservate
|
|
|
|
+ it in shared memory; -jiri
|
|
|
|
+ */
|
|
|
|
+ if (rpl==FAKED_REPLY)
|
|
|
|
+ trans->uac[branch].reply=FAKED_REPLY;
|
|
|
|
+ else
|
|
|
|
+ trans->uac[branch].reply = sip_msg_cloner( rpl );
|
|
|
|
+
|
|
|
|
+ if (! trans->uac[branch].reply ) {
|
|
|
|
+ LOG(L_ERR, "ERROR: store_reply: can't alloc' clone memory\n");
|
|
|
|
+ return 0;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return 1;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/* this is the code which decides what and when shall be relayed
|
|
|
|
+ upstream; note well -- it assumes it is entered locked with
|
|
|
|
+ REPLY_LOCK and it returns unlocked!
|
|
|
|
+*/
|
|
|
|
+enum rps relay_reply( struct cell *t, struct sip_msg *p_msg, int branch,
|
|
|
|
+ unsigned int msg_status, branch_bm_t *cancel_bitmap )
|
|
|
|
+{
|
|
|
|
+ int relay;
|
|
|
|
+ int save_clone;
|
|
|
|
+ char *buf;
|
|
|
|
+ /* length of outbound reply */
|
|
|
|
+ unsigned int res_len;
|
|
|
|
+ int relayed_code;
|
|
|
|
+ struct sip_msg *relayed_msg;
|
|
|
|
+ str to_tag;
|
|
|
|
+ enum rps reply_status;
|
|
|
|
+ /* retransmission structure of outbound reply and request */
|
|
|
|
+ struct retr_buf *uas_rb;
|
|
|
|
+
|
|
|
|
+ /* keep compiler warnings about use of uninit vars silent */
|
|
|
|
+ res_len=0;
|
|
|
|
+ buf=0;
|
|
|
|
+ relayed_msg=0;
|
|
|
|
+ relayed_code=0;
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ /* remember, what was sent upstream to know whether we are
|
|
|
|
+ forwarding a first final reply or not */
|
|
|
|
+
|
|
|
|
+ /* *** store and relay message as needed *** */
|
|
|
|
+ reply_status = t_should_relay_response(t, msg_status, branch,
|
|
|
|
+ &save_clone, &relay, cancel_bitmap );
|
|
|
|
+ DBG("DEBUG: relay_reply: branch=%d, save=%d, relay=%d\n",
|
|
|
|
+ branch, save_clone, relay );
|
|
|
|
+
|
|
|
|
+ /* store the message if needed */
|
|
|
|
+ if (save_clone) /* save for later use, typically branch picking */
|
|
{
|
|
{
|
|
- LOG(L_ERR, "ERROR: t_push: cannot allocate shmem buffer\n");
|
|
|
|
- goto error1;
|
|
|
|
|
|
+ if (!store_reply( t, branch, p_msg ))
|
|
|
|
+ goto error01;
|
|
}
|
|
}
|
|
- 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 ) {
|
|
|
|
|
|
+ uas_rb = & t->uas.response;
|
|
|
|
+ if (relay >= 0 ) {
|
|
|
|
+
|
|
|
|
+ /* initialize sockets for outbound reply */
|
|
|
|
+ uas_rb->activ_type=msg_status;
|
|
|
|
+ /* only messages known to be relayed immediately will be
|
|
|
|
+ be called on; we do not evoke this callback on messages
|
|
|
|
+ stored in shmem -- they are fixed and one cannot change them
|
|
|
|
+ anyway
|
|
|
|
+ */
|
|
|
|
+ if (msg_status<300 && branch==relay) {
|
|
|
|
+ callback_event( TMCB_REPLY_IN, t, p_msg, msg_status );
|
|
|
|
+ }
|
|
|
|
+ /* try bulding the outbound reply from either the current
|
|
|
|
+ or a stored message */
|
|
|
|
+ relayed_msg = branch==relay ? p_msg : t->uac[relay].reply;
|
|
|
|
+ if (relayed_msg ==FAKED_REPLY) {
|
|
|
|
+ relayed_code = branch==relay
|
|
|
|
+ ? msg_status : t->uac[relay].last_received;
|
|
|
|
+ buf = build_res_buf_from_sip_req( relayed_code,
|
|
|
|
+ error_text(relayed_code), 0,0, /* no to-tag */
|
|
|
|
+ t->uas.request, &res_len );
|
|
|
|
+ } else {
|
|
|
|
+ relayed_code=relayed_msg->REPLY_STATUS;
|
|
|
|
+ buf = build_res_buf_from_sip_res( relayed_msg, &res_len );
|
|
|
|
+ /* if we build a message from shmem, we need to remove
|
|
|
|
+ via delete lumps which are now stirred in the shmem-ed
|
|
|
|
+ structure
|
|
|
|
+ */
|
|
|
|
+ if (branch!=relay) {
|
|
|
|
+ free_via_lump(&relayed_msg->repl_add_rm);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ if (!buf) {
|
|
|
|
+ LOG(L_ERR, "ERROR: relay_reply: "
|
|
|
|
+ "no mem for outbound reply buffer\n");
|
|
|
|
+ goto error02;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* attempt to copy the message to UAS's shmem:
|
|
|
|
+ - copy to-tag for ACK matching as well
|
|
|
|
+ - allocate little a bit more for provisionals as
|
|
|
|
+ larger messages are likely to follow and we will be
|
|
|
|
+ able to reuse the memory frag
|
|
|
|
+ */
|
|
|
|
+ uas_rb->buffer = (char*)shm_resize( uas_rb->buffer, res_len +
|
|
|
|
+ (msg_status<200 ? REPLY_OVERBUFFER_LEN : 0));
|
|
|
|
+ if (!uas_rb->buffer) {
|
|
|
|
+ LOG(L_ERR, "ERROR: relay_reply: cannot alloc reply shmem\n");
|
|
|
|
+ goto error03;
|
|
|
|
+ }
|
|
|
|
+ uas_rb->buffer_len = res_len;
|
|
|
|
+ memcpy( uas_rb->buffer, buf, res_len );
|
|
|
|
+ /* to tag now */
|
|
|
|
+ if (relayed_code>=300 && t->is_invite) {
|
|
|
|
+ if (relayed_msg!=FAKED_REPLY) {
|
|
|
|
+ to_tag=get_to(relayed_msg)->tag_value;
|
|
|
|
+ t->uas.to_tag.s=(char *)shm_resize( t->uas.to_tag.s,
|
|
|
|
+ to_tag.len );
|
|
|
|
+ if (!t->uas.to_tag.s) {
|
|
|
|
+ LOG(L_ERR, "ERROR: no shmem for to-tag\n");
|
|
|
|
+ goto error04;
|
|
|
|
+ }
|
|
|
|
+ t->uas.to_tag.len=to_tag.len;
|
|
|
|
+ memcpy(t->uas.to_tag.s, to_tag.s, to_tag.len );
|
|
|
|
+ } else {
|
|
|
|
+ if (t->uas.to_tag.s) shm_free(t->uas.to_tag.s);
|
|
|
|
+ t->uas.to_tag.s=0;
|
|
|
|
+ t->uas.to_tag.len=0;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* update the status ... */
|
|
|
|
+ t->uas.status = relayed_code;
|
|
|
|
+ t->relaied_reply_branch = relay;
|
|
|
|
+ }; /* if relay ... */
|
|
|
|
+
|
|
|
|
+ UNLOCK_REPLIES( t );
|
|
|
|
|
|
- memcpy( & trans->ack_to, & trans->outbound_request[ branch ]->to,
|
|
|
|
- sizeof( struct sockaddr_in ) );
|
|
|
|
- trans->relaied_reply_branch = branch;
|
|
|
|
|
|
+ /* send it now (from the private buffer) */
|
|
|
|
+ if (relay >= 0) {
|
|
|
|
+ SEND_PR_BUFFER( uas_rb, buf, res_len );
|
|
|
|
+ DBG("DEBUG: reply relayed. buf=%p: %.9s..., shmem=%p: %.9s\n",
|
|
|
|
+ buf, buf, uas_rb->buffer, uas_rb->buffer );
|
|
|
|
+ callback_event( TMCB_REPLY, t, relayed_msg, relayed_code );
|
|
|
|
+ pkg_free( buf );
|
|
}
|
|
}
|
|
|
|
|
|
- /*send the reply*/
|
|
|
|
- SEND_BUFFER( rb );
|
|
|
|
- return 1;
|
|
|
|
|
|
+ /* success */
|
|
|
|
+ return reply_status;
|
|
|
|
+
|
|
|
|
+error04:
|
|
|
|
+ shm_free( uas_rb->buffer );
|
|
|
|
+ uas_rb->buffer=0;
|
|
|
|
+error03:
|
|
|
|
+ pkg_free( buf );
|
|
|
|
+error02:
|
|
|
|
+ if (save_clone) {
|
|
|
|
+ if (t->uac[branch].reply!=FAKED_REPLY)
|
|
|
|
+ sip_msg_free( t->uac[branch].reply );
|
|
|
|
+ t->uac[branch].reply = NULL;
|
|
|
|
+ }
|
|
|
|
+error01:
|
|
|
|
+ UNLOCK_REPLIES(t);
|
|
|
|
+ if (t->is_invite) cancel_uacs( t, *cancel_bitmap );
|
|
|
|
+ /* a serious error occured -- attempt to send an error reply;
|
|
|
|
+ it will take care of clean-ups
|
|
|
|
+ */
|
|
|
|
+ t_reply( t, t->uas.request, 500, "Reply processing error" );
|
|
|
|
+
|
|
|
|
+ /* failure */
|
|
|
|
+ return RPS_ERROR;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/* this is the "UAC" above transaction layer; if a final reply
|
|
|
|
+ is received, it triggers a callback; note well -- it assumes
|
|
|
|
+ it is entered locked with REPLY_LOCK and it returns unlocked!
|
|
|
|
+*/
|
|
|
|
+enum rps local_reply( struct cell *t, struct sip_msg *p_msg, int branch,
|
|
|
|
+ unsigned int msg_status, branch_bm_t *cancel_bitmap)
|
|
|
|
+{
|
|
|
|
+ /* how to deal with replies for local transaction */
|
|
|
|
+ int local_store, local_winner;
|
|
|
|
+ enum rps reply_status;
|
|
|
|
+ struct sip_msg *winning_msg;
|
|
|
|
+ int winning_code;
|
|
|
|
+ /* branch_bm_t cancel_bitmap; */
|
|
|
|
+
|
|
|
|
+ /* keep warning 'var might be used un-inited' silent */
|
|
|
|
+ winning_msg=0;
|
|
|
|
+ winning_code=0;
|
|
|
|
+
|
|
|
|
+ *cancel_bitmap=0;
|
|
|
|
+
|
|
|
|
+ reply_status=t_should_relay_response( t, msg_status, branch,
|
|
|
|
+ &local_store, &local_winner, cancel_bitmap );
|
|
|
|
+ DBG("DEBUG: local_reply: branch=%d, save=%d, winner=%d\n",
|
|
|
|
+ branch, local_store, local_winner );
|
|
|
|
+ if (local_store) {
|
|
|
|
+ if (!store_reply(t, branch, p_msg))
|
|
|
|
+ goto error;
|
|
|
|
+ }
|
|
|
|
+ if (local_winner>=0) {
|
|
|
|
+ winning_msg= branch==local_winner
|
|
|
|
+ ? p_msg : t->uac[local_winner].reply;
|
|
|
|
+ if (winning_msg==FAKED_REPLY) {
|
|
|
|
+ winning_code = branch==local_winner
|
|
|
|
+ ? msg_status : t->uac[local_winner].last_received;
|
|
|
|
+ } else {
|
|
|
|
+ winning_code=winning_msg->REPLY_STATUS;
|
|
|
|
+ }
|
|
|
|
+ t->uas.status = winning_code;
|
|
|
|
+ }
|
|
|
|
+ UNLOCK_REPLIES(t);
|
|
|
|
+ if (local_winner>=0 && winning_code>=200 ) {
|
|
|
|
+ DBG("DEBUG: local transaction completed\n");
|
|
|
|
+ callback_event( TMCB_LOCAL_COMPLETED, t, winning_msg, winning_code );
|
|
|
|
+ if (t->completion_cb)
|
|
|
|
+ t->completion_cb( t, winning_msg, winning_code, 0 /* empty param */);
|
|
|
|
+ }
|
|
|
|
+ return reply_status;
|
|
|
|
|
|
-error1:
|
|
|
|
error:
|
|
error:
|
|
- return -1;
|
|
|
|
|
|
+ which_cancel(t, cancel_bitmap);
|
|
|
|
+ UNLOCK_REPLIES(t);
|
|
|
|
+ cleanup_uac_timers(t);
|
|
|
|
+ if ( get_cseq(p_msg)->method.len==INVITE_LEN
|
|
|
|
+ && memcmp( get_cseq(p_msg)->method.s, INVITE, INVITE_LEN)==0)
|
|
|
|
+ cancel_uacs( t, *cancel_bitmap );
|
|
|
|
+ put_on_wait(t);
|
|
|
|
+ return RPS_ERROR;
|
|
}
|
|
}
|
|
-#endif
|
|
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
|
|
|
|
|
|
/* This function is called whenever a reply for our module is received;
|
|
/* This function is called whenever a reply for our module is received;
|
|
@@ -211,262 +486,130 @@ error:
|
|
*/
|
|
*/
|
|
int t_on_reply( struct sip_msg *p_msg )
|
|
int t_on_reply( struct sip_msg *p_msg )
|
|
{
|
|
{
|
|
- int branch, msg_status, msg_class, save_clone;
|
|
|
|
- int local_cancel;
|
|
|
|
- int relay;
|
|
|
|
- int start_fr = 0;
|
|
|
|
- int is_invite;
|
|
|
|
- /* retransmission structure of outbound reply and request */
|
|
|
|
- struct retr_buf *rb=0;
|
|
|
|
- char *buf=0, *ack=0;
|
|
|
|
- /* length of outbound reply */
|
|
|
|
- unsigned int res_len, ack_len;
|
|
|
|
- /* buffer length (might be somewhat larger than message size */
|
|
|
|
- unsigned int alloc_len;
|
|
|
|
- str *str_foo;
|
|
|
|
- struct socket_info* send_sock;
|
|
|
|
|
|
|
|
|
|
+ int msg_status;
|
|
|
|
+ char *ack;
|
|
|
|
+ unsigned int ack_len;
|
|
|
|
+ int branch;
|
|
|
|
+ /* has the transaction completed now and we need to clean-up? */
|
|
|
|
+ int reply_status;
|
|
|
|
+ branch_bm_t cancel_bitmap;
|
|
|
|
+ struct ua_client *uac;
|
|
|
|
+ struct cell *t;
|
|
|
|
|
|
- /* make sure we know the assosociated tranaction ... */
|
|
|
|
- if (t_check( p_msg , &branch , &local_cancel)==-1)
|
|
|
|
- return 1;
|
|
|
|
- /* ... if there is no such, tell the core router to forward statelessly */
|
|
|
|
- if ( T<=0 ) return 1;
|
|
|
|
|
|
|
|
- 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);
|
|
|
|
|
|
+ /* make sure we know the assosociated transaction ... */
|
|
|
|
+ if (t_check( p_msg , &branch )==-1)
|
|
|
|
+ return 1;
|
|
|
|
+ /*... if there is none, tell the core router to fwd statelessly */
|
|
|
|
+ t=get_t();
|
|
|
|
+ if ( t<=0 ) return 1;
|
|
|
|
|
|
- /* special cases (local cancel reply) -bogdan */
|
|
|
|
- if (local_cancel==1)
|
|
|
|
- {
|
|
|
|
- reset_timer( hash_table, &(T->uac[branch].request.retr_timer));
|
|
|
|
- if ( p_msg->REPLY_STATUS>=200 )
|
|
|
|
- reset_timer(hash_table,&(T->uac[branch].request.fr_timer));
|
|
|
|
- goto error;
|
|
|
|
- }
|
|
|
|
|
|
+ cancel_bitmap=0;
|
|
|
|
+ msg_status=p_msg->REPLY_STATUS;
|
|
|
|
|
|
- /* do we have via2 ? - maybe we'll need it for forwarding -bogdan*/
|
|
|
|
- if ((p_msg->via2==0) || (p_msg->via2->error!=PARSE_OK)){
|
|
|
|
- /* no second via => error */
|
|
|
|
- LOG(L_ERR, "ERROR: t_on_reply: no 2nd via found in reply\n");
|
|
|
|
- goto error;
|
|
|
|
|
|
+ uac=&t->uac[branch];
|
|
|
|
+ DBG("DEBUG: t_on_reply: org. status uas=%d, uac[%d]=%d local=%d)\n",
|
|
|
|
+ t->uas.status, branch, uac->last_received, t->local);
|
|
|
|
+
|
|
|
|
+ /* it's a cancel ... ? */
|
|
|
|
+ if (get_cseq(p_msg)->method.len==CANCEL_LEN
|
|
|
|
+ && memcmp( get_cseq(p_msg)->method.s, CANCEL, CANCEL_LEN)==0
|
|
|
|
+ /* .. which is not e2e ? ... */
|
|
|
|
+ && t->is_invite ) {
|
|
|
|
+ /* ... then just stop timers */
|
|
|
|
+ reset_timer( hash_table, &uac->local_cancel.retr_timer);
|
|
|
|
+ if ( msg_status >= 200 )
|
|
|
|
+ reset_timer( hash_table, &uac->local_cancel.fr_timer);
|
|
|
|
+ DBG("DEBUG: reply to local CANCEL processed\n");
|
|
|
|
+ goto done;
|
|
}
|
|
}
|
|
|
|
|
|
- msg_status=p_msg->REPLY_STATUS;
|
|
|
|
- msg_class=REPLY_CLASS(p_msg);
|
|
|
|
- is_invite= T->uas.request->REQ_METHOD==METHOD_INVITE;
|
|
|
|
-
|
|
|
|
-#ifdef _DONT_DO_IT_MAN
|
|
|
|
-/* 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 */
|
|
|
|
- if (msg_status==100 && T->uac[branch].status)
|
|
|
|
- buf=0;
|
|
|
|
- else {
|
|
|
|
- /* buf maybe allo'ed*/
|
|
|
|
- buf = build_res_buf_from_sip_res ( p_msg, &res_len);
|
|
|
|
- if (!buf) {
|
|
|
|
- LOG(L_ERR, "ERROR: t_on_reply_received: "
|
|
|
|
- "no mem for outbound reply buffer\n");
|
|
|
|
- goto error;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-#endif
|
|
|
|
|
|
|
|
/* *** stop timers *** */
|
|
/* *** stop timers *** */
|
|
/* stop retransmission */
|
|
/* stop retransmission */
|
|
- reset_timer( hash_table, &(T->uac[branch].request.retr_timer));
|
|
|
|
|
|
+ reset_timer( hash_table, &uac->request.retr_timer);
|
|
/* stop final response timer only if I got a final response */
|
|
/* stop final response timer only if I got a final response */
|
|
- if ( msg_class>1 )
|
|
|
|
- reset_timer( hash_table, &(T->uac[branch].request.fr_timer));
|
|
|
|
-
|
|
|
|
- LOCK_REPLIES( T );
|
|
|
|
- /* if a got the first prov. response for an INVITE ->
|
|
|
|
- change FR_TIME_OUT to INV_FR_TIME_UT */
|
|
|
|
- start_fr = !T->uac[branch].rpl_received && msg_class==1 && is_invite;
|
|
|
|
-
|
|
|
|
- /* *** store and relay message as needed *** */
|
|
|
|
- relay = t_should_relay_response( T , msg_status, branch, &save_clone );
|
|
|
|
- DBG("DEBUG: t_on_reply: branch=%d, save=%d, relay=%d\n",
|
|
|
|
- branch, save_clone, relay );
|
|
|
|
-
|
|
|
|
- if (save_clone)
|
|
|
|
- {
|
|
|
|
- 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)
|
|
|
|
- {
|
|
|
|
- LOG( L_ERR , "ERROR: t_on_reply: connot alocate memory!\n");
|
|
|
|
- goto error1;
|
|
|
|
- }
|
|
|
|
- /* when forking, replies greater then 300 are saved */
|
|
|
|
- if ((T->nr_of_outgoings>1 || T->uac[T->nr_of_outgoings].uri.s)
|
|
|
|
- && msg_status>=300 )
|
|
|
|
- {
|
|
|
|
- DBG("DEBUG: t_on_reply: saving reply! \n");
|
|
|
|
- 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*/
|
|
|
|
- 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;
|
|
|
|
|
|
+ if ( msg_status >= 200 )
|
|
|
|
+ reset_timer( hash_table, &uac->request.fr_timer);
|
|
|
|
+
|
|
|
|
+ LOCK_REPLIES( t );
|
|
|
|
+ if (t->local) {
|
|
|
|
+ reply_status=local_reply( t, p_msg, branch, msg_status, &cancel_bitmap );
|
|
|
|
+ } else {
|
|
|
|
+ reply_status=relay_reply( t, p_msg, branch, msg_status,
|
|
|
|
+ &cancel_bitmap );
|
|
}
|
|
}
|
|
|
|
|
|
- rb = & T->uas.response;
|
|
|
|
- if (relay >= 0 && (relay=check_for_no_response(T,msg_status,relay))>=0 ) {
|
|
|
|
|
|
+ if (reply_status==RPS_ERROR)
|
|
|
|
+ goto done;
|
|
|
|
|
|
- buf = build_res_buf_from_sip_res ( p_msg, &res_len);
|
|
|
|
- if (!buf) {
|
|
|
|
- UNLOCK_REPLIES( T );
|
|
|
|
- start_fr = 1;
|
|
|
|
- LOG(L_ERR, "ERROR: t_on_reply_received: "
|
|
|
|
- "no mem for outbound reply buffer\n");
|
|
|
|
- goto error1;
|
|
|
|
|
|
+ /* acknowledge negative INVITE replies */
|
|
|
|
+ if (t->is_invite && (msg_status>=300 || (t->local && msg_status>=200))) {
|
|
|
|
+ ack = build_ack( p_msg, t, branch , &ack_len);
|
|
|
|
+ if (ack) {
|
|
|
|
+ SEND_PR_BUFFER( &uac->request, ack, ack_len );
|
|
|
|
+ shm_free(ack);
|
|
}
|
|
}
|
|
- callback_event( TMCB_REPLY_IN, T, p_msg );
|
|
|
|
-
|
|
|
|
- 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;
|
|
|
|
- }
|
|
|
|
- /* if there is no reply yet, initialize the structure */
|
|
|
|
- if ( ! rb->buffer ) {
|
|
|
|
- /*init retrans buffer*/
|
|
|
|
- if (update_sock_struct_from_via( &(rb->to),p_msg->via2 )==-1) {
|
|
|
|
- UNLOCK_REPLIES( T );
|
|
|
|
- start_fr = 1;
|
|
|
|
- LOG(L_ERR, "ERROR: t_on_reply: cannot lookup reply dst: %s\n",
|
|
|
|
- p_msg->via2->host.s );
|
|
|
|
- goto error1;
|
|
|
|
- }
|
|
|
|
- send_sock=get_send_socket(&rb->to);
|
|
|
|
- if (send_sock==0) {
|
|
|
|
- UNLOCK_REPLIES( T );
|
|
|
|
- LOG(L_ERR, "ERROR: t_on_reply: cannot fwd to af %d "
|
|
|
|
- "no socket\n", rb->to.s.sa_family);
|
|
|
|
- start_fr=1;
|
|
|
|
- goto error1;
|
|
|
|
- }
|
|
|
|
- /* rb->to.sin_family = AF_INET; */
|
|
|
|
- rb->send_sock=send_sock;
|
|
|
|
- rb->activ_type = p_msg->REPLY_STATUS;
|
|
|
|
- /* allocate something more for the first message;
|
|
|
|
- subsequent messages will be longer and buffer
|
|
|
|
- reusing will save us a malloc lock */
|
|
|
|
- alloc_len = res_len + REPLY_OVERBUFFER_LEN ;
|
|
|
|
- }else{
|
|
|
|
- alloc_len = res_len;
|
|
|
|
- }
|
|
|
|
- /* puts the reply's buffer to uas.response */
|
|
|
|
- if (! (rb->buffer = (char*)shm_resize( rb->buffer, alloc_len ))) {
|
|
|
|
- UNLOCK_REPLIES( T );
|
|
|
|
- start_fr = 1;
|
|
|
|
- LOG(L_ERR, "ERROR: t_on_reply: cannot alloc shmem\n");
|
|
|
|
- goto error1;
|
|
|
|
- }
|
|
|
|
- rb->buffer_len = res_len;
|
|
|
|
- memcpy( rb->buffer, buf, res_len );
|
|
|
|
- /* update the status ... */
|
|
|
|
- T->uas.status = p_msg->REPLY_STATUS;
|
|
|
|
- T->uas.tag=&(T->uac[relay].tag);
|
|
|
|
- if (T->uas.status >=200 && T->relaied_reply_branch==-1 )
|
|
|
|
- T->relaied_reply_branch = relay;
|
|
|
|
- }; /* if relay ... */
|
|
|
|
-
|
|
|
|
- UNLOCK_REPLIES( T );
|
|
|
|
-
|
|
|
|
- if (relay >= 0) {
|
|
|
|
- SEND_PR_BUFFER( rb, buf, res_len );
|
|
|
|
- t_update_timers_after_sending_reply( rb );
|
|
|
|
- callback_event( TMCB_REPLY, T, p_msg );
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /* *** ACK handling *** */
|
|
|
|
- if ( is_invite ) {
|
|
|
|
- if ( T->uac[branch].request.ack_len )
|
|
|
|
- { /*retransmit*/
|
|
|
|
- /* I don't need any additional syncing here -- after ack
|
|
|
|
- is introduced it's never changed */
|
|
|
|
- DBG("DEBUG: localy cached ACK retranmitted\n");
|
|
|
|
- SEND_ACK_BUFFER( &(T->uac[branch].request) );
|
|
|
|
- } 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");
|
|
|
|
- ack = build_ack( p_msg, T, branch , &ack_len);
|
|
|
|
- if (ack) {
|
|
|
|
- SEND_PR_BUFFER( &(T->uac[branch].request), ack, ack_len );
|
|
|
|
- /* append to transaction structure */
|
|
|
|
- attach_ack( T, branch, ack , ack_len );
|
|
|
|
- } else {
|
|
|
|
- /* restart FR */
|
|
|
|
- start_fr=1;
|
|
|
|
- DBG("ERROR: t_on_reply: build_ack failed\n");
|
|
|
|
- }
|
|
|
|
|
|
+ } /* ack-ing negative INVITE replies */
|
|
|
|
+
|
|
|
|
+ /* clean-up the transaction when transaction completed */
|
|
|
|
+ if (reply_status==RPS_COMPLETED) {
|
|
|
|
+ /* no more UAC FR/RETR (if I received a 2xx, there may
|
|
|
|
+ be still pending branches ...
|
|
|
|
+ */
|
|
|
|
+ cleanup_uac_timers( t );
|
|
|
|
+ if (t->is_invite) cancel_uacs( t, cancel_bitmap );
|
|
|
|
+ /* FR for negative INVITES, WAIT anything else */
|
|
|
|
+ set_final_timer( /* hash_table,*/ t );
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* update FR/RETR timers on provisional replies */
|
|
|
|
+ if (msg_status<200) { /* provisional now */
|
|
|
|
+ if (t->is_invite) {
|
|
|
|
+ /* invite: change FR to longer FR_INV, do not
|
|
|
|
+ attempt to restart retransmission any more
|
|
|
|
+ */
|
|
|
|
+ set_timer( hash_table, & uac->request.fr_timer,
|
|
|
|
+ FR_INV_TIMER_LIST );
|
|
|
|
+ } else {
|
|
|
|
+ /* non-invite: restart retransmisssions (slow now) */
|
|
|
|
+ uac->request.retr_list=RT_T2;
|
|
|
|
+ set_timer( hash_table,
|
|
|
|
+ & uac->request.retr_timer, RT_T2 );
|
|
}
|
|
}
|
|
- } /* is_invite */
|
|
|
|
|
|
+ } /* provisional replies */
|
|
|
|
|
|
- /* restart retransmission if a provisional response came for
|
|
|
|
- a non_INVITE -> retrasmit at RT_T2*/
|
|
|
|
- if ( msg_class==1 && !is_invite )
|
|
|
|
- {
|
|
|
|
- rb->retr_list = RT_T2;
|
|
|
|
- set_timer( hash_table, &(rb->retr_timer), RT_T2 );
|
|
|
|
- }
|
|
|
|
-error1:
|
|
|
|
- if (start_fr)
|
|
|
|
- set_timer( hash_table, &(rb->fr_timer), FR_INV_TIMER_LIST );
|
|
|
|
- if (buf) pkg_free( buf );
|
|
|
|
-error:
|
|
|
|
- T_UNREF( T );
|
|
|
|
- /* don't try to relay statelessly on error; on troubles, simply do nothing;
|
|
|
|
- that will make the other party to retransmit; hopefuly, we'll then
|
|
|
|
- be better off */
|
|
|
|
|
|
+done:
|
|
|
|
+#ifdef _OBSOLETED
|
|
|
|
+ /* moved to script callback */
|
|
|
|
+ UNREF( t );
|
|
|
|
+ T=T_UNDEFINED;
|
|
|
|
+#endif
|
|
|
|
+ /* don't try to relay statelessly neither on success
|
|
|
|
+ (we forwarded statefuly) nor on error; on troubles,
|
|
|
|
+ simply do nothing; that will make the other party to
|
|
|
|
+ retransmit; hopefuly, we'll then be better off */
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
-/* Checks if the new reply (with new_code status) should be sent or not
|
|
|
|
|
|
+/* This is the neuralgical 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
|
|
* based on the current
|
|
* transactin status.
|
|
* transactin status.
|
|
* Returns - branch number (0,1,...) which should be relayed
|
|
* Returns - branch number (0,1,...) which should be relayed
|
|
* -1 if nothing to be relayed
|
|
* -1 if nothing to be relayed
|
|
*/
|
|
*/
|
|
-int t_should_relay_response( struct cell *Trans , int new_code,
|
|
|
|
- int branch , int *should_store )
|
|
|
|
|
|
+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 )
|
|
{
|
|
{
|
|
- //int T_code;
|
|
|
|
- int b, lowest_b, lowest_s;
|
|
|
|
-
|
|
|
|
- //if (Trans->uas.request->REQ_METHOD==METHOD_INVITE)
|
|
|
|
- // T_code = Trans->uac[branch].status;
|
|
|
|
- //else
|
|
|
|
- //T_code = Trans->uas.status;
|
|
|
|
|
|
+ int b, lowest_b, lowest_s, dummy;
|
|
|
|
|
|
/* note: this code never lets replies to CANCEL go through;
|
|
/* note: this code never lets replies to CANCEL go through;
|
|
we generate always a local 200 for CANCEL; 200s are
|
|
we generate always a local 200 for CANCEL; 200s are
|
|
@@ -480,58 +623,204 @@ int t_should_relay_response( struct cell *Trans , int new_code,
|
|
if (new_code>=200 && new_code < 300 &&
|
|
if (new_code>=200 && new_code < 300 &&
|
|
Trans->uas.request->REQ_METHOD==METHOD_INVITE) {
|
|
Trans->uas.request->REQ_METHOD==METHOD_INVITE) {
|
|
DBG("DBG: t_should_relay: 200 INV after final sent\n");
|
|
DBG("DBG: t_should_relay: 200 INV after final sent\n");
|
|
- *should_store=1;
|
|
|
|
- return branch;
|
|
|
|
|
|
+ *should_store=0;
|
|
|
|
+ Trans->uac[branch].last_received=new_code;
|
|
|
|
+ *should_relay=branch;
|
|
|
|
+ return RPS_PUSHED_AFTER_COMPLETION;
|
|
} else {
|
|
} else {
|
|
|
|
+ /* except the exception above, too late messages will
|
|
|
|
+ be discarded */
|
|
*should_store=0;
|
|
*should_store=0;
|
|
- return -1;
|
|
|
|
|
|
+ *should_relay=-1;
|
|
|
|
+ return RPS_DISCARDED;
|
|
}
|
|
}
|
|
- } else { /* no final response sent yet */
|
|
|
|
- /* negative replies subject to fork picking */
|
|
|
|
- if (new_code >=300 ) {
|
|
|
|
- /* dirty hack by Jiri -- subject to clean up as all the
|
|
|
|
- reply_processing crap; if there are no branches at
|
|
|
|
- all, I guess TM wants to reply itself and allow that
|
|
|
|
- */
|
|
|
|
- if (Trans->nr_of_outgoings==0)
|
|
|
|
- return 0;
|
|
|
|
- *should_store=1;
|
|
|
|
- /* if all_final return lowest */
|
|
|
|
- lowest_b=-1; lowest_s=999;
|
|
|
|
- for ( b=0; b<Trans->nr_of_outgoings ; b++ ) {
|
|
|
|
- /* "fake" for the currently processed branch */
|
|
|
|
- if (b==branch) {
|
|
|
|
- if (new_code<lowest_s) {
|
|
|
|
- lowest_b=b;
|
|
|
|
- lowest_s=new_code;
|
|
|
|
- }
|
|
|
|
- continue;
|
|
|
|
- }
|
|
|
|
- /* there is still an unfinished UAC transaction; wait now! */
|
|
|
|
- if ( Trans->uac[b].status<200 )
|
|
|
|
- return -1;
|
|
|
|
- if ( Trans->uac[b].status<lowest_s )
|
|
|
|
- {
|
|
|
|
- lowest_b =b;
|
|
|
|
- lowest_s = T->uac[b].status;
|
|
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* no final response sent yet */
|
|
|
|
+ /* negative replies subject to fork picking */
|
|
|
|
+ if (new_code >=300 ) {
|
|
|
|
+ /* negative reply received after we have received
|
|
|
|
+ a final reply previously -- discard , unless
|
|
|
|
+ a recoverable error occured, in which case
|
|
|
|
+ retry
|
|
|
|
+ */
|
|
|
|
+ if (Trans->uac[branch].last_received>=200) {
|
|
|
|
+ /* then drop! */
|
|
|
|
+ *should_store=0;
|
|
|
|
+ *should_relay=-1;
|
|
|
|
+ return RPS_DISCARDED;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ Trans->uac[branch].last_received=new_code;
|
|
|
|
+ /* if all_final return lowest */
|
|
|
|
+ lowest_b=-1; lowest_s=999;
|
|
|
|
+ for ( b=0; b<Trans->nr_of_outgoings ; b++ ) {
|
|
|
|
+ /* "fake" for the currently processed branch */
|
|
|
|
+ if (b==branch) {
|
|
|
|
+ if (new_code<lowest_s) {
|
|
|
|
+ lowest_b=b;
|
|
|
|
+ lowest_s=new_code;
|
|
}
|
|
}
|
|
|
|
+ continue;
|
|
|
|
+ }
|
|
|
|
+ /* skip 'empty branches' */
|
|
|
|
+ if (!Trans->uac[b].request.buffer) continue;
|
|
|
|
+ /* there is still an unfinished UAC transaction; wait now! */
|
|
|
|
+ if ( Trans->uac[b].last_received<200 ) {
|
|
|
|
+ *should_store=1;
|
|
|
|
+ *should_relay=-1;
|
|
|
|
+ return RPS_STORE;
|
|
|
|
+ }
|
|
|
|
+ if ( Trans->uac[b].last_received<lowest_s )
|
|
|
|
+ {
|
|
|
|
+ lowest_b =b;
|
|
|
|
+ lowest_s = Trans->uac[b].last_received;
|
|
|
|
+ }
|
|
|
|
+ } /* find lowest branch */
|
|
|
|
+ if (lowest_b==-1) {
|
|
|
|
+ LOG(L_CRIT, "ERROR: t_should_relay_response: lowest==-1\n");
|
|
|
|
+ }
|
|
|
|
+ /* no more pending branches -- try if that changes after
|
|
|
|
+ a callback
|
|
|
|
+ */
|
|
|
|
+ callback_event( TMCB_ON_NEGATIVE, Trans, 0, lowest_s );
|
|
|
|
+ /* look if the callback introduced new branches ... */
|
|
|
|
+ init_branch_iterator();
|
|
|
|
+ if (next_branch(&dummy)) {
|
|
|
|
+ if (t_forward_nonack(Trans, Trans->uas.request,
|
|
|
|
+ (struct proxy_l *) 0 ) <0) {
|
|
|
|
+ /* error ... behave as if we did not try to
|
|
|
|
+ add a new branch */
|
|
|
|
+ *should_store=0;
|
|
|
|
+ *should_relay=lowest_b;
|
|
|
|
+ return RPS_COMPLETED;
|
|
}
|
|
}
|
|
- return lowest_b;
|
|
|
|
- /* 1xx except 100 and 2xx will be relayed */
|
|
|
|
- } else if (new_code>100) {
|
|
|
|
|
|
+ /* we succeded to launch new branches -- await
|
|
|
|
+ result
|
|
|
|
+ */
|
|
*should_store=1;
|
|
*should_store=1;
|
|
- return branch;
|
|
|
|
|
|
+ *should_relay=-1;
|
|
|
|
+ return RPS_STORE;
|
|
}
|
|
}
|
|
- /* 100 won't be relayed */
|
|
|
|
- else {
|
|
|
|
- if (!T->uac[branch].rpl_received) *should_store=1;
|
|
|
|
- else *should_store=0;
|
|
|
|
- if (Trans->uas.status==0) return branch;
|
|
|
|
- else return -1;
|
|
|
|
|
|
+ /* look if the callback perhaps replied transaction */
|
|
|
|
+ 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;
|
|
}
|
|
}
|
|
|
|
+ /* really no more pending branches -- return lowest code */
|
|
|
|
+ *should_store=0;
|
|
|
|
+ *should_relay=lowest_b;
|
|
|
|
+ /* 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;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* 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);
|
|
|
|
+ *should_store=0;
|
|
|
|
+ *should_relay=-1;
|
|
|
|
+ return RPS_DISCARDED;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+char *build_ack(struct sip_msg* rpl,struct cell *trans,int branch,
|
|
|
|
+ int *ret_len)
|
|
|
|
+{
|
|
|
|
+ str to;
|
|
|
|
+
|
|
|
|
+ if ( parse_headers(rpl,HDR_TO, 0)==-1 || !rpl->to )
|
|
|
|
+ {
|
|
|
|
+ LOG(L_ERR, "ERROR: t_build_ACK: "
|
|
|
|
+ "cannot generate a HBH ACK if key HFs in reply missing\n");
|
|
|
|
+ return NULL;
|
|
|
|
+ }
|
|
|
|
+ to.len=rpl->to->body.s+rpl->to->body.len-rpl->to->name.s;
|
|
|
|
+ to.s=rpl->orig+(rpl->to->name.s-rpl->buf);
|
|
|
|
+ return build_local( trans, branch, ret_len,
|
|
|
|
+ ACK, ACK_LEN, &to );
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+void on_negative_reply( struct cell* t, struct sip_msg* msg,
|
|
|
|
+ int code, void *param )
|
|
|
|
+{
|
|
|
|
+ int act_ret;
|
|
|
|
+ struct sip_msg faked_msg;
|
|
|
|
+
|
|
|
|
+ /* nobody cares about a negative transaction -- ok, return */
|
|
|
|
+ if (!t->on_negative) {
|
|
|
|
+ DBG("DBG: on_negative_reply: no on_negative\n");
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ DBG("DBG: on_negative_reply processed for transaction %p\n", t);
|
|
|
|
+
|
|
|
|
+ /* create faked environment -- uri rewriting stuff needs the
|
|
|
|
+ original uri
|
|
|
|
+ */
|
|
|
|
+ memset( &faked_msg, 0, sizeof( struct sip_msg ));
|
|
|
|
+ /* original URI doesn't change -- feel free to refer to shmem */
|
|
|
|
+ faked_msg.first_line.u.request.uri=
|
|
|
|
+ t->uas.request->first_line.u.request.uri;
|
|
|
|
+ /* new_uri can change -- make a private copy */
|
|
|
|
+ if (t->uas.request->new_uri.s!=0 && t->uas.request->new_uri.len!=0) {
|
|
|
|
+ faked_msg.new_uri.s=pkg_malloc(t->uas.request->new_uri.len+1);
|
|
|
|
+ if (!faked_msg.new_uri.s) return;
|
|
|
|
+ faked_msg.new_uri.len=t->uas.request->new_uri.len;
|
|
|
|
+ memcpy( faked_msg.new_uri.s, t->uas.request->new_uri.s,
|
|
|
|
+ faked_msg.new_uri.len);
|
|
|
|
+ faked_msg.new_uri.s[faked_msg.new_uri.len]=0;
|
|
|
|
+ } else { faked_msg.new_uri.s=0; faked_msg.new_uri.len=0; }
|
|
|
|
+ faked_msg.flags=t->uas.request->flags;
|
|
|
|
+ /* if we set msg_id to something different from current's message
|
|
|
|
+ id, the first t_fork will properly clean new branch URIs
|
|
|
|
+ */
|
|
|
|
+ faked_msg.id=t->uas.request->id-1;
|
|
|
|
+
|
|
|
|
+ act_ret=run_actions(reply_rlist[t->on_negative], &faked_msg );
|
|
|
|
+
|
|
|
|
+ if (act_ret<0) {
|
|
|
|
+ LOG(L_ERR, "on_negative_reply: Error in do_action\n");
|
|
}
|
|
}
|
|
|
|
|
|
- LOG(L_CRIT, "ERROR: Oh my gooosh! We don't know whether to relay\n");
|
|
|
|
- abort();
|
|
|
|
|
|
+#ifdef _OBSOLETED
|
|
|
|
+ /* this didn't work becaue URI is a part of shmem "monoblock";
|
|
|
|
+ I could split it but it does not seem to be worth the
|
|
|
|
+ effor
|
|
|
|
+ */
|
|
|
|
+ /* project changes in faked message back to shmem copy */
|
|
|
|
+ t->uas.request->flags=faked_msg.flags;
|
|
|
|
+ if (faked_msg.new_uri.s) {
|
|
|
|
+ t->uas.request->new_uri.s=shm_resize(t->uas.request->new_uri.s,
|
|
|
|
+ faked_msg.new_uri.len);
|
|
|
|
+ if (!t->uas.request->new_uri.s) goto done;
|
|
|
|
+ memcpy(t->uas.request->new_uri.s, faked_msg.new_uri.s,
|
|
|
|
+ faked_msg.new_uri.len );
|
|
|
|
+ t->uas.request->new_uri.len=faked_msg.new_uri.len;
|
|
|
|
+ }
|
|
|
|
+done:
|
|
|
|
+#endif
|
|
|
|
+ /* destroy faked environment, new_uri in particular */
|
|
|
|
+ if (faked_msg.new_uri.s) pkg_free(faked_msg.new_uri.s);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+
|