c732d49e |
#if HAVE_CRT
#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>
#endif //HAVE_CRT
/*
* Copyright (C) 2017, University of the Basque Country (UPV/EHU)
* Contact for licensing options: <licensing-mcpttclient(at)mcopenplatform(dot)com>
*
* The original file was part of Open Source Doubango Framework
* Copyright (C) 2010-2011 Mamadou Diop.
* Copyright (C) 2012 Doubango Telecom <http://doubango.org>
*
* This file is part of Open Source Doubango Framework.
*
* DOUBANGO is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* DOUBANGO is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with DOUBANGO.
*
*/
/*=============================================================================
|Request received
|pass to TU
V
+-----------+
| |
| Trying |-------------+
| | |
+-----------+ |200-699 from TU
| |send response
|1xx from TU |
|send response |
| |
Request V 1xx from TU |
send response+-----------+send response|
+--------| |--------+ |
| | Proceeding| | |
+------->| |<-------+ |
+<--------------| | |
|Trnsprt Err +-----------+ |
|Inform TU | |
| | |
| |200-699 from TU |
| |send response |
| Request V |
| send response+-----------+ |
| +--------| | |
| | | Completed |<------------+
| +------->| |
+<--------------| |
|Trnsprt Err +-----------+
|Inform TU |
| |Timer J fires
| |-
| |
| V
| +-----------+
| | |
+-------------->| Terminated|
| |
+-----------+
=============================================================================*/
/**@file tsip_transac_nist.c
* @brief SIP Non-INVITE Server Transaction as per RFC 3261 subclause 17.2.2.
*
* @author Mamadou Diop <diopmamadou(at)doubango[dot]org>
*
*/
#include "tinysip/transactions/tsip_transac_nist.h"
#include "tsk_debug.h"
#define DEBUG_STATE_MACHINE 1
#define TRANSAC_NIST_TIMER_SCHEDULE(TX) TRANSAC_TIMER_SCHEDULE(nist, TX)
#define TRANSAC_NIST_SET_LAST_RESPONSE(self, response) \
if(response){ \
TSK_OBJECT_SAFE_FREE(self->lastResponse); \
self->lastResponse = tsk_object_ref((void*)response); \
}
/* ======================== internal functions ======================== */
int tsip_transac_nist_init(tsip_transac_nist_t *self);
int tsip_transac_nist_OnTerminated(tsip_transac_nist_t *self);
/* ======================== transitions ======================== */
int tsip_transac_nist_Started_2_Trying_X_request(va_list *app);
int tsip_transac_nist_Trying_2_Proceeding_X_send_1xx(va_list *app);
int tsip_transac_nist_Trying_2_Completed_X_send_200_to_699(va_list *app);
int tsip_transac_nist_Proceeding_2_Proceeding_X_send_1xx(va_list *app);
int tsip_transac_nist_Proceeding_2_Proceeding_X_request(va_list *app);
int tsip_transac_nist_Proceeding_2_Completed_X_send_200_to_699(va_list *app);
int tsip_transac_nist_Completed_2_Completed_X_request(va_list *app);
int tsip_transac_nist_Completed_2_Terminated_X_tirmerJ(va_list *app);
int tsip_transac_nist_Any_2_Terminated_X_transportError(va_list *app);
int tsip_transac_nist_Any_2_Terminated_X_Error(va_list *app);
int tsip_transac_nist_Any_2_Terminated_X_cancel(va_list *app); /* doubango-specific */
/* ======================== conds ======================== */
/* ======================== actions ======================== */
typedef enum _fsm_action_e
{
_fsm_action_cancel = tsip_atype_cancel,
_fsm_action_request = 0xFF,
_fsm_action_send_1xx,
_fsm_action_send_200_to_699,
_fsm_action_timerJ,
_fsm_action_transporterror,
_fsm_action_error,
}
_fsm_action_t;
/* ======================== states ======================== */
typedef enum _fsm_state_e
{
_fsm_state_Started,
_fsm_state_Trying,
_fsm_state_Proceeding,
_fsm_state_Completed,
_fsm_state_Terminated
}
_fsm_state_t;
int tsip_transac_nist_event_callback(const tsip_transac_nist_t *self, tsip_transac_event_type_t type, const tsip_message_t *msg)
{
int ret = -1;
switch(type)
{
case tsip_transac_incoming_msg: /* From Transport Layer to Transaction Layer */
{
if(msg && TSIP_MESSAGE_IS_REQUEST(msg)){
ret = tsip_transac_fsm_act(TSIP_TRANSAC(self), _fsm_action_request, msg);
}
break;
}
case tsip_transac_outgoing_msg: /* From TU to Transport Layer */
{
if(msg && TSIP_MESSAGE_IS_RESPONSE(msg))
{
if(TSIP_RESPONSE_IS_1XX(msg)){
ret = tsip_transac_fsm_act(TSIP_TRANSAC(self), _fsm_action_send_1xx, msg);
}
else if(TSIP_RESPONSE_IS_23456(msg)){
ret = tsip_transac_fsm_act(TSIP_TRANSAC(self), _fsm_action_send_200_to_699, msg);
}
}
break;
}
case tsip_transac_canceled:
case tsip_transac_terminated:
case tsip_transac_timedout:
break;
case tsip_transac_error:
{
ret = tsip_transac_fsm_act(TSIP_TRANSAC(self), _fsm_action_error, msg);
break;
}
case tsip_transac_transport_error:
{
ret = tsip_transac_fsm_act(TSIP_TRANSAC(self), _fsm_action_transporterror, msg);
break;
}
}
return ret;
}
int tsip_transac_nist_timer_callback(const tsip_transac_nist_t* self, tsk_timer_id_t timer_id)
{
int ret = -1;
if(self){
if(timer_id == self->timerJ.id){
ret = tsip_transac_fsm_act(TSIP_TRANSAC(self), _fsm_action_timerJ, tsk_null);
}
}
return ret;
}
int tsip_transac_nist_init(tsip_transac_nist_t *self)
{
/* Initialize the state machine.
*/
tsk_fsm_set(TSIP_TRANSAC_GET_FSM(self),
/*=======================
* === Started ===
*/
// Started -> (receive request) -> Trying
TSK_FSM_ADD_ALWAYS(_fsm_state_Started, _fsm_action_request, _fsm_state_Trying, tsip_transac_nist_Started_2_Trying_X_request, "tsip_transac_nist_Started_2_Trying_X_request"),
// Started -> (Any other) -> Started
TSK_FSM_ADD_ALWAYS_NOTHING(_fsm_state_Started, "tsip_transac_nist_Started_2_Started_X_any"),
/*=======================
* === Trying ===
*/
// Trying -> (receive request retransmission) -> Trying
TSK_FSM_ADD_ALWAYS(_fsm_state_Trying, _fsm_action_request, _fsm_state_Trying, tsk_null, "tsip_transac_nist_Trying_2_Trying_X_request"),
// Trying -> (send 1xx) -> Proceeding
TSK_FSM_ADD_ALWAYS(_fsm_state_Trying, _fsm_action_send_1xx, _fsm_state_Proceeding, tsip_transac_nist_Trying_2_Proceeding_X_send_1xx, "tsip_transac_nist_Trying_2_Proceeding_X_send_1xx"),
// Trying -> (send 200 to 699) -> Completed
TSK_FSM_ADD_ALWAYS(_fsm_state_Trying, _fsm_action_send_200_to_699, _fsm_state_Completed, tsip_transac_nist_Trying_2_Completed_X_send_200_to_699, "tsip_transac_nist_Trying_2_Completed_X_send_200_to_699"),
/*=======================
* === Proceeding ===
*/
// Proceeding -> (send 1xx) -> Proceeding
TSK_FSM_ADD_ALWAYS(_fsm_state_Proceeding, _fsm_action_send_1xx, _fsm_state_Proceeding, tsip_transac_nist_Proceeding_2_Proceeding_X_send_1xx, "tsip_transac_nist_Proceeding_2_Proceeding_X_send_1xx"),
// Proceeding -> (send 200 to 699) -> Completed
TSK_FSM_ADD_ALWAYS(_fsm_state_Proceeding, _fsm_action_send_200_to_699, _fsm_state_Completed, tsip_transac_nist_Proceeding_2_Completed_X_send_200_to_699, "tsip_transac_nist_Proceeding_2_Completed_X_send_200_to_699"),
// Proceeding -> (receive request) -> Proceeding
TSK_FSM_ADD_ALWAYS(_fsm_state_Proceeding, _fsm_action_request, _fsm_state_Proceeding, tsip_transac_nist_Proceeding_2_Proceeding_X_request, "tsip_transac_nist_Proceeding_2_Proceeding_X_request"),
/*=======================
* === Completed ===
*/
// Completed -> (receive request) -> Completed
TSK_FSM_ADD_ALWAYS(_fsm_state_Completed, _fsm_action_request, _fsm_state_Completed, tsip_transac_nist_Completed_2_Completed_X_request, "tsip_transac_nist_Completed_2_Completed_X_request"),
// Completed -> (timer J) -> Terminated
TSK_FSM_ADD_ALWAYS(_fsm_state_Completed, _fsm_action_timerJ, _fsm_state_Terminated, tsip_transac_nist_Completed_2_Terminated_X_tirmerJ, "tsip_transac_nist_Completed_2_Terminated_X_tirmerJ"),
/*=======================
* === Any ===
*/
// Any -> (transport error) -> Terminated
TSK_FSM_ADD_ALWAYS(tsk_fsm_state_any, _fsm_action_transporterror, _fsm_state_Terminated, tsip_transac_nist_Any_2_Terminated_X_transportError, "tsip_transac_nist_Any_2_Terminated_X_transportError"),
// Any -> (transport error) -> Terminated
TSK_FSM_ADD_ALWAYS(tsk_fsm_state_any, _fsm_action_error, _fsm_state_Terminated, tsip_transac_nist_Any_2_Terminated_X_Error, "tsip_transac_nist_Any_2_Terminated_X_Error"),
// Any -> (cancel) -> Terminated
TSK_FSM_ADD_ALWAYS(tsk_fsm_state_any, _fsm_action_cancel, _fsm_state_Terminated, tsip_transac_nist_Any_2_Terminated_X_cancel, "tsip_transac_nist_Any_2_Terminated_X_cancel"),
TSK_FSM_ADD_NULL());
/* Set callback function to call when new messages arrive or errors happen at
the transport layer.
*/
TSIP_TRANSAC(self)->callback = TSIP_TRANSAC_EVENT_CALLBACK_F(tsip_transac_nist_event_callback);
return 0;
}
tsip_transac_nist_t* tsip_transac_nist_create(int32_t cseq_value, const char* cseq_method, const char* callid, tsip_transac_dst_t* dst)
{
tsip_transac_nist_t* transac = tsk_object_new(tsip_transac_nist_def_t);
if(transac){
// initialize base class
tsip_transac_init(TSIP_TRANSAC(transac), tsip_transac_type_nist, cseq_value, cseq_method, callid, dst, _fsm_state_Started, _fsm_state_Terminated);
// init FSM
TSIP_TRANSAC_GET_FSM(transac)->debug = DEBUG_STATE_MACHINE;
tsk_fsm_set_callback_terminated(TSIP_TRANSAC_GET_FSM(transac), TSK_FSM_ONTERMINATED_F(tsip_transac_nist_OnTerminated), (const void*)transac);
// initialize NICT object
tsip_transac_nist_init(transac);
}
return transac;
}
int tsip_transac_nist_start(tsip_transac_nist_t *self, const tsip_request_t* request)
{
int ret = -1;
if(self && !TSIP_TRANSAC(self)->running && request){
TSIP_TRANSAC(self)->running = 1;
if((ret = tsip_transac_fsm_act(TSIP_TRANSAC(self), _fsm_action_request, request))){
//
}
}
return ret;
}
//--------------------------------------------------------
// == STATE MACHINE BEGIN ==
//--------------------------------------------------------
/* Started --> (INCOMING REQUEST) --> Trying
*/
int tsip_transac_nist_Started_2_Trying_X_request(va_list *app)
{
tsip_transac_nist_t *self = va_arg(*app, tsip_transac_nist_t *);
const tsip_request_t *request = va_arg(*app, const tsip_request_t *);
if(TNET_SOCKET_TYPE_IS_VALID(request->src_net_type)){
TSIP_TRANSAC(self)->reliable = TNET_SOCKET_TYPE_IS_STREAM(request->src_net_type);
}
/* Set Timers */
self->timerJ.timeout = TSIP_TRANSAC(self)->reliable ? 0 : TSIP_TIMER_GET(J); /* RFC 3261 - 17.2.2*/
/* RFC 3261 - 17.2.2
The state machine is initialized in the "Trying" state and is passed
a request other than INVITE or ACK when initialized. This request is
passed up to the TU. Once in the "Trying" state, any further request
retransmissions are discarded. A request is a retransmission if it
matches the same server transaction, using the rules specified in
Section 17.2.3.
*/
return tsip_transac_deliver(TSIP_TRANSAC(self), tsip_dialog_i_msg, request);
}
/* Trying --> (1xx) --> Proceeding
*/
int tsip_transac_nist_Trying_2_Proceeding_X_send_1xx(va_list *app)
{
tsip_transac_nist_t *self = va_arg(*app, tsip_transac_nist_t *);
const tsip_response_t *response = va_arg(*app, const tsip_response_t *);
int ret;
/* RFC 3261 - 17.2.2
While in the "Trying" state, if the TU passes a provisional response
to the server transaction, the server transaction MUST enter the
"Proceeding" state. The response MUST be passed to the transport
layer for transmission.
*/
ret = tsip_transac_send(TSIP_TRANSAC(self), TSIP_TRANSAC(self)->branch, TSIP_MESSAGE(response));
/* Update last response */
TRANSAC_NIST_SET_LAST_RESPONSE(self, response);
return ret;
}
/* Trying --> (200-699) --> Completed
*/
int tsip_transac_nist_Trying_2_Completed_X_send_200_to_699(va_list *app)
{
tsip_transac_nist_t *self = va_arg(*app, tsip_transac_nist_t *);
const tsip_response_t *response = va_arg(*app, const tsip_response_t *);
int ret;
ret = tsip_transac_send(TSIP_TRANSAC(self), TSIP_TRANSAC(self)->branch, TSIP_MESSAGE(response));
/* RFC 3261 - 17.2.2
When the server transaction enters the "Completed" state, it MUST set
Timer J to fire in 64*T1 seconds for unreliable transports, and zero
seconds for reliable transports.
*/
TRANSAC_NIST_TIMER_SCHEDULE(J);
/* Update last response */
TRANSAC_NIST_SET_LAST_RESPONSE(self, response);
return ret;
}
/* Proceeding --> (1xx) --> Proceeding
*/
int tsip_transac_nist_Proceeding_2_Proceeding_X_send_1xx(va_list *app)
{
tsip_transac_nist_t *self = va_arg(*app, tsip_transac_nist_t *);
const tsip_response_t *response = va_arg(*app, const tsip_response_t *);
/* RFC 3261 - 17.2.2
Any further provisional responses that are
received from the TU while in the "Proceeding" state MUST be passed
to the transport layer for transmission.
*/
tsip_transac_send(TSIP_TRANSAC(self), TSIP_TRANSAC(self)->branch, TSIP_MESSAGE(response));
/* Update last response */
TRANSAC_NIST_SET_LAST_RESPONSE(self, response);
return 0;
}
/* Proceeding -> (INCOMING REQUEST) -> Proceeding
*/
int tsip_transac_nist_Proceeding_2_Proceeding_X_request(va_list *app)
{
tsip_transac_nist_t *self = va_arg(*app, tsip_transac_nist_t *);
/*const tsip_response_t *response = va_arg(*app, const tsip_response_t *);*/
/* RFC 3261 - 17.2.2
If a retransmission of the request is received while in the "Proceeding" state, the most
recently sent provisional response MUST be passed to the transport
layer for retransmission.
*/
if(self->lastResponse){
tsip_transac_send(TSIP_TRANSAC(self), TSIP_TRANSAC(self)->branch, self->lastResponse);
}
return 0;
}
/* Proceeding --> (200-699) --> Completed
*/
int tsip_transac_nist_Proceeding_2_Completed_X_send_200_to_699(va_list *app)
{
tsip_transac_nist_t *self = va_arg(*app, tsip_transac_nist_t *);
const tsip_response_t *response = va_arg(*app, const tsip_response_t *);
int ret;
/* RFC 3261 - 17.2.2
If the TU passes a final response (status
codes 200-699) to the server while in the "Proceeding" state, the
transaction MUST enter the "Completed" state, and the response MUST
be passed to the transport layer for transmission.
*/
ret = tsip_transac_send(TSIP_TRANSAC(self), TSIP_TRANSAC(self)->branch, TSIP_MESSAGE(response));
/* RFC 3261 - 17.2.2
When the server transaction enters the "Completed" state, it MUST set
Timer J to fire in 64*T1 seconds for unreliable transports, and zero
seconds for reliable transports.
*/
TRANSAC_NIST_TIMER_SCHEDULE(J);
/* Update last response */
TRANSAC_NIST_SET_LAST_RESPONSE(self, response);
return ret;
}
/* Completed --> (INCOMING REQUEST) --> Completed
*/
int tsip_transac_nist_Completed_2_Completed_X_request(va_list *app)
{
tsip_transac_nist_t *self = va_arg(*app, tsip_transac_nist_t *);
/*const tsip_response_t *response = va_arg(*app, const tsip_response_t *);*/
/* RFC 3261 - 17.2.2
While in the "Completed" state, the server transaction MUST pass the final response to the transport
layer for retransmission whenever a retransmission of the request is received.
*/
if(self->lastResponse){
tsip_transac_send(TSIP_TRANSAC(self), TSIP_TRANSAC(self)->branch, self->lastResponse);
}
return 0;
}
/* Complete --> (Timer J) --> Terminated
*/
int tsip_transac_nist_Completed_2_Terminated_X_tirmerJ(va_list *app)
{
/*tsip_transac_nist_t *self = va_arg(*app, tsip_transac_nist_t *);*/
/*const tsip_response_t *response = va_arg(*app, const tsip_response_t *);*/
/* RFC 3261 - 17.2.2
The server transaction remains in this state (Completed) until Timer J fires, at
which point it MUST transition to the "Terminated" state.
*/
/* RFC 3261 - 17.2.2
THE SERVER TRANSACTION MUST BE DESTROYED THE INSTANT IT ENTERS THE "TERMINATED" STATE.
*/
return 0;
}
/* Any -> (Transport Error) -> Terminated
*/
int tsip_transac_nist_Any_2_Terminated_X_transportError(va_list *app)
{
tsip_transac_nist_t *self = va_arg(*app, tsip_transac_nist_t *);
//const tsip_message_t *message = va_arg(*app, const tsip_message_t *);
/* Timers will be canceled by "tsip_transac_nict_OnTerminated" */
return tsip_transac_deliver(TSIP_TRANSAC(self), tsip_dialog_transport_error, tsk_null);
}
/* Any -> (Error) -> Terminated
*/
int tsip_transac_nist_Any_2_Terminated_X_Error(va_list *app)
{
tsip_transac_nist_t *self = va_arg(*app, tsip_transac_nist_t *);
//const tsip_message_t *message = va_arg(*app, const tsip_message_t *);
/* Timers will be canceled by "tsip_transac_nict_OnTerminated" */
return tsip_transac_deliver(TSIP_TRANSAC(self), tsip_dialog_error, tsk_null);
}
/* Any -> (cancel) -> Terminated
*/
int tsip_transac_nist_Any_2_Terminated_X_cancel(va_list *app)
{
/* doubango-specific */
return 0;
}
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// == STATE MACHINE END ==
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++
/*== Callback function called when the state machine enter in the "terminated" state.
*/
int tsip_transac_nist_OnTerminated(tsip_transac_nist_t *self)
{
TSK_DEBUG_INFO("=== NIST terminated ===");
/* Remove (and destroy) the transaction from the layer. */
return tsip_transac_remove(TSIP_TRANSAC(self));
}
//========================================================
// NIST object definition
//
static tsk_object_t* tsip_transac_nist_ctor(tsk_object_t * self, va_list * app)
{
tsip_transac_nist_t *transac = self;
if(transac){
}
return self;
}
static tsk_object_t* tsip_transac_nist_dtor(tsk_object_t * _self)
{
tsip_transac_nist_t *self = _self;
if(self){
/* Cancel timers */
TRANSAC_TIMER_CANCEL(J);
TSIP_TRANSAC(self)->running = tsk_false;
TSK_OBJECT_SAFE_FREE(self->lastResponse);
/* DeInitialize base class */
tsip_transac_deinit(TSIP_TRANSAC(self));
TSK_DEBUG_INFO("*** NIST destroyed ***");
}
return _self;
}
static int tsip_transac_nist_cmp(const tsk_object_t *t1, const tsk_object_t *t2)
{
return tsip_transac_cmp(t1, t2);
}
static const tsk_object_def_t tsip_transac_nist_def_s =
{
sizeof(tsip_transac_nist_t),
tsip_transac_nist_ctor,
tsip_transac_nist_dtor,
tsip_transac_nist_cmp,
};
const tsk_object_def_t *tsip_transac_nist_def_t = &tsip_transac_nist_def_s; |