#if HAVE_CRT #define _CRTDBG_MAP_ALLOC #include #include #endif //HAVE_CRT /* * Copyright (C) 2020, University of the Basque Country (UPV/EHU) * Contact for licensing options: * * The original file was part of Open Source Doubango Framework * Copyright (C) 2010-2011 Mamadou Diop. * Copyright (C) 2012 Doubango Telecom * * 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 * */ #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 : "organization")); } 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;