summaryrefslogtreecommitdiffstats
path: root/tinySIP/src/transactions/tsip_transac_ict.c
diff options
context:
space:
mode:
Diffstat (limited to 'tinySIP/src/transactions/tsip_transac_ict.c')
-rw-r--r--tinySIP/src/transactions/tsip_transac_ict.c927
1 files changed, 927 insertions, 0 deletions
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;
OpenPOWER on IntegriCloud