diff options
Diffstat (limited to 'tinySIP/src/transactions')
-rw-r--r-- | tinySIP/src/transactions/tsip_transac.c | 274 | ||||
-rw-r--r-- | tinySIP/src/transactions/tsip_transac_ict.c | 927 | ||||
-rw-r--r-- | tinySIP/src/transactions/tsip_transac_ist.c | 823 | ||||
-rw-r--r-- | tinySIP/src/transactions/tsip_transac_layer.c | 352 | ||||
-rw-r--r-- | tinySIP/src/transactions/tsip_transac_nict.c | 731 | ||||
-rw-r--r-- | tinySIP/src/transactions/tsip_transac_nist.c | 583 |
6 files changed, 3690 insertions, 0 deletions
diff --git a/tinySIP/src/transactions/tsip_transac.c b/tinySIP/src/transactions/tsip_transac.c new file mode 100644 index 0000000..1ecac59 --- /dev/null +++ b/tinySIP/src/transactions/tsip_transac.c @@ -0,0 +1,274 @@ +/* +* Copyright (C) 2010-2011 Mamadou Diop. +* +* Contact: Mamadou Diop <diopmamadou(at)doubango[dot]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. +* +*/ + +/**@file tsip_transac.c + * @brief SIP transaction base class as per RFC 3261 subclause 17. + * + * @author Mamadou Diop <diopmamadou(at)doubango[dot]org> + * + + */ +#include "tinysip/transactions/tsip_transac.h" + +#include "tinysip/transports/tsip_transport_layer.h" +#include "tinysip/transactions/tsip_transac_layer.h" + +#include "tinysip/transactions/tsip_transac_ist.h" +#include "tinysip/transactions/tsip_transac_nist.h" +#include "tinysip/transactions/tsip_transac_nict.h" +#include "tinysip/transactions/tsip_transac_ict.h" + +#include "tsk_string.h" +#include "tsk_memory.h" +#include "tsk_debug.h" + +static tsk_object_t* tsip_transac_dst_ctor(tsk_object_t * _self, va_list * app) +{ + tsip_transac_dst_t *dst = _self; + if(dst){ + + } + return _self; +} +static tsk_object_t* tsip_transac_dst_dtor(tsk_object_t * _self) +{ + tsip_transac_dst_t *dst = _self; + if(dst){ + TSK_OBJECT_SAFE_FREE(dst->stack); + switch(dst->type){ + case tsip_transac_dst_type_dialog: + { + TSK_OBJECT_SAFE_FREE(dst->dialog.dlg); + break; + } + case tsip_transac_dst_type_net: + { + break; + } + } + } + return _self; +} +static const tsk_object_def_t tsip_transac_dst_def_s = +{ + sizeof(tsip_transac_dst_t), + tsip_transac_dst_ctor, + tsip_transac_dst_dtor, + tsk_null, +}; +const tsk_object_def_t *tsip_transac_dst_def_t = &tsip_transac_dst_def_s; + +static struct tsip_transac_dst_s* tsip_transac_dst_create(tsip_transac_dst_type_t type, struct tsip_stack_s* stack) +{ + struct tsip_transac_dst_s* dst = tsk_object_new(tsip_transac_dst_def_t); + if(dst){ + dst->type = type; + dst->stack = tsk_object_ref(stack); + } + return dst; +} + +struct tsip_transac_dst_s* tsip_transac_dst_dialog_create(tsip_dialog_t *dlg) +{ + struct tsip_transac_dst_s* dst; + if((dst = tsip_transac_dst_create(tsip_transac_dst_type_dialog, TSIP_DIALOG_GET_STACK(dlg)))){ + dst->dialog.dlg = tsk_object_ref(dlg); + } + return dst; +} + +struct tsip_transac_dst_s* tsip_transac_dst_net_create(struct tsip_stack_s* stack) +{ + struct tsip_transac_dst_s* dst; + if((dst = tsip_transac_dst_create(tsip_transac_dst_type_net, stack))){ + } + return dst; +} + +static int tsip_transac_dst_deliver(struct tsip_transac_dst_s* self, tsip_dialog_event_type_t event_type, const tsip_message_t *msg) +{ + if(!self){ + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + + switch(self->type){ + case tsip_transac_dst_type_dialog: + { + return self->dialog.dlg->callback( + self->dialog.dlg, + event_type, + msg + ); + } + case tsip_transac_dst_type_net: + { + if(!msg){ + TSK_DEBUG_ERROR("Message is null"); + return -1; + } + + // all messages coming from WebSocket transport have to be updated (AoR, Via...) before network delivering + // all other messages MUST not unless specified from the dialog layer + TSIP_MESSAGE(msg)->update |= (TNET_SOCKET_TYPE_IS_WS(msg->src_net_type) || TNET_SOCKET_TYPE_IS_WSS(msg->src_net_type)); + + return tsip_transport_layer_send( + self->stack->layer_transport, + msg->firstVia ? msg->firstVia->branch : tsk_null, + TSIP_MESSAGE(msg) + ); + } + default: + { + TSK_DEBUG_ERROR("Unexpected code called"); + return -2; + } + } +} + + +int tsip_transac_init(tsip_transac_t *self, tsip_transac_type_t type, int32_t cseq_value, const char* cseq_method, const char* callid, struct tsip_transac_dst_s* dst, tsk_fsm_state_id curr, tsk_fsm_state_id term) +{ + if(self && !self->initialized){ + self->type = type; + self->cseq_value = cseq_value; + tsk_strupdate(&self->cseq_method, cseq_method); + tsk_strupdate(&self->callid, callid); + self->dst = tsk_object_ref(dst); + + /* FSM */ + self->fsm = tsk_fsm_create(curr, term); + + self->initialized = tsk_true; + + return 0; + } + return -1; +} + +int tsip_transac_deinit(tsip_transac_t *self) +{ + if(self && self->initialized){ + /* FSM */ + TSK_OBJECT_SAFE_FREE(self->fsm); + + TSK_FREE(self->branch); + TSK_FREE(self->cseq_method); + TSK_FREE(self->callid); + TSK_OBJECT_SAFE_FREE(self->dst); + + self->initialized = tsk_false; + + return 0; + } + return -1; +} + +int tsip_transac_start(tsip_transac_t *self, const tsip_request_t* request) +{ + if(!self){ + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + + switch(self->type){ + case tsip_transac_type_nist:{ + return tsip_transac_nist_start(TSIP_TRANSAC_NIST(self), request); + } + case tsip_transac_type_ist:{ + return tsip_transac_ist_start(TSIP_TRANSAC_IST(self), request); + } + case tsip_transac_type_nict:{ + return tsip_transac_nict_start(TSIP_TRANSAC_NICT(self), request); + } + case tsip_transac_type_ict:{ + return tsip_transac_ict_start(TSIP_TRANSAC_ICT(self), request); + } + } + + TSK_DEBUG_ERROR("Unexpected code called"); + return -2; +} + +// deliver the message to the destination (e.g. local dialog) +int tsip_transac_deliver(tsip_transac_t* self, tsip_dialog_event_type_t event_type, const tsip_message_t *msg) +{ + if(!self){ + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + return tsip_transac_dst_deliver(self->dst, event_type, msg); +} + +// send the message over the network +int tsip_transac_send(tsip_transac_t *self, const char *branch, tsip_message_t *msg) +{ + if(self && TSIP_TRANSAC_GET_STACK(self)->layer_transport && msg){ + const struct tsip_ssession_s* ss = TSIP_TRANSAC_GET_SESSION(self); + if(ss){ + // set SigComp identifier as the message is directly sent to the transport layer + tsk_strupdate(&msg->sigcomp_id, ss->sigcomp_id); + } + return tsip_transport_layer_send(TSIP_TRANSAC_GET_STACK(self)->layer_transport, branch, TSIP_MESSAGE(msg)); + } + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; +} + +int tsip_transac_cmp(const tsip_transac_t *t1, const tsip_transac_t *t2) +{ + if(t1 && t2){ + if(tsk_strequals(t1->branch, t2->branch) && tsk_strequals(t1->cseq_method, t2->cseq_method)){ + return 0; + } + } + return -1; +} + +int tsip_transac_remove(const tsip_transac_t* self) +{ + int ret; + tsip_transac_t* safe_copy; + + safe_copy = (tsip_transac_t*)tsk_object_ref(TSK_OBJECT(self)); + ret = tsip_transac_layer_remove(TSIP_TRANSAC_GET_STACK(self)->layer_transac, safe_copy); + tsk_object_unref(safe_copy); + + return ret; +} + +int tsip_transac_fsm_act(tsip_transac_t* self, tsk_fsm_action_id action_id, const tsip_message_t* message) +{ + int ret; + tsip_transac_t* safe_copy; + + if(!self || !self->fsm){ + TSK_DEBUG_WARN("Invalid parameter."); + return -1; + } + + safe_copy = tsk_object_ref(TSK_OBJECT(self)); + ret = tsk_fsm_act(self->fsm, action_id, safe_copy, message, self, message); + tsk_object_unref(safe_copy); + + return ret; +} diff --git a/tinySIP/src/transactions/tsip_transac_ict.c b/tinySIP/src/transactions/tsip_transac_ict.c new file mode 100644 index 0000000..8678851 --- /dev/null +++ b/tinySIP/src/transactions/tsip_transac_ict.c @@ -0,0 +1,927 @@ +/* +* Copyright (C) 2010-2011 Mamadou Diop. +* +* Contact: Mamadou Diop <diopmamadou(at)doubango[dot]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. +* +*/ + +/*============================================================================= +* IMPORTANT: The INVITE Client Transaction (ICT) implements corrections defined in 'draft-sparks-sip-invfix-02.txt'. +* which fixes RFC 3261. This will alow us to easily suppoort forking. + + |INVITE from TU + Timer A fires |INVITE sent Timer B fired + Reset A, V or Transport Err. + INVITE sent +-----------+ inform TU + +---------| |--------------------------+ + | | Calling | | + +-------->| |-----------+ | + 300-699 +-----------+ 2xx | | + ACK sent | | 2xx to TU | | + resp. to TU | |1xx | | + +-----------------------------+ |1xx to TU | | + | | | | + | 1xx V | | + | 1xx to TU +-----------+ | | + | +---------| | | | + | | |Proceeding | | | + | +-------->| | | | + | +-----------+ 2xx | | + | 300-699 | | 2xx to TU | | + | ACK sent, +--------+ +---------------+ | + | resp. to TU| | | + | | | | + | V V | + | +-----------+ +----------+ | + +------------->| |Transport Err. | | | + | Completed |Inform TU | Accepted | | + +--| |-------+ | |-+ | + 300-699 | +-----------+ | +----------+ | | + ACK sent| ^ | | | ^ | | + | | | | | | | | + +----+ | | | +-----+ | + |Timer D fires | Timer M fires| 2xx | + |- | - | 2xx to TU | + +--------+ | +-----------+ | + NOTE: V V V | + transitions +------------+ | + labeled with | | | + the event | Terminated |<-----------------------+ + over the action | | + to take +------------+ + + + draft-sparks-sip-invfix-03.txt - Figure 3: INVITE client transaction + +=============================================================================*/ + +/**@file tsip_transac_ict.c + * @brief SIP INVITE Client Transaction as per RFC 3261 subclause 17.1.1. + * + * @author Mamadou Diop <diopmamadou(at)doubango[dot]org> + * + + */ +#include "tinysip/transactions/tsip_transac_ict.h" + +#include "tsk_debug.h" + +#define DEBUG_STATE_MACHINE 0 + +#define TRANSAC_ICT_TIMER_SCHEDULE(TX) TRANSAC_TIMER_SCHEDULE(ict, TX) + +/* ======================== internal functions ======================== */ +int tsip_transac_ict_init(tsip_transac_ict_t *self); +int tsip_transac_ict_send_ACK(tsip_transac_ict_t *self, const tsip_response_t* response); // ACK +int tsip_transac_ict_OnTerminated(tsip_transac_ict_t *self); + +/* ======================== transitions ======================== */ +int tsip_transac_ict_Started_2_Calling_X_send(va_list *app); +int tsip_transac_ict_Calling_2_Calling_X_timerA(va_list *app); +int tsip_transac_ict_Calling_2_Terminated_X_timerB(va_list *app); +int tsip_transac_ict_Calling_2_Completed_X_300_to_699(va_list *app); +int tsip_transac_ict_Calling_2_Proceeding_X_1xx(va_list *app); +int tsip_transac_ict_Calling_2_Accepted_X_2xx(va_list *app); +int tsip_transac_ict_Proceeding_2_Proceeding_X_1xx(va_list *app); +int tsip_transac_ict_Proceeding_2_Completed_X_300_to_699(va_list *app); +int tsip_transac_ict_Proceeding_2_Accepted_X_2xx(va_list *app); +int tsip_transac_ict_Completed_2_Completed_X_300_to_699(va_list *app); +int tsip_transac_ict_Completed_2_Terminated_X_timerD(va_list *app); +int tsip_transac_ict_Accepted_2_Accepted_X_2xx(va_list *app); +int tsip_transac_ict_Accepted_2_Terminated_X_timerM(va_list *app); +int tsip_transac_ict_Any_2_Terminated_X_transportError(va_list *app); +int tsip_transac_ict_Any_2_Terminated_X_Error(va_list *app); +int tsip_transac_ict_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_send = 0xFF, + _fsm_action_timerA, + _fsm_action_timerB, + _fsm_action_timerD, + _fsm_action_timerM, + _fsm_action_1xx, + _fsm_action_2xx, + _fsm_action_300_to_699, + _fsm_action_transporterror, + _fsm_action_error, +} +_fsm_action_t; + +/* ======================== states ======================== */ +typedef enum _fsm_state_e +{ + _fsm_state_Started, + _fsm_state_Calling, + _fsm_state_Proceeding, + _fsm_state_Completed, + _fsm_state_Accepted, + _fsm_state_Terminated +} +_fsm_state_t; + + +/** + * Callback function called by the transport layer to alert the transaction for incoming messages + * or errors (e.g. transport error). + * + * @param [in,out] self A pointer to the IC transaction. + * @param type The event type. + * @param [in,out] msg The incoming message. + * + * @return Zero if succeed and no-zero error code otherwise. +**/ +int tsip_transac_ict_event_callback(const tsip_transac_ict_t *self, tsip_transac_event_type_t type, const tsip_message_t *msg) +{ + /* draft-sparks-sip-invfix-03 - 7.2. UAC Impacts + Any response received which does not match an existing client transaction state machine is simply dropped. + */ + switch(type){ + + case tsip_transac_incoming_msg: + { + if(msg) + { + if(TSIP_MESSAGE_IS_RESPONSE(msg)){ + if(TSIP_RESPONSE_IS_1XX(msg)){ + return tsip_transac_fsm_act(TSIP_TRANSAC(self), _fsm_action_1xx, msg); + } + else if(TSIP_RESPONSE_IS_2XX(msg)){ + return tsip_transac_fsm_act(TSIP_TRANSAC(self), _fsm_action_2xx, msg); + } + else if(TSIP_RESPONSE_IS_3456(msg)){ + return tsip_transac_fsm_act(TSIP_TRANSAC(self), _fsm_action_300_to_699, msg); + } + else{ + TSK_DEBUG_WARN("Not supported status code: %d", TSIP_RESPONSE_CODE(msg)); + return 0; + } + } + // any other response have to be delivered if dst_type is 'network' + if(TSIP_TRANSAC(self)->dst->type == tsip_transac_dst_type_net){ + return tsip_transac_deliver(TSIP_TRANSAC(self), tsip_transac_incoming_msg, msg); + } + } + break; + } + + case tsip_transac_canceled: + case tsip_transac_terminated: + case tsip_transac_timedout: + break; + + case tsip_transac_error: + { + return tsip_transac_fsm_act(TSIP_TRANSAC(self), _fsm_action_error, msg); + } + + case tsip_transac_transport_error: + { + return tsip_transac_fsm_act(TSIP_TRANSAC(self), _fsm_action_transporterror, msg); + } + + default: break; + } + + return 0; +} + +int tsip_transac_ict_timer_callback(const tsip_transac_ict_t* self, tsk_timer_id_t timer_id) +{ + int ret = -1; + + if(self && TSIP_TRANSAC(self)) + { + if(timer_id == self->timerA.id){ + ret = tsip_transac_fsm_act(TSIP_TRANSAC(self), _fsm_action_timerA, tsk_null); + } + else if(timer_id == self->timerB.id){ + ret = tsip_transac_fsm_act(TSIP_TRANSAC(self), _fsm_action_timerB, tsk_null); + } + else if(timer_id == self->timerD.id){ + ret = tsip_transac_fsm_act(TSIP_TRANSAC(self), _fsm_action_timerD, tsk_null); + } + else if(timer_id == self->timerM.id){ + ret = tsip_transac_fsm_act(TSIP_TRANSAC(self), _fsm_action_timerM, tsk_null); + } + } + + return ret; +} + + +/** Initializes the transaction. + * + * @author Mamadou + * @date 12/24/2009 + * + * @param [in,out] self The transaction to initialize. +**/ +int tsip_transac_ict_init(tsip_transac_ict_t *self) +{ + /* Initialize the state machine. */ + tsk_fsm_set(TSIP_TRANSAC_GET_FSM(self), + + /*======================= + * === Started === + */ + // Started -> (Send) -> Calling + TSK_FSM_ADD_ALWAYS(_fsm_state_Started, _fsm_action_send, _fsm_state_Calling, tsip_transac_ict_Started_2_Calling_X_send, "tsip_transac_ict_Started_2_Calling_X_send"), + // Started -> (Any) -> Started + TSK_FSM_ADD_ALWAYS_NOTHING(_fsm_state_Started, "tsip_transac_ict_Started_2_Started_X_any"), + + /*======================= + * === Calling === + */ + // Calling -> (timerA) -> Calling + TSK_FSM_ADD_ALWAYS(_fsm_state_Calling, _fsm_action_timerA, _fsm_state_Calling, tsip_transac_ict_Calling_2_Calling_X_timerA, "tsip_transac_ict_Calling_2_Calling_X_timerA"), + // Calling -> (timerB) -> Terminated + TSK_FSM_ADD_ALWAYS(_fsm_state_Calling, _fsm_action_timerB, _fsm_state_Terminated, tsip_transac_ict_Calling_2_Terminated_X_timerB, "tsip_transac_ict_Calling_2_Terminated_X_timerB"), + // Calling -> (300-699) -> Completed + TSK_FSM_ADD_ALWAYS(_fsm_state_Calling, _fsm_action_300_to_699, _fsm_state_Completed, tsip_transac_ict_Calling_2_Completed_X_300_to_699, "tsip_transac_ict_Calling_2_Completed_X_300_to_699"), + // Calling -> (1xx) -> Proceeding + TSK_FSM_ADD_ALWAYS(_fsm_state_Calling, _fsm_action_1xx, _fsm_state_Proceeding, tsip_transac_ict_Calling_2_Proceeding_X_1xx, "tsip_transac_ict_Calling_2_Proceeding_X_1xx"), + // Calling -> (2xx) -> Accepted + TSK_FSM_ADD_ALWAYS(_fsm_state_Calling, _fsm_action_2xx, _fsm_state_Accepted, tsip_transac_ict_Calling_2_Accepted_X_2xx, "tsip_transac_ict_Calling_2_Accepted_X_2xx"), + + /*======================= + * === Proceeding === + */ + // Proceeding -> (1xx) -> Proceeding + TSK_FSM_ADD_ALWAYS(_fsm_state_Proceeding, _fsm_action_1xx, _fsm_state_Proceeding, tsip_transac_ict_Proceeding_2_Proceeding_X_1xx, "tsip_transac_ict_Proceeding_2_Proceeding_X_1xx"), + // Proceeding -> (300-699) -> Completed + TSK_FSM_ADD_ALWAYS(_fsm_state_Proceeding, _fsm_action_300_to_699, _fsm_state_Completed, tsip_transac_ict_Proceeding_2_Completed_X_300_to_699, "tsip_transac_ict_Proceeding_2_Completed_X_300_to_699"), + // Proceeding -> (2xx) -> Accepted + TSK_FSM_ADD_ALWAYS(_fsm_state_Proceeding, _fsm_action_2xx, _fsm_state_Accepted, tsip_transac_ict_Proceeding_2_Accepted_X_2xx, "tsip_transac_ict_Proceeding_2_Accepted_X_2xx"), + + /*======================= + * === Completed === + */ + // Completed -> (300-699) -> Completed + TSK_FSM_ADD_ALWAYS(_fsm_state_Completed, _fsm_action_300_to_699, _fsm_state_Completed, tsip_transac_ict_Completed_2_Completed_X_300_to_699, "tsip_transac_ict_Completed_2_Completed_X_300_to_699"), + // Completed -> (timerD) -> Terminated + TSK_FSM_ADD_ALWAYS(_fsm_state_Completed, _fsm_action_timerD, _fsm_state_Terminated, tsip_transac_ict_Completed_2_Terminated_X_timerD, "tsip_transac_ict_Completed_2_Terminated_X_timerD"), + + /*======================= + * === Accepted === + */ + // Accepted -> (2xx) -> Accepted + TSK_FSM_ADD_ALWAYS(_fsm_state_Accepted, _fsm_action_2xx, _fsm_state_Accepted, tsip_transac_ict_Accepted_2_Accepted_X_2xx, "tsip_transac_ict_Accepted_2_Accepted_X_2xx"), + // Accepted -> (timerM) -> Terminated + TSK_FSM_ADD_ALWAYS(_fsm_state_Accepted, _fsm_action_timerM, _fsm_state_Terminated, tsip_transac_ict_Accepted_2_Terminated_X_timerM, "tsip_transac_ict_Accepted_2_Terminated_X_timerM"), + + /*======================= + * === Any === + */ + // Any -> (transport error) -> Terminated + TSK_FSM_ADD_ALWAYS(tsk_fsm_state_any, _fsm_action_transporterror, _fsm_state_Terminated, tsip_transac_ict_Any_2_Terminated_X_transportError, "tsip_transac_ict_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_ict_Any_2_Terminated_X_Error, "tsip_transac_ict_Any_2_Terminated_X_Error"), + // Any -> (cancel) -> Terminated + TSK_FSM_ADD_ALWAYS(tsk_fsm_state_any, _fsm_action_cancel, _fsm_state_Terminated, tsip_transac_ict_Any_2_Terminated_X_cancel, "tsip_transac_ict_Any_2_Terminated_X_cancel"), + + TSK_FSM_ADD_NULL()); + + + /* Set callback function to call when new messages arrive or errors happen in + the transport layer. + */ + TSIP_TRANSAC(self)->callback = TSIP_TRANSAC_EVENT_CALLBACK_F(tsip_transac_ict_event_callback); + + /* Timers */ + self->timerA.id = TSK_INVALID_TIMER_ID; + self->timerB.id = TSK_INVALID_TIMER_ID; + self->timerD.id = TSK_INVALID_TIMER_ID; + self->timerM.id = TSK_INVALID_TIMER_ID; + + self->timerA.timeout = TSIP_TIMER_GET(A); + self->timerB.timeout = TSIP_TIMER_GET(B); + self->timerM.timeout = TSIP_TIMER_GET(M); + + return 0; +} + +tsip_transac_ict_t* tsip_transac_ict_create(int32_t cseq_value, const char* callid, tsip_transac_dst_t* dst) +{ + tsip_transac_ict_t* transac = tsk_object_new(tsip_transac_ict_def_t); + if(transac){ + // initialize base class + tsip_transac_init(TSIP_TRANSAC(transac), tsip_transac_type_ict, cseq_value, "INVITE", 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_ict_OnTerminated), (const void*)transac); + + // initialize ICT object + tsip_transac_ict_init(transac); + } + return transac; +} + +/** + * Starts the client transaction. + * + * @param [in,out] self The client transaction to start. + * @param [in,out] request The SIP/IMS INVITE request to send. + * + * @return Zero if succeed and non-zero error code otherwise. +**/ +int tsip_transac_ict_start(tsip_transac_ict_t *self, const tsip_request_t* request) +{ + int ret = -1; + if(self && request && !TSIP_TRANSAC(self)->running){ + /* Add branch to the new client transaction + * - Transac will use request branch if exit (e.g. when request received over websocket) + */ + if((request->firstVia && !tsk_strnullORempty(request->firstVia->branch))){ + tsk_strupdate(&TSIP_TRANSAC(self)->branch, (request->firstVia ? request->firstVia->branch : "doubango")); + } + else if((TSIP_TRANSAC(self)->branch = tsk_strdup(TSIP_TRANSAC_MAGIC_COOKIE))){ + tsk_istr_t branch; + tsk_strrandom(&branch); + tsk_strcat_2(&(TSIP_TRANSAC(self)->branch), "-%s", branch); + } + + TSIP_TRANSAC(self)->running = 1; + self->request = tsk_object_ref((void*)request); + + ret = tsip_transac_fsm_act(TSIP_TRANSAC(self), _fsm_action_send, tsk_null); + } + return ret; +} + + + + + +//-------------------------------------------------------- +// == STATE MACHINE BEGIN == +//-------------------------------------------------------- + +/* Started -> (send) -> Calling +*/ +int tsip_transac_ict_Started_2_Calling_X_send(va_list *app) +{ + tsip_transac_ict_t *self = va_arg(*app, tsip_transac_ict_t *); + //const tsip_message_t *message = va_arg(*app, const tsip_message_t *); + + //== Send the request + tsip_transac_send(TSIP_TRANSAC(self), TSIP_TRANSAC(self)->branch, TSIP_MESSAGE(self->request)); + + // Now that the first request is sent using the best transport mean we know if it's reliable or not + if(TNET_SOCKET_TYPE_IS_VALID(self->request->dst_net_type)){ + TSIP_TRANSAC(self)->reliable = TNET_SOCKET_TYPE_IS_STREAM(self->request->dst_net_type); + self->timerD.timeout = TSIP_TRANSAC(self)->reliable ? 0 : TSIP_TIMER_GET(D); + } + + /* RFC 3261 - 17.1.1.2 Formal Description + If an unreliable transport is being used, the client transaction MUST + start timer A with a value of T1. + If a reliable transport is being used, the client transaction SHOULD + NOT start timer A (Timer A controls request retransmissions). For + any transport, the client transaction MUST start timer B with a value + of 64*T1 seconds (Timer B controls transaction timeouts). + */ + if(!TSIP_TRANSAC(self)->reliable){ + TRANSAC_ICT_TIMER_SCHEDULE(A); + } + TRANSAC_ICT_TIMER_SCHEDULE(B); + + return 0; +} + +/* Calling -> (timerA) -> Calling +*/ +int tsip_transac_ict_Calling_2_Calling_X_timerA(va_list *app) +{ + tsip_transac_ict_t *self = va_arg(*app, tsip_transac_ict_t *); + //const tsip_message_t *message = va_arg(*app, const tsip_message_t *); + + /* RFC 3261 - 17.1.1.2 Formal Description + When timer A fires, the client transaction MUST retransmit the + request by passing it to the transport layer, and MUST reset the + timer with a value of 2*T1. The formal definition of retransmit + + within the context of the transaction layer is to take the message + previously sent to the transport layer and pass it to the transport + layer once more. + + When timer A fires 2*T1 seconds later, the request MUST be + retransmitted again (assuming the client transaction is still in this + state). This process MUST continue so that the request is + retransmitted with intervals that double after each transmission. + These retransmissions SHOULD only be done while the client + transaction is in the "calling" state. + */ + + //== Send the request + tsip_transac_send(TSIP_TRANSAC(self), TSIP_TRANSAC(self)->branch, self->request); + + self->timerA.timeout *= 2; /* Will not raise indefinitely ==> see timer B */ + TRANSAC_ICT_TIMER_SCHEDULE(A); + + return 0; +} + +/* Calling -> (timerB) -> Terminated +*/ +int tsip_transac_ict_Calling_2_Terminated_X_timerB(va_list *app) +{ + tsip_transac_ict_t *self = va_arg(*app, tsip_transac_ict_t *); + //const tsip_message_t *message = va_arg(*app, const tsip_message_t *); + + /* RFC 3261 - 17.1.1.2 Formal Description + If the client transaction is still in the "Calling" state when timer + B fires, the client transaction SHOULD inform the TU that a timeout + has occurred. The client transaction MUST NOT generate an ACK. The + value of 64*T1 is equal to the amount of time required to send seven + requests in the case of an unreliable transport. + */ + tsip_transac_deliver(TSIP_TRANSAC(self), tsip_dialog_timedout, tsk_null); + + return 0; +} + +/* Calling -> (300-699) -> Completed +*/ +int tsip_transac_ict_Calling_2_Completed_X_300_to_699(va_list *app) +{ + tsip_transac_ict_t *self = va_arg(*app, tsip_transac_ict_t *); + const tsip_response_t *response = va_arg(*app, const tsip_response_t *); + int ret; + + /* draft-sparks-sip-invfix-03 - 8.4. Pages 126 through 128 + When in either the "Calling" or "Proceeding" states, reception of + a response with status code from 300-699 MUST cause the client + transaction to transition to "Completed". The client transaction + MUST pass the received response up to the TU, and the client + transaction MUST generate an ACK request, even if the transport is + reliable (guidelines for constructing the ACK from the response + are given in Section 17.1.1.3) and then pass the ACK to the + transport layer for transmission. The ACK MUST be sent to the + same address, port, and transport to which the original request + was sent. + */ + /* Do not retransmit */ + if(!TSIP_TRANSAC(self)->reliable){ + TRANSAC_TIMER_CANCEL(A); + } + TRANSAC_TIMER_CANCEL(B); /* Now it's up to the UAS to update the FSM. */ + + /* draft-sparks-sip-invfix-03 - 8.4. Pages 126 through 128 + The client transaction MUST start timer D when it enters the + "Completed" state for any reason, with a value of at least 32 + seconds for unreliable transports, and a value of zero seconds for + reliable transports. Timer D reflects the amount of time that the + server transaction can remain in the "Completed" state when + unreliable transports are used. + */ + TRANSAC_ICT_TIMER_SCHEDULE(D); /* timerD already have the right value (0 if reliable and non-zero otherwise) */ + + /* Send ACK */ + if((ret = tsip_transac_ict_send_ACK(self, response))){ + return ret; + } + + /* Pass the response to the dialog. */ + return tsip_transac_deliver(TSIP_TRANSAC(self), tsip_dialog_i_msg, response); +} + +/* Calling -> (1xx) -> Proceeding +*/ +int tsip_transac_ict_Calling_2_Proceeding_X_1xx(va_list *app) +{ + tsip_transac_ict_t *self = va_arg(*app, tsip_transac_ict_t *); + const tsip_response_t *response = va_arg(*app, const tsip_response_t *); + + /* RFC 3261 - 17.1.1.2 Formal Description + If the client transaction receives a provisional response while in + the "Calling" state, it transitions to the "Proceeding" state. In the + "Proceeding" state, the client transaction SHOULD NOT retransmit the + request any longer. Furthermore, the provisional response MUST be + passed to the TU. Any further provisional responses MUST be passed + up to the TU while in the "Proceeding" state. + */ + + /* Do not retransmit */ + if(!TSIP_TRANSAC(self)->reliable){ + TRANSAC_TIMER_CANCEL(A); + } + TRANSAC_TIMER_CANCEL(B); /* Now it's up to the UAS to update the FSM. */ + + /* Pass the provisional response to the dialog. */ + tsip_transac_deliver(TSIP_TRANSAC(self), tsip_dialog_i_msg, response); + + return 0; +} + +/* Calling -> (2xx) -> Accepted +*/ +int tsip_transac_ict_Calling_2_Accepted_X_2xx(va_list *app) +{ + tsip_transac_ict_t *self = va_arg(*app, tsip_transac_ict_t *); + const tsip_response_t *response = va_arg(*app, const tsip_response_t *); + + /* draft-sparks-sip-invfix-03 - 8.4. Pages 126 through 128 + When a 2xx response is received while in either the "Calling" or + "Proceeding" states, the client transaction MUST transition to the + "Accepted" state, and Timer M MUST be started with a value of + 64*T1. The 2xx response MUST be passed up to the TU. The client + transaction MUST NOT generate an ACK to the 2xx response - its + handling is delegated to the TU. + */ + + /* Schedule timer M */ + TRANSAC_ICT_TIMER_SCHEDULE(M); + + /* Cancel timers A and B */ + if(!TSIP_TRANSAC(self)->reliable){ + TRANSAC_ICT_TIMER_SCHEDULE(A); + } + TRANSAC_ICT_TIMER_SCHEDULE(B); + + /* pass the response to the TU (dialog) */ + return tsip_transac_deliver(TSIP_TRANSAC(self), tsip_dialog_i_msg, response); +} + +/* Proceeding -> (1xx) -> Proceeding +*/ +int tsip_transac_ict_Proceeding_2_Proceeding_X_1xx(va_list *app) +{ + tsip_transac_ict_t *self = va_arg(*app, tsip_transac_ict_t *); + const tsip_response_t *response = va_arg(*app, const tsip_response_t *); + + /* pass the response to the TU (dialog) */ + return tsip_transac_deliver(TSIP_TRANSAC(self), tsip_dialog_i_msg, response); +} + +/* Proceeding -> (300-699) -> Completed +*/ +int tsip_transac_ict_Proceeding_2_Completed_X_300_to_699(va_list *app) +{ + tsip_transac_ict_t *self = va_arg(*app, tsip_transac_ict_t *); + const tsip_response_t *response = va_arg(*app, const tsip_response_t *); + int ret; + + /* draft-sparks-sip-invfix-03 - 8.4. Pages 126 through 128 + When in either the "Calling" or "Proceeding" states, reception of + a response with status code from 300-699 MUST cause the client + transaction to transition to "Completed". The client transaction + MUST pass the received response up to the TU, and the client + transaction MUST generate an ACK request, even if the transport is + reliable (guidelines for constructing the ACK from the response + are given in Section 17.1.1.3) and then pass the ACK to the + transport layer for transmission. The ACK MUST be sent to the + same address, port, and transport to which the original request + was sent. + */ + /* Do not retransmit */ + if(!TSIP_TRANSAC(self)->reliable){ + TRANSAC_TIMER_CANCEL(A); + } + TRANSAC_TIMER_CANCEL(B); /* Now it's up to the UAS to update the FSM. */ + + /* draft-sparks-sip-invfix-03 - 8.4. Pages 126 through 128 + The client transaction MUST start timer D when it enters the + "Completed" state for any reason, with a value of at least 32 + seconds for unreliable transports, and a value of zero seconds for + reliable transports. Timer D reflects the amount of time that the + server transaction can remain in the "Completed" state when + unreliable transports are used. + */ + TRANSAC_ICT_TIMER_SCHEDULE(D); /* timerD already have the right value (0 if reliable and non-zero otherwise) */ + + /* Send ACK */ + if((ret = tsip_transac_ict_send_ACK(self, response))){ + return ret; + } + + /* Pass the response to the dialog. */ + ret = tsip_transac_deliver(TSIP_TRANSAC(self), tsip_dialog_i_msg, response); + + return ret; +} + +/* Proceeding -> (2xx) -> Accepted +*/ +int tsip_transac_ict_Proceeding_2_Accepted_X_2xx(va_list *app) +{ + tsip_transac_ict_t *self = va_arg(*app, tsip_transac_ict_t *); + const tsip_response_t *response = va_arg(*app, const tsip_response_t *); + + /* draft-sparks-sip-invfix-03 - 8.4. Pages 126 through 128 + When a 2xx response is received while in either the "Calling" or + "Proceeding" states, the client transaction MUST transition to the + "Accepted" state, and Timer M MUST be started with a value of + 64*T1. The 2xx response MUST be passed up to the TU. The client + transaction MUST NOT generate an ACK to the 2xx response - its + handling is delegated to the TU. + */ + + /* Schedule timer M */ + TRANSAC_ICT_TIMER_SCHEDULE(M); + + /* Cancel timers A and B */ + if(!TSIP_TRANSAC(self)->reliable){ + TRANSAC_ICT_TIMER_SCHEDULE(A); + } + TRANSAC_ICT_TIMER_SCHEDULE(B); + + /* pass the response to the TU (dialog) */ + return tsip_transac_deliver(TSIP_TRANSAC(self), tsip_dialog_i_msg, response); +} + +/* Completed -> (300-699) -> Completed +*/ +int tsip_transac_ict_Completed_2_Completed_X_300_to_699(va_list *app) +{ + tsip_transac_ict_t *self = va_arg(*app, tsip_transac_ict_t *); + const tsip_response_t *response = va_arg(*app, const tsip_response_t *); + + /* draft-sparks-sip-invfix-03 - 8.4. Pages 126 through 128 + Any retransmissions of a response with status code 300-699 that + are received while in the "Completed" state MUST cause the ACK to + be re-passed to the transport layer for retransmission, but the + newly received response MUST NOT be passed up to the TU. + */ + + return tsip_transac_ict_send_ACK(self, response); +} + +/* Completed -> (timerD) -> Terminated +*/ +int tsip_transac_ict_Completed_2_Terminated_X_timerD(va_list *app) +{ + /* draft-sparks-sip-invfix-03 - 8.4. Pages 126 through 128 + If timer D fires while the client transaction is in the + "Completed" state, the client transaction MUST move to the + "Terminated" state. + */ + + /* Timers will be canceled by "tsip_transac_ict_OnTerminated" */ + return 0; +} + +/* Accepted -> (2xx) -> Accepted +*/ +int tsip_transac_ict_Accepted_2_Accepted_X_2xx(va_list *app) +{ + tsip_transac_ict_t *self = va_arg(*app, tsip_transac_ict_t *); + const tsip_response_t *response = va_arg(*app, const tsip_response_t *); + + /* draft-sparks-sip-invfix-03 - 7.2. UAC Impacts + A 2xx response received while in the "Accepted" state MUST be passed to the TU and + the machine remains in the "Accepted" state. The client transaction + MUST NOT generate an ACK to any 2xx response on its own. The TU + responsible for the transaction will generate the ACK. + */ + + /* Pass the response to the TU. */ + tsip_transac_deliver(TSIP_TRANSAC(self), tsip_dialog_i_msg, response); + + return 0; +} + +/* Accepted -> (timerM) -> Terminated +*/ +int tsip_transac_ict_Accepted_2_Terminated_X_timerM(va_list *app) +{ + /* draft-sparks-sip-invfix-03 - 8.4. Pages 126 through 128 + If timer M fires while the client transaction is in the "Accepted" + state, the client transaction MUST move to the "Terminated" state. + */ + return 0; +} + +/* Any -> (Transport Error) -> Terminated +*/ +int tsip_transac_ict_Any_2_Terminated_X_transportError(va_list *app) +{ + tsip_transac_ict_t *self = va_arg(*app, tsip_transac_ict_t *); + //const tsip_message_t *message = va_arg(*app, const tsip_message_t *); + + /* Timers will be canceled by "tsip_transac_ict_OnTerminated" */ + + return tsip_transac_deliver(TSIP_TRANSAC(self), tsip_dialog_transport_error, tsk_null); +} + +/* Any -> (Error) -> Terminated +*/ +int tsip_transac_ict_Any_2_Terminated_X_Error(va_list *app) +{ + tsip_transac_ict_t *self = va_arg(*app, tsip_transac_ict_t *); + //const tsip_message_t *message = va_arg(*app, const tsip_message_t *); + + /* Timers will be canceled by "tsip_transac_ict_OnTerminated" */ + + return tsip_transac_deliver(TSIP_TRANSAC(self), tsip_dialog_error, tsk_null); +} + +/* Any -> (cancel) -> Terminated +*/ +int tsip_transac_ict_Any_2_Terminated_X_cancel(va_list *app) +{ + /* doubango-specific */ + return 0; +} + + +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +// == STATE MACHINE END == +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +/* Send ACK message +*/ +int tsip_transac_ict_send_ACK(tsip_transac_ict_t *self, const tsip_response_t* response) +{ + int ret = -1; + tsip_request_t *request = tsk_null; + const tsk_list_item_t* item; + + if(!self || !self->request || !response){ + goto bail; + } + + // check lastINVITE + if( !self->request->firstVia || + !self->request->From || + !self->request->line.request.uri || + !self->request->Call_ID || + !self->request->CSeq) + { + ret = -2; + goto bail; + } + + // check response + if(!response->To){ + ret = -3; + goto bail; + } + + /* RFC 3261 - 17.1.1.3 Construction of the ACK Request + + The ACK request constructed by the client transaction MUST contain + values for the Call-ID, From, and Request-URI that are equal to the + values of those header fields in the request passed to the transport + by the client transaction (call this the "original request"). The To + header field in the ACK MUST equal the To header field in the + response being acknowledged, and therefore will usually differ from + the To header field in the original request by the addition of the + tag parameter. The ACK MUST contain a single Via header field, and + this MUST be equal to the top Via header field of the original + request. The CSeq header field in the ACK MUST contain the same + value for the sequence number as was present in the original request, + but the method parameter MUST be equal to "ACK". + + If the INVITE request whose response is being acknowledged had Route + header fields, those header fields MUST appear in the ACK. This is + to ensure that the ACK can be routed properly through any downstream + stateless proxies. + + Although any request MAY contain a body, a body in an ACK is special + since the request cannot be rejected if the body is not understood. + Therefore, placement of bodies in ACK for non-2xx is NOT RECOMMENDED, + but if done, the body types are restricted to any that appeared in + the INVITE, assuming that the response to the INVITE was not 415. If + it was, the body in the ACK MAY be any type listed in the Accept + header field in the 415. + */ + if((request = tsip_request_new("ACK", self->request->line.request.uri, self->request->From->uri, response->To->uri, self->request->Call_ID->value, self->request->CSeq->seq))){ + // Via + request->firstVia = tsk_object_ref((void*)self->request->firstVia); + // tags + if(request->From){ + request->From->tag = tsk_strdup(self->request->From->tag); + } + if(request->To){ + request->To->tag = tsk_strdup(response->To->tag); + } + // Routes + tsk_list_foreach(item, self->request->headers){ + const tsip_header_t* curr = item->data; + if(curr->type == tsip_htype_Route){ + tsip_message_add_header(request, curr); + } + } + + // SigComp + if(TSIP_TRANSAC_GET_SESSION(self) && TSIP_TRANSAC_GET_SESSION(self)->sigcomp_id){ + request->sigcomp_id = tsk_strdup(TSIP_TRANSAC_GET_SESSION(self)->sigcomp_id); + } + + // send the request + ret = tsip_transac_send(TSIP_TRANSAC(self), request->firstVia->branch, request); + TSK_OBJECT_SAFE_FREE(request); + } + +bail: + return ret; +} + +/*== TERMINATED +*/ +int tsip_transac_ict_OnTerminated(tsip_transac_ict_t *self) +{ + /* draft-sparks-sip-invfix-03 - 8.4. Pages 126 through 128 + The client transaction MUST be destroyed the instant it enters the "Terminated" state. + */ + TSK_DEBUG_INFO("=== ICT terminated ==="); + + /* Remove (and destroy) the transaction from the layer. */ + return tsip_transac_remove(TSIP_TRANSAC(self)); +} + + + + + + + + + + + + + + + + + + + + + + + + + + + +//======================================================== +// ICT object definition +// +static tsk_object_t* tsip_transac_ict_ctor(tsk_object_t * self, va_list * app) +{ + tsip_transac_ict_t *transac = self; + if(transac){ + } + return self; +} + +static tsk_object_t* tsip_transac_ict_dtor(tsk_object_t * _self) +{ + tsip_transac_ict_t *self = _self; + if(self){ + /* Cancel timers */ + if(!TSIP_TRANSAC(self)->reliable){ + TRANSAC_TIMER_CANCEL(A); + } + TRANSAC_TIMER_CANCEL(B); + TRANSAC_TIMER_CANCEL(D); + TRANSAC_TIMER_CANCEL(M); + + TSIP_TRANSAC(self)->running = tsk_false; + TSK_OBJECT_SAFE_FREE(self->request); + + /* DeInitialize base class */ + tsip_transac_deinit(TSIP_TRANSAC(self)); + + TSK_DEBUG_INFO("*** ICT destroyed ***"); + } + return _self; +} + +static int tsip_transac_ict_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_ict_def_s = +{ + sizeof(tsip_transac_ict_t), + tsip_transac_ict_ctor, + tsip_transac_ict_dtor, + tsip_transac_ict_cmp, +}; +const tsk_object_def_t *tsip_transac_ict_def_t = &tsip_transac_ict_def_s; diff --git a/tinySIP/src/transactions/tsip_transac_ist.c b/tinySIP/src/transactions/tsip_transac_ist.c new file mode 100644 index 0000000..f4f9233 --- /dev/null +++ b/tinySIP/src/transactions/tsip_transac_ist.c @@ -0,0 +1,823 @@ +/* +* Copyright (C) 2010-2011 Mamadou Diop. +* +* Contact: Mamadou Diop <diopmamadou(at)doubango[dot]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. +* +*/ + +/*============================================================================= + + |INVITE + |pass INV to TU + INVITE V send 100 if TU won't in 200ms + send response+------------+ + +--------| |--------+ 101-199 from TU + | | | | send response + +------->| |<-------+ + | Proceeding | + | |--------+ Transport Err. + | | | Inform TU + | |<-------+ + +------------+ + 300-699 from TU | |2xx from TU + send response | |send response + +--------------+ +------------+ + | | + INVITE V Timer G fires | + send response +-----------+ send response | + +--------| |--------+ | + | | | | | + +------->| Completed |<-------+ INVITE | Transport Err. + | | - | Inform TU + +--------| |----+ +-----+ | +---+ + | +-----------+ | ACK | | v | v + | ^ | | - | +------------+ + | | | | | | | + +----------+ | | +->| Accepted | + Transport Err. | | | | + Inform TU | V +------------+ + | +-----------+ | ^ | + | | | | | | + | | Confirmed | | +-----+ + | | | | 2xx from TU + Timer H fires | +-----------+ | send response + - | | | + | | Timer I fires | + | | - | Timer L fires + | V | - + | +------------+ | + | | |<----+ + +------->| Terminated | + | | + +------------+ + + + + draft-sparks-sip-invfix-03 - Figure 5: INVITE server transaction + +=============================================================================*/ + +/**@file tsip_transac_ist.c + * @brief SIP INVITE Server Transaction as per RFC 3261 subclause 17.2.1. + * + * @author Mamadou Diop <diopmamadou(at)doubango[dot]org> + * + + */ +#include "tinysip/transactions/tsip_transac_ist.h" + + +#include "tsk_debug.h" + +#define DEBUG_STATE_MACHINE 1 + +#define TRANSAC_IST_TIMER_SCHEDULE(TX) TRANSAC_TIMER_SCHEDULE(ist, TX) +#define TRANSAC_IST_SET_LAST_RESPONSE(self, response) \ + if(response){ \ + TSK_OBJECT_SAFE_FREE(self->lastResponse); \ + self->lastResponse = tsk_object_ref((void*)response); \ + } + +/* ======================== internal functions ======================== */ +static int tsip_transac_ist_init(tsip_transac_ist_t *self); +static int tsip_transac_ist_OnTerminated(tsip_transac_ist_t *self); + +/* ======================== transitions ======================== */ +static int tsip_transac_ist_Started_2_Proceeding_X_INVITE(va_list *app); +static int tsip_transac_ist_Proceeding_2_Proceeding_X_INVITE(va_list *app); +static int tsip_transac_ist_Proceeding_2_Proceeding_X_1xx(va_list *app); +static int tsip_transac_ist_Proceeding_2_Completed_X_300_to_699(va_list *app); +static int tsip_transac_ist_Proceeding_2_Accepted_X_2xx(va_list *app); +static int tsip_transac_ist_Completed_2_Completed_INVITE(va_list *app); +static int tsip_transac_ist_Completed_2_Completed_timerG(va_list *app); +static int tsip_transac_ist_Completed_2_Terminated_timerH(va_list *app); +static int tsip_transac_ist_Completed_2_Confirmed_ACK(va_list *app); +static int tsip_transac_ist_Accepted_2_Accepted_INVITE(va_list *app); +static int tsip_transac_ist_Accepted_2_Accepted_2xx(va_list *app); +static int tsip_transac_ist_Accepted_2_Accepted_timerX(va_list *app); /* doubango-specific */ +static int tsip_transac_ist_Accepted_2_Accepted_iACK(va_list *app); /* doubango-specific */ +static int tsip_transac_ist_Accepted_2_Terminated_timerL(va_list *app); +static int tsip_transac_ist_Confirmed_2_Terminated_timerI(va_list *app); +static int tsip_transac_ist_Any_2_Terminated_X_transportError(va_list *app); +static int tsip_transac_ist_Any_2_Terminated_X_Error(va_list *app); +static int tsip_transac_ist_Any_2_Terminated_X_cancel(va_list *app); /* doubango-specific */ + +/* ======================== conds ======================== */ +static tsk_bool_t _fsm_cond_is_resp2INVITE(tsip_transac_ist_t* self, tsip_message_t* message) +{ + return TSIP_RESPONSE_IS_TO_INVITE(message); +} + +/* ======================== actions ======================== */ +typedef enum _fsm_action_e +{ + _fsm_action_cancel = tsip_atype_cancel, + + _fsm_action_recv_INVITE = 0xFF, + _fsm_action_recv_ACK, + _fsm_action_send_1xx, + _fsm_action_send_2xx, + _fsm_action_send_300_to_699, + _fsm_action_send_non1xx, + _fsm_action_timerH, + _fsm_action_timerI, + _fsm_action_timerG, + _fsm_action_timerL, + _fsm_action_timerX, + _fsm_action_transporterror, + _fsm_action_error, +} +_fsm_action_t; + +/* ======================== states ======================== */ +typedef enum _fsm_state_e +{ + _fsm_state_Started, + _fsm_state_Proceeding, + _fsm_state_Completed, + _fsm_state_Accepted, + _fsm_state_Confirmed, + _fsm_state_Terminated +} +_fsm_state_t; + +int tsip_transac_ist_event_callback(const tsip_transac_ist_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)){ + if(TSIP_REQUEST_IS_INVITE(msg)){ + ret = tsip_transac_fsm_act(TSIP_TRANSAC(self), _fsm_action_recv_INVITE, msg); + } + else if(TSIP_REQUEST_IS_ACK(msg)){ + ret = tsip_transac_fsm_act(TSIP_TRANSAC(self), _fsm_action_recv_ACK, 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_2XX(msg)){ + ret = tsip_transac_fsm_act(TSIP_TRANSAC(self), _fsm_action_send_2xx, msg); + } + else if(TSIP_RESPONSE_IS_3456(msg)){ + ret = tsip_transac_fsm_act(TSIP_TRANSAC(self), _fsm_action_send_300_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_ist_timer_callback(const tsip_transac_ist_t* self, tsk_timer_id_t timer_id) +{ + int ret = -1; + + if(self){ + if(timer_id == self->timerH.id){ + ret = tsip_transac_fsm_act(TSIP_TRANSAC(self), _fsm_action_timerH, tsk_null); + } + else if(timer_id == self->timerI.id){ + ret = tsip_transac_fsm_act(TSIP_TRANSAC(self), _fsm_action_timerI, tsk_null); + } + else if(timer_id == self->timerG.id){ + ret = tsip_transac_fsm_act(TSIP_TRANSAC(self), _fsm_action_timerG, tsk_null); + } + else if(timer_id == self->timerL.id){ + ret = tsip_transac_fsm_act(TSIP_TRANSAC(self), _fsm_action_timerL, tsk_null); + } + else if(timer_id == self->timerX.id){ + ret = tsip_transac_fsm_act(TSIP_TRANSAC(self), _fsm_action_timerX, tsk_null); + } + } + + return ret; +} + +int tsip_transac_ist_init(tsip_transac_ist_t *self) +{ + /* Initialize the state machine. + */ + tsk_fsm_set(TSIP_TRANSAC_GET_FSM(self), + + /*======================= + * === Started === + */ + // Started -> (recv INVITE) -> Proceeding + TSK_FSM_ADD_ALWAYS(_fsm_state_Started, _fsm_action_recv_INVITE, _fsm_state_Proceeding, tsip_transac_ist_Started_2_Proceeding_X_INVITE, "tsip_transac_ist_Started_2_Proceeding_X_INVITE"), + // Started -> (Any other) -> Started + TSK_FSM_ADD_ALWAYS_NOTHING(_fsm_state_Started, "tsip_transac_ist_Started_2_Started_X_any"), + + /*======================= + * === Proceeding === + */ + // Proceeding -> (recv INVITE) -> Proceeding + TSK_FSM_ADD_ALWAYS(_fsm_state_Proceeding, _fsm_action_recv_INVITE, _fsm_state_Proceeding, tsip_transac_ist_Proceeding_2_Proceeding_X_INVITE, "tsip_transac_ist_Proceeding_2_Proceeding_X_INVITE"), + // Proceeding -> (send 1xx) -> Proceeding + TSK_FSM_ADD(_fsm_state_Proceeding, _fsm_action_send_1xx, _fsm_cond_is_resp2INVITE, _fsm_state_Proceeding, tsip_transac_ist_Proceeding_2_Proceeding_X_1xx, "tsip_transac_ist_Proceeding_2_Proceeding_X_1xx"), + // Proceeding -> (send 300to699) -> Completed + TSK_FSM_ADD(_fsm_state_Proceeding, _fsm_action_send_300_to_699, _fsm_cond_is_resp2INVITE, _fsm_state_Completed, tsip_transac_ist_Proceeding_2_Completed_X_300_to_699, "tsip_transac_ist_Proceeding_2_Completed_X_300_to_699"), + // Proceeding -> (send 2xx) -> Accepted + TSK_FSM_ADD(_fsm_state_Proceeding, _fsm_action_send_2xx, _fsm_cond_is_resp2INVITE, _fsm_state_Accepted, tsip_transac_ist_Proceeding_2_Accepted_X_2xx, "tsip_transac_ist_Proceeding_2_Accepted_X_2xx"), + + /*======================= + * === Completed === + */ + // Completed -> (recv INVITE) -> Completed + TSK_FSM_ADD_ALWAYS(_fsm_state_Completed, _fsm_action_recv_INVITE, _fsm_state_Completed, tsip_transac_ist_Completed_2_Completed_INVITE, "tsip_transac_ist_Completed_2_Completed_INVITE"), + // Completed -> (timer G) -> Completed + TSK_FSM_ADD_ALWAYS(_fsm_state_Completed, _fsm_action_timerG, _fsm_state_Completed, tsip_transac_ist_Completed_2_Completed_timerG, "tsip_transac_ist_Completed_2_Completed_timerG"), + // Completed -> (timerH) -> Terminated + TSK_FSM_ADD_ALWAYS(_fsm_state_Completed, _fsm_action_timerH, _fsm_state_Terminated, tsip_transac_ist_Completed_2_Terminated_timerH, "tsip_transac_ist_Completed_2_Terminated_timerH"), + // Completed -> (recv ACK) -> Confirmed + TSK_FSM_ADD_ALWAYS(_fsm_state_Completed, _fsm_action_recv_ACK, _fsm_state_Confirmed, tsip_transac_ist_Completed_2_Confirmed_ACK, "tsip_transac_ist_Completed_2_Confirmed_ACK"), + + /*======================= + * === Accepted === + */ + // Accepted -> (recv INVITE) -> Accepted + TSK_FSM_ADD_ALWAYS(_fsm_state_Accepted, _fsm_action_recv_INVITE, _fsm_state_Accepted, tsip_transac_ist_Accepted_2_Accepted_INVITE, "tsip_transac_ist_Accepted_2_Accepted_INVITE"), + // Accepted -> (send 2xx) -> Accepted + TSK_FSM_ADD(_fsm_state_Accepted, _fsm_action_send_2xx, _fsm_cond_is_resp2INVITE, _fsm_state_Accepted, tsip_transac_ist_Accepted_2_Accepted_2xx, "tsip_transac_ist_Accepted_2_Accepted_2xx"), + // Accepted -> (timer X) -> Accepted + TSK_FSM_ADD_ALWAYS(_fsm_state_Accepted, _fsm_action_timerX, _fsm_state_Accepted, tsip_transac_ist_Accepted_2_Accepted_timerX, "tsip_transac_ist_Accepted_2_Accepted_timerX"), + // Accepted -> (recv ACK) -> Accepted + TSK_FSM_ADD_ALWAYS(_fsm_state_Accepted, _fsm_action_recv_ACK, _fsm_state_Accepted, tsip_transac_ist_Accepted_2_Accepted_iACK, "tsip_transac_ist_Accepted_2_Accepted_iACK"), + // Accepted -> (timerL) -> Terminated + TSK_FSM_ADD_ALWAYS(_fsm_state_Accepted, _fsm_action_timerL, _fsm_state_Terminated, tsip_transac_ist_Accepted_2_Terminated_timerL, "tsip_transac_ist_Accepted_2_Terminated_timerL"), + + /*======================= + * === Confirmed === + */ + // Confirmed -> (timerI) -> Terminated + TSK_FSM_ADD_ALWAYS(_fsm_state_Confirmed, _fsm_action_timerI, _fsm_state_Terminated, tsip_transac_ist_Confirmed_2_Terminated_timerI, "tsip_transac_ist_Confirmed_2_Terminated_timerI"), + + + /*======================= + * === Any === + */ + // Any -> (transport error) -> Terminated + TSK_FSM_ADD_ALWAYS(tsk_fsm_state_any, _fsm_action_transporterror, _fsm_state_Terminated, tsip_transac_ist_Any_2_Terminated_X_transportError, "tsip_transac_ist_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_ist_Any_2_Terminated_X_Error, "tsip_transac_ist_Any_2_Terminated_X_Error"), + // Any -> (cancel) -> Terminated + TSK_FSM_ADD_ALWAYS(tsk_fsm_state_any, _fsm_action_cancel, _fsm_state_Terminated, tsip_transac_ist_Any_2_Terminated_X_cancel, "tsip_transac_ist_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_ist_event_callback); + + /* Set Timers + * RFC 3261 17.2.1: For unreliable transports, timer G is set to fire in T1 seconds, and is not set to fire for + reliable transports. + */ + self->timerH.timeout = TSIP_TIMER_GET(H); + self->timerG.timeout = TSIP_TIMER_GET(G); + self->timerL.timeout = TSIP_TIMER_GET(L); + self->timerX.timeout = TSIP_TIMER_GET(G); + + return 0; +} + +tsip_transac_ist_t* tsip_transac_ist_create(int32_t cseq_value, const char* callid, tsip_transac_dst_t* dst) +{ + tsip_transac_ist_t* transac = tsk_object_new(tsip_transac_ist_def_t); + if(transac){ + // initialize base class + tsip_transac_init(TSIP_TRANSAC(transac), tsip_transac_type_ist, cseq_value, "INVITE", 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_ist_OnTerminated), (const void*)transac); + + // initialize IST object + tsip_transac_ist_init(transac); + } + return transac; +} + +int tsip_transac_ist_start(tsip_transac_ist_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_recv_INVITE, request))){ + // + } + } + return ret; +} + + + + + +//-------------------------------------------------------- +// == STATE MACHINE BEGIN == +//-------------------------------------------------------- + +/* Started --> (recv INVITE) --> Proceeding +*/ +int tsip_transac_ist_Started_2_Proceeding_X_INVITE(va_list *app) +{ + tsip_transac_ist_t *self = va_arg(*app, tsip_transac_ist_t *); + const tsip_request_t *request = va_arg(*app, const tsip_request_t *); + int ret = -1; + + 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->timerI.timeout = TSIP_TRANSAC(self)->reliable ? 0 : TSIP_TIMER_GET(I); + + /* RFC 3261 - 17.2.1 INVITE Server Transaction + When a server transaction is constructed for a request, it enters the + "Proceeding" state. The server transaction MUST generate a 100 + (Trying) response unless it knows that the TU will generate a + provisional or final response within 200 ms, in which case it MAY + generate a 100 (Trying) response. + + RFC 3262 - 3. UAS Behavior + A UAS MUST NOT attempt to send a 100 (Trying) response reliably. + */ + if(request){ + tsip_response_t* response; + if((response = tsip_response_new(100, "Trying (sent from the Transaction Layer)", request))){ + ret = tsip_transac_send(TSIP_TRANSAC(self), TSIP_TRANSAC(self)->branch, response); + TRANSAC_IST_SET_LAST_RESPONSE(self, response); // Update last response + TSK_OBJECT_SAFE_FREE(response); + } + } + if(!ret){ /* Send "100 Trying" is OK ==> alert dialog for the incoming INVITE */ + ret = tsip_transac_deliver(TSIP_TRANSAC(self), tsip_dialog_i_msg, request); + } + return ret; +} + +/* Proceeding --> (recv INVITE) --> Proceeding +*/ +int tsip_transac_ist_Proceeding_2_Proceeding_X_INVITE(va_list *app) +{ + tsip_transac_ist_t *self = va_arg(*app, tsip_transac_ist_t *); + //const tsip_request_t *request = va_arg(*app, const tsip_request_t *); + int ret = -1; + + /* RFC 3261 - 17.2.1 INVITE Server Transaction + If a request retransmission is received while in the "Proceeding" state, the most + recent provisional response that was received from the TU MUST be + passed to the transport layer for retransmission. + */ + if(self->lastResponse){ + ret = tsip_transac_send(TSIP_TRANSAC(self), TSIP_TRANSAC(self)->branch, self->lastResponse); + } + + return ret; +} + +/* Proceeding --> (send 1xx) --> Proceeding +*/ +int tsip_transac_ist_Proceeding_2_Proceeding_X_1xx(va_list *app) +{ + tsip_transac_ist_t *self = va_arg(*app, tsip_transac_ist_t *); + const tsip_response_t *response = va_arg(*app, const tsip_response_t *); + int ret; + + /* Send to the transport layer */ + ret = tsip_transac_send(TSIP_TRANSAC(self), TSIP_TRANSAC(self)->branch, TSIP_MESSAGE(response)); + + /* Update last response */ + TRANSAC_IST_SET_LAST_RESPONSE(self, response); + + return ret; +} + +/* Proceeding --> (send 300-699) --> Completed +*/ +int tsip_transac_ist_Proceeding_2_Completed_X_300_to_699(va_list *app) +{ + tsip_transac_ist_t *self = va_arg(*app, tsip_transac_ist_t *); + const tsip_response_t *response = va_arg(*app, const tsip_response_t *); + int ret; + + /* RFC 3264 17.2.1 INVITE Server Transaction + While in the "Proceeding" state, if the TU passes a response with + status code from 300 to 699 to the server transaction, the response + MUST be passed to the transport layer for transmission, and the state + machine MUST enter the "Completed" state. For unreliable transports, timer G is set to fire in T1 seconds, + and is not set to fire for reliable transports. + */ + if(!TSIP_TRANSAC(self)->reliable){ + TRANSAC_IST_TIMER_SCHEDULE(G); + } + + /* Send to the transport layer */ + ret = tsip_transac_send(TSIP_TRANSAC(self), TSIP_TRANSAC(self)->branch, TSIP_MESSAGE(response)); + + /* Update last response */ + TRANSAC_IST_SET_LAST_RESPONSE(self, response); + + /* RFC 3261 - 17.2.1 INVITE Server Transaction + When the "Completed" state is entered, timer H MUST be set to fire in + 64*T1 seconds for all transports. + */ + TRANSAC_IST_TIMER_SCHEDULE(H); + + return ret; +} + +/* Proceeding --> (send 2xx) --> Accepted +*/ +int tsip_transac_ist_Proceeding_2_Accepted_X_2xx(va_list *app) +{ + tsip_transac_ist_t *self = va_arg(*app, tsip_transac_ist_t *); + const tsip_response_t *response = va_arg(*app, const tsip_response_t *); + int ret = -1; + + /* draft-sparks-sip-invfix-03 - 8.5. Pages 134 to 135 + If, while in the "Proceeding" state, the TU passes a 2xx response + to the server transaction, the server transaction MUST pass this + response to the transport layer for transmission. It is not + retransmitted by the server transaction; retransmissions of 2xx + responses are handled by the TU. The server transaction MUST then + transition to the "Accepted" state. + */ + ret = tsip_transac_send(TSIP_TRANSAC(self), TSIP_TRANSAC(self)->branch, TSIP_MESSAGE(response)); + + /* Update last response */ + TRANSAC_IST_SET_LAST_RESPONSE(self, response); + + /* RFC 3261 - 13.3.1.4 The INVITE is Accepted + Since 2xx is retransmitted end-to-end, there may be hops between + UAS and UAC that are UDP. To ensure reliable delivery across + these hops, the response is retransmitted periodically even if the + transport at the UAS is reliable. + */ + TRANSAC_IST_TIMER_SCHEDULE(X); + self->timerX.timeout <<= 1; + + /* draft-sparks-sip-invfix-03 - 8.7. Page 137 + When the INVITE server transaction enters the "Accepted" state, + Timer L MUST be set to fire in 64*T1 for all transports. This + value matches both Timer B in the next upstream client state + machine (the amount of time the previous hop will wait for a + response when no provisionals have been sent) and the amount of + time this (or any downstream) UAS core might be retransmitting the + 2xx while waiting for an ACK. + */ + TRANSAC_IST_TIMER_SCHEDULE(L); + + return ret; +} + +/* Completed --> (recv INVITE) --> Completed +*/ +int tsip_transac_ist_Completed_2_Completed_INVITE(va_list *app) +{ + tsip_transac_ist_t *self = va_arg(*app, tsip_transac_ist_t *); + //const tsip_message_t *message = va_arg(*app, const tsip_message_t *); + int ret = -1; + + /* RFC 3261 - 17.2.1 INVITE Server Transaction + Furthermore, while in the "Completed" state, if a request retransmission is + received, the server SHOULD pass the response to the transport for + retransmission. + */ + if(self->lastResponse){ + ret = tsip_transac_send(TSIP_TRANSAC(self), TSIP_TRANSAC(self)->branch, self->lastResponse); + } + + return ret; +} + +/* Completed --> (timerG) --> Completed +*/ +int tsip_transac_ist_Completed_2_Completed_timerG(va_list *app) +{ + tsip_transac_ist_t *self = va_arg(*app, tsip_transac_ist_t *); + //const tsip_message_t *message = va_arg(*app, const tsip_message_t *); + int ret = -1; + + /* RFC 3261 - 17.2.1 INVITE Server Transaction + If timer G fires, the response is passed to the transport layer once + more for retransmission, and timer G is set to fire in MIN(2*T1, T2) seconds. + From then on, when timer G fires, the response is passed to the transport again for + transmission, and timer G is reset with a value that doubles, unless + that value exceeds T2, in which case it is reset with the value of T2. + */ + if(self->lastResponse){ + ret = tsip_transac_send(TSIP_TRANSAC(self), TSIP_TRANSAC(self)->branch, self->lastResponse); + } + self->timerG.timeout = TSK_MIN(self->timerG.timeout*2, TSIP_TIMER_GET(T2)); + TRANSAC_IST_TIMER_SCHEDULE(G); + + return ret; +} + +/* Completed --> (timerH) --> Terminated +*/ +int tsip_transac_ist_Completed_2_Terminated_timerH(va_list *app) +{ + tsip_transac_ist_t *self = va_arg(*app, tsip_transac_ist_t *); + //const tsip_message_t *message = va_arg(*app, const tsip_message_t *); + + /* RFC 3261 - 17.2.1 INVITE Server Transaction + If timer H fires while in the "Completed" state, it implies that the + ACK was never received. In this case, the server transaction MUST + transition to the "Terminated" state, and MUST indicate to the TU + that a transaction failure has occurred. + */ + return tsip_transac_deliver(TSIP_TRANSAC(self), tsip_dialog_transport_error, tsk_null); +} + +/* Completed --> (recv ACK) --> Confirmed +*/ +int tsip_transac_ist_Completed_2_Confirmed_ACK(va_list *app) +{ + tsip_transac_ist_t *self = va_arg(*app, tsip_transac_ist_t *); + //const tsip_message_t *message = va_arg(*app, const tsip_message_t *); + + /* RFC 3261 - 17.2.1 INVITE Server Transaction + If an ACK is received while the server transaction is in the + "Completed" state, the server transaction MUST transition to the + "Confirmed" state. As Timer G is ignored in this state, any + retransmissions of the response will cease + */ + TRANSAC_TIMER_CANCEL(G);/* To avoid warnings from FSM manager. */ + + /* RFC 3261 - 17.2.1 INVITE Server Transaction + The purpose of the "Confirmed" state is to absorb any additional ACK + messages that arrive, triggered from retransmissions of the final + response. When this state is entered, timer I is set to fire in T4 + seconds for unreliable transports, and zero seconds for reliable + transports. + */ + TRANSAC_IST_TIMER_SCHEDULE(I); /* Has the right value (zero of reliable and ...) */ + + + return 0; +} + +/* Accepted --> (recv INVITE) --> Accepted +*/ +int tsip_transac_ist_Accepted_2_Accepted_INVITE(va_list *app) +{ + tsip_transac_ist_t *self = va_arg(*app, tsip_transac_ist_t *); + + /* draft-sparks-sip-invfix-03 - 8.7. Page 137 + The purpose of the "Accepted" state is to absorb retransmissions + of an accepted INVITE request. Any such retransmissions are + absorbed entirely within the server transaction. They are not + passed up to the TU since any downstream UAS cores that accepted + the request have taken responsibility for reliability and will + already retransmit their 2xx responses if neccessary. + */ + + /* Do not pass to the TU (see above) + VERY IMPORTANT: INVITE dialog is responsible for reliability of the 2xx response. + */ + if(self->lastResponse){ + return tsip_transac_send(TSIP_TRANSAC(self), TSIP_TRANSAC(self)->branch, self->lastResponse); + } + return 0; +} + +/* Accepted --> (send 2xx) --> Accepted +*/ +int tsip_transac_ist_Accepted_2_Accepted_2xx(va_list *app) +{ + tsip_transac_ist_t *self = va_arg(*app, tsip_transac_ist_t *); + const tsip_response_t *response = va_arg(*app, const tsip_response_t *); + int ret; + /* draft-sparks-sip-invfix-03 - 8.7. Page 137 + While in the "Accepted" state, if the TU passes a 2xx response, + the server transaction MUST pass the response to the transport + layer for transmission. + */ + ret = tsip_transac_send(TSIP_TRANSAC(self), TSIP_TRANSAC(self)->branch, TSIP_MESSAGE(response)); + + /* Update last response */ + TRANSAC_IST_SET_LAST_RESPONSE(self, response); + + return ret; +} + +/* Accepted --> (timer X) --> Accepted +* Doubango specific +*/ +static int tsip_transac_ist_Accepted_2_Accepted_timerX(va_list *app) +{ + tsip_transac_ist_t *self = va_arg(*app, tsip_transac_ist_t *); + if(self->lastResponse){ + int ret; + ret = tsip_transac_send(TSIP_TRANSAC(self), TSIP_TRANSAC(self)->branch, self->lastResponse); + self->timerX.timeout <<= 1; + TRANSAC_IST_TIMER_SCHEDULE(X); + } + return 0; +} + +/* Accepted --> (Recv ACK) --> Accepted +* Doubango specific +*/ +int tsip_transac_ist_Accepted_2_Accepted_iACK(va_list *app) +{ + tsip_transac_ist_t *self = va_arg(*app, tsip_transac_ist_t *); + const tsip_request_t *request = va_arg(*app, const tsip_request_t *); + self->acked = tsk_true; + TRANSAC_TIMER_CANCEL(X); + return tsip_transac_deliver(TSIP_TRANSAC(self), tsip_dialog_i_msg, request); +} + +/* Accepted --> (timerL) --> Terminated +*/ +static int tsip_transac_ist_Accepted_2_Terminated_timerL(va_list *app) +{ + tsip_transac_ist_t *self = va_arg(*app, tsip_transac_ist_t *); + //const tsip_message_t *message = va_arg(*app, const tsip_message_t *); + + /* draft-sparks-sip-invfix-03 - 8.7. Page 137 + If Timer L fires while the INVITE server transaction is in the "Accepted" state, the transaction + MUST transition to the "Terminated" state. Once the transaction is in the "Terminated" state, it MUST be + destroyed immediately. + */ + if(!self->acked){ + TSK_DEBUG_ERROR("ACK not received"); + return tsip_transac_deliver(TSIP_TRANSAC(self), tsip_dialog_transport_error, tsk_null); + } + return 0; +} + +/* Confirmed --> (timerI) --> Terminated +*/ +static int tsip_transac_ist_Confirmed_2_Terminated_timerI(va_list *app) +{ + /* RFC 3261 - 17.2.1 INVITE Server Transaction + Once timer I fires, the server MUST transition to the + "Terminated" state. + + Once the transaction is in the "Terminated" state, it MUST be + destroyed immediately. As with client transactions, this is needed + to ensure reliability of the 2xx responses to INVITE. + */ + return 0; +} + +/* Any -> (Transport Error) -> Terminated +*/ +static int tsip_transac_ist_Any_2_Terminated_X_transportError(va_list *app) +{ + tsip_transac_ist_t *self = va_arg(*app, tsip_transac_ist_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 +*/ +static int tsip_transac_ist_Any_2_Terminated_X_Error(va_list *app) +{ + tsip_transac_ist_t *self = va_arg(*app, tsip_transac_ist_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 +*/ +static int tsip_transac_ist_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. +*/ +static int tsip_transac_ist_OnTerminated(tsip_transac_ist_t *self) +{ + TSK_DEBUG_INFO("=== IST terminated ==="); + + /* Remove (and destroy) the transaction from the layer. */ + return tsip_transac_remove(TSIP_TRANSAC(self)); +} + + + + + + + + + + + + + +//======================================================== +// IST object definition +// +static tsk_object_t* tsip_transac_ist_ctor(tsk_object_t * self, va_list * app) +{ + tsip_transac_ist_t *transac = self; + if(transac){ + } + return self; +} + +static tsk_object_t* tsip_transac_ist_dtor(tsk_object_t * _self) +{ + tsip_transac_ist_t *self = _self; + if(self) + { + /* Cancel timers */ + TRANSAC_TIMER_CANCEL(H); + TRANSAC_TIMER_CANCEL(I); + if(!TSIP_TRANSAC(self)->reliable){ + TRANSAC_TIMER_CANCEL(G); + } + TRANSAC_TIMER_CANCEL(L); + TRANSAC_TIMER_CANCEL(X); + + TSIP_TRANSAC(self)->running = tsk_false; + TSK_OBJECT_SAFE_FREE(self->lastResponse); + + /* DeInitialize base class */ + tsip_transac_deinit(TSIP_TRANSAC(self)); + + TSK_DEBUG_INFO("*** IST destroyed ***"); + } + return _self; +} + +static int tsip_transac_ist_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_ist_def_s = +{ + sizeof(tsip_transac_ist_t), + tsip_transac_ist_ctor, + tsip_transac_ist_dtor, + tsip_transac_ist_cmp, +}; +const tsk_object_def_t *tsip_transac_ist_def_t = &tsip_transac_ist_def_s; diff --git a/tinySIP/src/transactions/tsip_transac_layer.c b/tinySIP/src/transactions/tsip_transac_layer.c new file mode 100644 index 0000000..b3cf167 --- /dev/null +++ b/tinySIP/src/transactions/tsip_transac_layer.c @@ -0,0 +1,352 @@ +/* +* Copyright (C) 2010-2011 Mamadou Diop. +* +* Contact: Mamadou Diop <diopmamadou(at)doubango[dot]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. +* +*/ + +/**@file tsip_transac_layer.c + * @brief SIP transaction layer. + * + * @author Mamadou Diop <diopmamadou(at)doubango[dot]org> + * + + */ +#include "tinysip/transactions/tsip_transac_layer.h" + +#include "tinysip/transactions/tsip_transac_ict.h" +#include "tinysip/transactions/tsip_transac_ist.h" +#include "tinysip/transactions/tsip_transac_nict.h" +#include "tinysip/transactions/tsip_transac_nist.h" + +#include "tsk_string.h" +#include "tsk_debug.h" + +tsip_transac_layer_t* tsip_transac_layer_create(tsip_stack_t* stack) +{ + return tsk_object_new(tsip_transac_layer_def_t, stack); +} + +tsip_transac_t* tsip_transac_layer_new(const tsip_transac_layer_t *self, tsk_bool_t isCT, const tsip_message_t* msg, tsip_transac_dst_t* dst) +{ + tsip_transac_t *ret = tsk_null; + tsip_transac_t *transac = tsk_null; + + tsk_safeobj_lock(self); + + if(self && msg) + { + if(TSIP_MESSAGE_IS_REQUEST(msg)) + { + if(isCT) /* Client transaction */ + { + if(TSIP_REQUEST_IS_INVITE(msg)){ + // INVITE Client transaction (ICT) + transac = (tsip_transac_t *)tsip_transac_ict_create(msg->CSeq->seq, msg->Call_ID->value, dst); + } + else{ + // NON-INVITE Client transaction (NICT) + transac = (tsip_transac_t *)tsip_transac_nict_create(msg->CSeq->seq, msg->CSeq->method, msg->Call_ID->value, dst); + } + } + else /* Server transaction */ + { + if(TSIP_REQUEST_IS_INVITE(msg)){ + // INVITE Server transaction (IST) + transac = (tsip_transac_t *)tsip_transac_ist_create(msg->CSeq->seq, msg->Call_ID->value, dst); + } + else{ + // NON-INVITE Server transaction (NIST) + transac = (tsip_transac_t *)tsip_transac_nist_create(msg->CSeq->seq, msg->CSeq->method, msg->Call_ID->value, dst); + } + + if(transac){ /* Copy branch from the message */ + transac->branch = tsk_strdup(msg->firstVia->branch); + } + } + + /* Add new transaction */ + if(transac){ + ret = tsk_object_ref(transac); + tsk_list_push_back_data(self->transactions, (void**)&transac); + } + } + } + + tsk_safeobj_unlock(self); + + return ret; +} + +int tsip_transac_layer_remove(tsip_transac_layer_t *self, const tsip_transac_t *transac) +{ + if(transac && self){ + tsk_safeobj_lock(self); + tsk_list_remove_item_by_data(self->transactions, transac); + tsk_safeobj_unlock(self); + + return 0; + } + + return -1; +} + +/* cancel all transactions related to this dialog */ +int tsip_transac_layer_cancel_by_dialog(tsip_transac_layer_t *self, const struct tsip_dialog_s* dialog) +{ + tsk_list_item_t *item; + int ret = 0; /* Perhaps there is zero transaction */ + + + if(!self || !dialog){ + TSK_DEBUG_WARN("Invalid parameter."); + return -1; + } + + tsk_safeobj_lock(self); +again: + tsk_list_foreach(item, self->transactions){ + if(tsk_object_cmp(dialog, TSIP_TRANSAC_GET_DIALOG(item->data)) == 0){ + if((ret = tsip_transac_fsm_act(TSIP_TRANSAC(item->data), tsip_atype_cancel, tsk_null))){ /* will call tsip_transac_layer_remove() if succeed */ + /* break; */ + } + else{ + /* we cannot continue because an item has been removed from the list while we are looping through */ + goto again; + } + } + } + tsk_safeobj_unlock(self); + + return 0; +} + +tsip_transac_t* tsip_transac_layer_find_client(const tsip_transac_layer_t *self, const tsip_response_t* response) +{ + /* + RFC 3261 - 17.1.3 Matching Responses to Client Transactions + + When the transport layer in the client receives a response, it has to + determine which client transaction will handle the response, so that + the processing of Sections 17.1.1 and 17.1.2 can take place. The + branch parameter in the top Via header field is used for this + purpose. A response matches a client transaction under two + conditions: + + 1. If the response has the same value of the branch parameter in + the top Via header field as the branch parameter in the top + Via header field of the request that created the transaction. + + 2. If the method parameter in the CSeq header field matches the + method of the request that created the transaction. The + method is needed since a CANCEL request constitutes a + different transaction, but shares the same value of the branch + parameter. + */ + tsip_transac_t *ret = tsk_null; + tsip_transac_t *transac; + tsk_list_item_t *item; + + /* Check first Via/CSeq validity. + */ + if(!response->firstVia || !response->CSeq){ + return tsk_null; + } + + tsk_safeobj_lock(self); + + tsk_list_foreach(item, self->transactions){ + transac = item->data; + if( tsk_strequals(transac->branch, response->firstVia->branch) + && tsk_strequals(transac->cseq_method, response->CSeq->method) + ) + { + ret = tsk_object_ref(transac); + break; + } + } + + tsk_safeobj_unlock(self); + + return ret; +} + +tsip_transac_t* tsip_transac_layer_find_server(const tsip_transac_layer_t *self, const tsip_message_t* message) +{ + /* + RFC 3261 - 17.2.3 Matching Requests to Server Transactions + + When a request is received from the network by the server, it has to + be matched to an existing transaction. This is accomplished in the + following manner. + + The branch parameter in the topmost Via header field of the request + is examined. If it is present and begins with the magic cookie + "z9hG4bK", the request was generated by a client transaction + compliant to this specification. Therefore, the branch parameter + will be unique across all transactions sent by that client. The + request matches a transaction if: + + 1. the branch parameter in the request is equal to the one in the + top Via header field of the request that created the + transaction, and + + 2. the sent-by value in the top Via of the request is equal to the + one in the request that created the transaction, and + + 3. the method of the request matches the one that created the + transaction, except for ACK, where the method of the request + that created the transaction is INVITE. + */ + tsip_transac_t *ret = tsk_null; + tsip_transac_t *transac; + tsk_list_item_t *item; + //const char* sent_by; + + /* Check first Via/CSeq validity */ + if(!message->firstVia || !message->CSeq){ + return tsk_null; + } + + tsk_safeobj_lock(self); + + tsk_list_foreach(item, self->transactions){ + transac = item->data; + if(TSIP_REQUEST_IS_ACK(message) && tsk_strequals(transac->callid, message->Call_ID->value)){ /* 1. ACK branch won't match INVITE's but they MUST have the same CSeq/CallId values */ + // [transac->type == tsip_transac_type_ist] is used to avoid looping in webrtc2sip mode (e.g. browser <->(breaker)<->browser) + // (browser-1) -> INVITE -> (breaker) -> INVITE - (server) -> INVITE -> (breaker) -> (browser-2) + // the breaker will have two transactions (IST and ICT) with same cseq value and call-id (if not changed by the server) + if(transac->type == tsip_transac_type_ist && tsk_striequals(transac->cseq_method, "INVITE") && message->CSeq->seq == transac->cseq_value){ + ret = tsk_object_ref(transac); + break; + } + } + else if(tsk_strequals(transac->branch, message->firstVia->branch) /* 2. Compare branches*/ + && (1 == 1) /* FIXME: compare host:ip */ + ){ + if(tsk_strequals(transac->cseq_method, message->CSeq->method)){ + ret = tsk_object_ref(transac); + break; + } + else if(TSIP_REQUEST_IS_CANCEL(message) || TSIP_RESPONSE_IS_TO_CANCEL(message)){ + ret = tsk_object_ref(transac); + break; + } + } + } + + tsk_safeobj_unlock(self); + + return ret; +} + + + +/** + * @fn int tsip_transac_layer_handle_incoming_msg(const tsip_transac_layer_t *self, const tsip_message_t* message) + * + * @brief Handles SIP/IMS message incoming from the transport layer. + * + * @author Mamadou + * @date 1/8/2010 + * + * @param [in,out] self The transaction layer. + * @param [in,out] message The SIP/IMS message to handle. + * + * @return Zero if a matching transaction have been found and non-zero result code otherwise. +**/ +int tsip_transac_layer_handle_incoming_msg(const tsip_transac_layer_t *self, const tsip_message_t* message) +{ + int ret = -1; + tsip_transac_t *transac = tsk_null; + + if(!message){ + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + + //tsk_safeobj_lock(self); + + if(TSIP_MESSAGE_IS_REQUEST(message)){ + transac = tsip_transac_layer_find_server(self, /*TSIP_MESSAGE_AS_REQUEST*/(message)); + } + else{ + transac = tsip_transac_layer_find_client(self, /*TSIP_MESSAGE_AS_RESPONSE*/(message)); + } + + //tsk_safeobj_unlock(self); + + if(transac){ + ret = transac->callback(transac, tsip_transac_incoming_msg, message); + tsk_object_unref(transac); + } + + return ret; +} + + + + + + + + + + + +//======================================================== +// Transaction layer object definition +// +static tsk_object_t* tsip_transac_layer_ctor(tsk_object_t * self, va_list * app) +{ + tsip_transac_layer_t *layer = self; + if(layer){ + layer->stack = va_arg(*app, const tsip_stack_handle_t *); + layer->transactions = tsk_list_create(); + + tsk_safeobj_init(layer); + } + return self; +} + +static tsk_object_t* tsip_transac_layer_dtor(tsk_object_t * self) +{ + tsip_transac_layer_t *layer = self; + if(layer){ + TSK_OBJECT_SAFE_FREE(layer->transactions); + + tsk_safeobj_deinit(layer); + + TSK_DEBUG_INFO("*** Transaction Layer destroyed ***"); + } + return self; +} + +static int tsip_transac_layer_cmp(const tsk_object_t *obj1, const tsk_object_t *obj2) +{ + return -1; +} + +static const tsk_object_def_t tsip_transac_layer_def_s = +{ + sizeof(tsip_transac_layer_t), + tsip_transac_layer_ctor, + tsip_transac_layer_dtor, + tsip_transac_layer_cmp, +}; +const tsk_object_def_t *tsip_transac_layer_def_t = &tsip_transac_layer_def_s; diff --git a/tinySIP/src/transactions/tsip_transac_nict.c b/tinySIP/src/transactions/tsip_transac_nict.c new file mode 100644 index 0000000..c16e503 --- /dev/null +++ b/tinySIP/src/transactions/tsip_transac_nict.c @@ -0,0 +1,731 @@ +/* +* Copyright (C) 2010-2011 Mamadou Diop. +* +* Contact: Mamadou Diop <diopmamadou(at)doubango[dot]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 from TU + |send request + Timer E V + send request +-----------+ + +---------| |-------------------+ + | | Trying | Timer F | + +-------->| | or Transport Err.| + +-----------+ inform TU | + 200-699 | | | + resp. to TU | |1xx | + +---------------+ |resp. to TU | + | | | + | Timer E V Timer F | + | send req +-----------+ or Transport Err. | + | +---------| | inform TU | + | | |Proceeding |------------------>| + | +-------->| |-----+ | + | +-----------+ |1xx | + | | ^ |resp to TU | + | 200-699 | +--------+ | + | resp. to TU | | + | | | + | V | + | +-----------+ | + | | | | + | | Completed | | + | | | | + | +-----------+ | + | ^ | | + | | | Timer K | + +--------------+ | - | + | | + V | + NOTE: +-----------+ | + | | | + transitions | Terminated|<------------------+ + labeled with | | + the event +-----------+ + over the action + +=============================================================================*/ + +/**@file tsip_transac_nict.c + * @brief SIP Non-INVITE Client Transaction as per RFC 3261 subcaluse 17.1.2. + * + * @author Mamadou Diop <diopmamadou(at)doubango[dot]org> + * + */ +#include "tinysip/transactions/tsip_transac_nict.h" + +#include "tsk_debug.h" + +#define DEBUG_STATE_MACHINE 1 + +#define TRANSAC_NICT_TIMER_SCHEDULE(TX) TRANSAC_TIMER_SCHEDULE(nict, TX) + +/* ======================== internal functions ======================== */ +static int tsip_transac_nict_init(tsip_transac_nict_t *self); +static int tsip_transac_nict_OnTerminated(tsip_transac_nict_t *self); + +/* ======================== transitions ======================== */ +static int tsip_transac_nict_Started_2_Trying_X_send(va_list *app); +static int tsip_transac_nict_Trying_2_Trying_X_timerE(va_list *app); +static int tsip_transac_nict_Trying_2_Terminated_X_timerF(va_list *app); +static int tsip_transac_nict_Trying_2_Terminated_X_transportError(va_list *app); +static int tsip_transac_nict_Trying_2_Proceedding_X_1xx(va_list *app); +static int tsip_transac_nict_Trying_2_Completed_X_200_to_699(va_list *app); +static int tsip_transac_nict_Proceeding_2_Proceeding_X_timerE(va_list *app); +static int tsip_transac_nict_Proceeding_2_Terminated_X_timerF(va_list *app); +static int tsip_transac_nict_Proceeding_2_Terminated_X_transportError(va_list *app); +static int tsip_transac_nict_Proceeding_2_Proceeding_X_1xx(va_list *app); +static int tsip_transac_nict_Proceeding_2_Completed_X_200_to_699(va_list *app); +static int tsip_transac_nict_Completed_2_Terminated_X_timerK(va_list *app); +static int tsip_transac_nict_Any_2_Terminated_X_transportError(va_list *app); +static int tsip_transac_nict_Any_2_Terminated_X_Error(va_list *app); +static int tsip_transac_nict_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_send = 0xFF, + _fsm_action_timerE, + _fsm_action_timerF, + _fsm_action_timerK, + _fsm_action_1xx, + _fsm_action_200_to_699, + _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; + + +/** + * Callback function called by the transport layer to alert the transaction for incoming messages + * or errors (e.g. transport error). + * + * @param [in,out] self A pointer to the NIC transaction. + * @param type The event type. + * @param [in,out] msg The incoming message. + * + * @return Zero if succeed and no-zero error code otherwise. +**/ +int tsip_transac_nict_event_callback(const tsip_transac_nict_t *self, tsip_transac_event_type_t type, const tsip_message_t *msg) +{ + int ret = 0; + + switch(type) + { + case tsip_transac_incoming_msg: + { + if(msg && TSIP_MESSAGE_IS_RESPONSE(msg)){ + if(TSIP_RESPONSE_IS_1XX(msg)){ + ret = tsip_transac_fsm_act(TSIP_TRANSAC(self), _fsm_action_1xx, msg); + } + else if(TSIP_RESPONSE_IS_23456(msg)){ + ret = tsip_transac_fsm_act(TSIP_TRANSAC(self), _fsm_action_200_to_699, msg); + } + else{ + TSK_DEBUG_WARN("Not supported status code: %d", TSIP_RESPONSE_CODE(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; + } + + default: break; + } + + return ret; +} + +int tsip_transac_nict_timer_callback(const tsip_transac_nict_t* self, tsk_timer_id_t timer_id) +{ + int ret = -1; + + if(self){ + if(timer_id == self->timerE.id){ + ret = tsip_transac_fsm_act(TSIP_TRANSAC(self), _fsm_action_timerE, tsk_null); + } + else if(timer_id == self->timerF.id){ + ret = tsip_transac_fsm_act(TSIP_TRANSAC(self), _fsm_action_timerF, tsk_null); + } + else if(timer_id == self->timerK.id){ + ret = tsip_transac_fsm_act(TSIP_TRANSAC(self), _fsm_action_timerK, tsk_null); + } + } + + return ret; +} + +/** Initializes the transaction. + * + * @author Mamadou + * @date 12/24/2009 + * + * @param [in,out] self The transaction to initialize. +**/ +int tsip_transac_nict_init(tsip_transac_nict_t *self) +{ + /* Initialize the state machine. */ + tsk_fsm_set(TSIP_TRANSAC_GET_FSM(self), + + /*======================= + * === Started === + */ + // Started -> (Send) -> Trying + TSK_FSM_ADD_ALWAYS(_fsm_state_Started, _fsm_action_send, _fsm_state_Trying, tsip_transac_nict_Started_2_Trying_X_send, "tsip_transac_nict_Started_2_Trying_X_send"), + // Started -> (Any) -> Started + TSK_FSM_ADD_ALWAYS_NOTHING(_fsm_state_Started, "tsip_transac_nict_Started_2_Started_X_any"), + + /*======================= + * === Trying === + */ + // Trying -> (timerE) -> Trying + TSK_FSM_ADD_ALWAYS(_fsm_state_Trying, _fsm_action_timerE, _fsm_state_Trying, tsip_transac_nict_Trying_2_Trying_X_timerE, "tsip_transac_nict_Trying_2_Trying_X_timerE"), + // Trying -> (timerF) -> Terminated + TSK_FSM_ADD_ALWAYS(_fsm_state_Trying, _fsm_action_timerF, _fsm_state_Terminated, tsip_transac_nict_Trying_2_Terminated_X_timerF, "tsip_transac_nict_Trying_2_Terminated_X_timerF"), + // Trying -> (transport error) -> Terminated + TSK_FSM_ADD_ALWAYS(_fsm_state_Trying, _fsm_action_transporterror, _fsm_state_Terminated, tsip_transac_nict_Trying_2_Terminated_X_transportError, "tsip_transac_nict_Trying_2_Terminated_X_transportError"), + // Trying -> (1xx) -> Proceeding + TSK_FSM_ADD_ALWAYS(_fsm_state_Trying, _fsm_action_1xx, _fsm_state_Proceeding, tsip_transac_nict_Trying_2_Proceedding_X_1xx, "tsip_transac_nict_Trying_2_Proceedding_X_1xx"), + // Trying -> (200 to 699) -> Completed + TSK_FSM_ADD_ALWAYS(_fsm_state_Trying, _fsm_action_200_to_699, _fsm_state_Completed, tsip_transac_nict_Trying_2_Completed_X_200_to_699, "tsip_transac_nict_Trying_2_Completed_X_200_to_699"), + + /*======================= + * === Proceeding === + */ + // Proceeding -> (timerE) -> Proceeding + TSK_FSM_ADD_ALWAYS(_fsm_state_Proceeding, _fsm_action_timerE, _fsm_state_Proceeding, tsip_transac_nict_Proceeding_2_Proceeding_X_timerE, "tsip_transac_nict_Proceeding_2_Proceeding_X_timerE"), + // Proceeding -> (timerF) -> Terminated + TSK_FSM_ADD_ALWAYS(_fsm_state_Proceeding, _fsm_action_timerF, _fsm_state_Terminated, tsip_transac_nict_Proceeding_2_Terminated_X_timerF, "tsip_transac_nict_Proceeding_2_Terminated_X_timerF"), + // Proceeding -> (transport error) -> Terminated + TSK_FSM_ADD_ALWAYS(_fsm_state_Proceeding, _fsm_action_transporterror, _fsm_state_Terminated, tsip_transac_nict_Proceeding_2_Terminated_X_transportError, "tsip_transac_nict_Proceeding_2_Terminated_X_transportError"), + // Proceeding -> (1xx) -> Proceeding + TSK_FSM_ADD_ALWAYS(_fsm_state_Proceeding, _fsm_action_1xx, _fsm_state_Proceeding, tsip_transac_nict_Proceeding_2_Proceeding_X_1xx, "tsip_transac_nict_Proceeding_2_Proceeding_X_1xx"), + // Proceeding -> (200 to 699) -> Completed + TSK_FSM_ADD_ALWAYS(_fsm_state_Proceeding, _fsm_action_200_to_699, _fsm_state_Completed, tsip_transac_nict_Proceeding_2_Completed_X_200_to_699, "tsip_transac_nict_Proceeding_2_Completed_X_200_to_699"), + + /*======================= + * === Completed === + */ + // Completed -> (timer K) -> Terminated + TSK_FSM_ADD_ALWAYS(_fsm_state_Completed, _fsm_action_timerK, _fsm_state_Terminated, tsip_transac_nict_Completed_2_Terminated_X_timerK, "tsip_transac_nict_Completed_2_Terminated_X_timerK"), + + /*======================= + * === Any === + */ + // Any -> (transport error) -> Terminated + TSK_FSM_ADD_ALWAYS(tsk_fsm_state_any, _fsm_action_transporterror, _fsm_state_Terminated, tsip_transac_nict_Any_2_Terminated_X_transportError, "tsip_transac_nict_Any_2_Terminated_X_transportError"), + // Any -> (error) -> Terminated + TSK_FSM_ADD_ALWAYS(tsk_fsm_state_any, _fsm_action_error, _fsm_state_Terminated, tsip_transac_nict_Any_2_Terminated_X_Error, "tsip_transac_nict_Any_2_Terminated_X_Error"), + // Any -> (cancel) -> Terminated + TSK_FSM_ADD_ALWAYS(tsk_fsm_state_any, _fsm_action_cancel, _fsm_state_Terminated, tsip_transac_nict_Any_2_Terminated_X_cancel, "tsip_transac_nict_Any_2_Terminated_X_cancel"), + + TSK_FSM_ADD_NULL()); + + /* Set callback function to call when new messages arrive or errors happen in + the transport layer. + */ + TSIP_TRANSAC(self)->callback = TSIP_TRANSAC_EVENT_CALLBACK_F(tsip_transac_nict_event_callback); + + /* Timers */ + self->timerE.id = TSK_INVALID_TIMER_ID; + self->timerF.id = TSK_INVALID_TIMER_ID; + self->timerK.id = TSK_INVALID_TIMER_ID; + self->timerE.timeout = TSIP_TIMER_GET(E); + self->timerF.timeout = TSIP_TIMER_GET(F); + + return 0; +} + +tsip_transac_nict_t* tsip_transac_nict_create(int32_t cseq_value, const char* cseq_method, const char* callid, tsip_transac_dst_t* dst) +{ + tsip_transac_nict_t* transac = tsk_object_new(tsip_transac_nict_def_t); + if(transac){ + // initialize base class + tsip_transac_init(TSIP_TRANSAC(transac), tsip_transac_type_nict, 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_nict_OnTerminated), (const void*)transac); + + // initialize NICT object + tsip_transac_nict_init(transac); + } + return transac; +} + +/** + * Starts the client transaction. + * + * @param [in,out] self The client transaction to start. + * @param [in,out] request The SIP/IMS request to send. + * + * @return Zero if succeed and non-zero error code otherwise. +**/ +int tsip_transac_nict_start(tsip_transac_nict_t *self, const tsip_request_t* request) +{ + int ret = -1; + if(self && request && !TSIP_TRANSAC(self)->running){ + /* Add branch to the new client transaction + * - CANCEL will have the same Via and Contact headers as the request it cancel + * - Transac will use request branch if exit (e.g. when request received over websocket) + */ + if((request->firstVia && !tsk_strnullORempty(request->firstVia->branch))){ + tsk_strupdate(&TSIP_TRANSAC(self)->branch, (request->firstVia ? request->firstVia->branch : "doubango")); + } + else if((TSIP_TRANSAC(self)->branch = tsk_strdup(TSIP_TRANSAC_MAGIC_COOKIE))){ + tsk_istr_t branch; + tsk_strrandom(&branch); + tsk_strcat_2(&(TSIP_TRANSAC(self)->branch), "-%s", branch); + } + + TSIP_TRANSAC(self)->running = tsk_true; + self->request = tsk_object_ref((void*)request); + + ret = tsip_transac_fsm_act(TSIP_TRANSAC(self), _fsm_action_send, tsk_null); + } + return ret; +} + + + + + + + + + + +//-------------------------------------------------------- +// == STATE MACHINE BEGIN == +//-------------------------------------------------------- +/* Started -> (send) -> Trying +*/ +int tsip_transac_nict_Started_2_Trying_X_send(va_list *app) +{ + tsip_transac_nict_t *self = va_arg(*app, tsip_transac_nict_t *); + //const tsip_message_t *message = va_arg(*app, const tsip_message_t *); + + //== Send the request + tsip_transac_send(TSIP_TRANSAC(self), TSIP_TRANSAC(self)->branch, TSIP_MESSAGE(self->request)); + + // Now that the first request is sent using the best transport mean we know if it's reliable or not + if(TNET_SOCKET_TYPE_IS_VALID(self->request->dst_net_type)){ + TSIP_TRANSAC(self)->reliable = TNET_SOCKET_TYPE_IS_STREAM(self->request->dst_net_type); + self->timerK.timeout = TSIP_TRANSAC(self)->reliable ? 0 : TSIP_TIMER_GET(K); /* RFC 3261 - 17.1.2.2*/ + } + + /* RFC 3261 - 17.1.2.2 + The "Trying" state is entered when the TU initiates a new client + transaction with a request. When entering this state, the client + transaction SHOULD set timer F to fire in 64*T1 seconds. + */ + TRANSAC_NICT_TIMER_SCHEDULE(F); + + /* RFC 3261 - 17.1.2.2 + If an unreliable transport is in use, the client transaction MUST set timer + E to fire in T1 seconds. + */ + if(!TSIP_TRANSAC(self)->reliable){ + TRANSAC_NICT_TIMER_SCHEDULE(E); + } + + return 0; +} + +/* Trying -> (Timer E) -> Trying +*/ +int tsip_transac_nict_Trying_2_Trying_X_timerE(va_list *app) +{ + tsip_transac_nict_t *self = va_arg(*app, tsip_transac_nict_t *); + /*const tsip_message_t *message = va_arg(*app, const tsip_message_t *);*/ + + //== Send the request + tsip_transac_send(TSIP_TRANSAC(self), TSIP_TRANSAC(self)->branch, self->request); + + /* RFC 3261 - 17.1.2.2 + If timer E fires while still in this (Trying) state, the timer is reset, but this time with a value of MIN(2*T1, T2). + When the timer fires again, it is reset to a MIN(4*T1, T2). This process continues so that retransmissions occur with an exponentially + increasing interval that caps at T2. The default value of T2 is 4s, and it represents the amount of time a non-INVITE server transaction + will take to respond to a request, if it does not respond immediately. For the default values of T1 and T2, this results in + intervals of 500 ms, 1 s, 2 s, 4 s, 4 s, 4 s, etc. + */ + self->timerE.timeout = TSK_MIN((self->timerE.timeout<<1), TSIP_TIMER_GET(T2)); + TRANSAC_NICT_TIMER_SCHEDULE(E); + + return 0; +} + +/* Trying -> (Timer F) -> Terminated +*/ +int tsip_transac_nict_Trying_2_Terminated_X_timerF(va_list *app) +{ + tsip_transac_nict_t *self = va_arg(*app, tsip_transac_nict_t *); + /*const tsip_message_t *message = va_arg(*app, const tsip_message_t *);*/ + + /* RFC 3261 - 17.1.2.2 + If Timer F fires while the client transaction is still in the + "Trying" state, the client transaction SHOULD inform the TU about the + timeout, and then it SHOULD enter the "Terminated" state. + */ + + /* Timers will be canceled by "tsip_transac_nict_OnTerminated" */ + + tsip_transac_deliver(TSIP_TRANSAC(self), tsip_dialog_timedout, self->request); + + return 0; +} + +/* Trying -> (Transport Error) -> Terminated +*/ +int tsip_transac_nict_Trying_2_Terminated_X_transportError(va_list *app) +{ + tsip_transac_nict_t *self = va_arg(*app, tsip_transac_nict_t *); + /*const tsip_message_t *message = va_arg(*app, const tsip_message_t *);*/ + + /* Timers will be canceled by "tsip_transac_nict_OnTerminated" */ + + tsip_transac_deliver(TSIP_TRANSAC(self), tsip_dialog_transport_error, self->request); + + return 0; +} + +/* Trying -> (1xx) -> Proceeding +*/ +int tsip_transac_nict_Trying_2_Proceedding_X_1xx(va_list *app) +{ + tsip_transac_nict_t *self = va_arg(*app, tsip_transac_nict_t *); + const tsip_message_t *message = va_arg(*app, const tsip_message_t *); + + /* RFC 3261 - 17.1.2.2 + If a provisional response is received while in the "Trying" state, the + response MUST be passed to the TU, and then the client transaction + SHOULD move to the "Proceeding" state. + */ + + /* Cancel timers */ + if(!TSIP_TRANSAC(self)->reliable){ + TRANSAC_TIMER_CANCEL(E); + } + TRANSAC_TIMER_CANCEL(F); /* Now it's up to the UAS to update the FSM. */ + + /* Pass the provisional response to the dialog. */ + tsip_transac_deliver(TSIP_TRANSAC(self), tsip_dialog_i_msg, message); + + return 0; +} + +/* Trying -> (200-699) -> Completed +*/ +int tsip_transac_nict_Trying_2_Completed_X_200_to_699(va_list *app) +{ + tsip_transac_nict_t *self = va_arg(*app, tsip_transac_nict_t *); + const tsip_message_t *message = va_arg(*app, const tsip_message_t *); + + /* RFC 3261 - 17.1.2.2 + If a final response (status codes 200-699) is received while in the "Trying" state, the response + MUST be passed to the TU, and the client transaction MUST transition + to the "Completed" state. + + If Timer K fires while in this state (Completed), the client transaction MUST transition to the "Terminated" state. + */ + + if(!TSIP_TRANSAC(self)->reliable){ + TRANSAC_TIMER_CANCEL(E); + } + TRANSAC_TIMER_CANCEL(F); + + tsip_transac_deliver(TSIP_TRANSAC(self), tsip_dialog_i_msg, message); + + /* SCHEDULE timer K */ + TRANSAC_NICT_TIMER_SCHEDULE(K); + + return 0; +} + +/* Proceeding -> (TimerE) -> Proceeding +*/ +int tsip_transac_nict_Proceeding_2_Proceeding_X_timerE(va_list *app) +{ + tsip_transac_nict_t *self = va_arg(*app, tsip_transac_nict_t *); + //const tsip_message_t *message = va_arg(*app, const tsip_message_t *); + + //== Send the request + tsip_transac_send(TSIP_TRANSAC(self), TSIP_TRANSAC(self)->branch, self->request); + + /* RFC 3261 - 17.1.2.2 + If Timer E fires while in the "Proceeding" state, the request MUST be + passed to the transport layer for retransmission, and Timer E MUST be + reset with a value of T2 seconds. + */ + self->timerE.timeout = TSK_MIN(self->timerE.timeout*2, TSIP_TIMER_GET(T2)); + TRANSAC_NICT_TIMER_SCHEDULE(E); + + return 0; +} + +/* Proceeding -> (Timer F) -> Proceeding +*/ +int tsip_transac_nict_Proceeding_2_Terminated_X_timerF(va_list *app) +{ + tsip_transac_nict_t *self = va_arg(*app, tsip_transac_nict_t *); + //const tsip_message_t *message = va_arg(*app, const tsip_message_t *); + + /* RFC 3261 - 17.1.2.2 + If timer F fires while in the "Proceeding" state, the TU MUST be informed of a timeout, and the + client transaction MUST transition to the terminated state. + */ + + /* Timers will be canceled by "tsip_transac_nict_OnTerminated" */ + + tsip_transac_deliver(TSIP_TRANSAC(self), tsip_dialog_transport_error, tsk_null); + + return 0; +} + +/* Proceeding -> (Transport error) -> Terminated +*/ +int tsip_transac_nict_Proceeding_2_Terminated_X_transportError(va_list *app) +{ + tsip_transac_nict_t *self = va_arg(*app, tsip_transac_nict_t *); + /*const tsip_message_t *message = va_arg(*app, const tsip_message_t *);*/ + + /* Timers will be canceles by On */ + tsip_transac_deliver(TSIP_TRANSAC(self), tsip_dialog_transport_error, 0); + + return 0; +} + +/* Proceeding -> (1xx) -> Proceeding +*/ +int tsip_transac_nict_Proceeding_2_Proceeding_X_1xx(va_list *app) +{ + tsip_transac_nict_t *self = va_arg(*app, tsip_transac_nict_t *); + const tsip_message_t *message = va_arg(*app, const tsip_message_t *); + + if(!TSIP_TRANSAC(self)->reliable){ + TRANSAC_TIMER_CANCEL(E); + } + tsip_transac_deliver(TSIP_TRANSAC(self), tsip_dialog_i_msg, message); + + return 0; +} + +/* Proceeding -> (200-699) -> Completed +*/ +int tsip_transac_nict_Proceeding_2_Completed_X_200_to_699(va_list *app) +{ + tsip_transac_nict_t *self = va_arg(*app, tsip_transac_nict_t *); + const tsip_message_t *message = va_arg(*app, const tsip_message_t *); + + /* RFC 3261 - 17.1.2.2 + If a final response (status codes 200-699) is received while in the + "Proceeding" state, the response MUST be passed to the TU, and the + client transaction MUST transition to the "Completed" state. + */ + + /* RFC 3261 - 17.1.2.2 + Once the client transaction enters the "Completed" state, it MUST set + Timer K to fire in T4 seconds for unreliable transports, and zero + seconds for reliable transports. The "Completed" state exists to + buffer any additional response retransmissions that may be received + (which is why the client transaction remains there only for + + unreliable transports). T4 represents the amount of time the network + will take to clear messages between client and server transactions. + The default value of T4 is 5s. + */ + + if(!TSIP_TRANSAC(self)->reliable){ + TRANSAC_TIMER_CANCEL(E); + } + + tsip_transac_deliver(TSIP_TRANSAC(self), tsip_dialog_i_msg, message); + + /* SCHEDULE timer K */ + TRANSAC_NICT_TIMER_SCHEDULE(K); + + return 0; +} + +/* Completed -> (Timer K) -> Terminated +*/ +int tsip_transac_nict_Completed_2_Terminated_X_timerK(va_list *app) +{ + //tsip_transac_nict_t *self = va_arg(*app, tsip_transac_nict_t *); + //const tsip_message_t *message = va_arg(*app, const tsip_message_t *); + + /* RFC 3261 - 17.1.2.2 + If Timer K fires while in this state (Completed), the client transaction + MUST transition to the "Terminated" state. + */ + + /* RFC 3261 - 17.1.2.2 + ONCE THE TRANSACTION IS IN THE TERMINATED STATE, IT MUST BE DESTROYED IMMEDIATELY. + */ + + /* Timers will be canceled by "tsip_transac_nict_OnTerminated" */ + + //tsip_transac_deliver(TSIP_TRANSAC(self), tsip_dialog_transac_ok, 0); + + return 0; +} + +/* Any -> (Transport Error) -> Terminated +*/ +int tsip_transac_nict_Any_2_Terminated_X_transportError(va_list *app) +{ + tsip_transac_nict_t *self = va_arg(*app, tsip_transac_nict_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_nict_Any_2_Terminated_X_Error(va_list *app) +{ + tsip_transac_nict_t *self = va_arg(*app, tsip_transac_nict_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_nict_Any_2_Terminated_X_cancel(va_list *app) +{ + /* doubango-specific */ + return 0; +} + + +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +// == STATE MACHINE END == +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + + + + +/*== TERMINATED +*/ +int tsip_transac_nict_OnTerminated(tsip_transac_nict_t *self) +{ + TSK_DEBUG_INFO("=== NICT terminated ==="); + + /* Remove (and destroy) the transaction from the layer. */ + return tsip_transac_remove(TSIP_TRANSAC(self)); +} + + + + + + + + + + + + + + + + +//======================================================== +// NICT object definition +// +static tsk_object_t* tsip_transac_nict_ctor(tsk_object_t * self, va_list * app) +{ + tsip_transac_nict_t *transac = self; + if(transac){ + } + return self; +} + +static tsk_object_t* tsip_transac_nict_dtor(tsk_object_t * _self) +{ + tsip_transac_nict_t *self = _self; + if(self){ + /* Cancel timers */ + if(!TSIP_TRANSAC(self)->reliable){ + TRANSAC_TIMER_CANCEL(E); + } + TRANSAC_TIMER_CANCEL(F); + TRANSAC_TIMER_CANCEL(K); + + TSIP_TRANSAC(self)->running = tsk_false; + TSK_OBJECT_SAFE_FREE(self->request); + + /* DeInitialize base class */ + tsip_transac_deinit(TSIP_TRANSAC(self)); + + TSK_DEBUG_INFO("*** NICT destroyed ***"); + } + return _self; +} + +static int tsip_transac_nict_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_nict_def_s = +{ + sizeof(tsip_transac_nict_t), + tsip_transac_nict_ctor, + tsip_transac_nict_dtor, + tsip_transac_nict_cmp, +}; +const tsk_object_def_t *tsip_transac_nict_def_t = &tsip_transac_nict_def_s; diff --git a/tinySIP/src/transactions/tsip_transac_nist.c b/tinySIP/src/transactions/tsip_transac_nist.c new file mode 100644 index 0000000..66424b2 --- /dev/null +++ b/tinySIP/src/transactions/tsip_transac_nist.c @@ -0,0 +1,583 @@ +/* +* Copyright (C) 2010-2011 Mamadou Diop. +* +* Contact: Mamadou Diop <diopmamadou(at)doubango[dot]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; |