895 lines
33 KiB
C
895 lines
33 KiB
C
/*
|
|
* Copyright (C) 2009 Mamadou Diop.
|
|
*
|
|
* Contact: Mamadou Diop <diopmamadou(at)doubango.org>
|
|
*
|
|
* This file is part of Open Source Doubango Framework.
|
|
*
|
|
* DOUBANGO is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* DOUBANGO is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with DOUBANGO.
|
|
*
|
|
*/
|
|
|
|
/*=============================================================================
|
|
* 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.org>
|
|
*
|
|
* @date Created: Sat Nov 8 16:54:58 2009 mdiop
|
|
*/
|
|
#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 = 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)
|
|
{
|
|
int ret = 0;
|
|
|
|
/* 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 && 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_2XX(msg)){
|
|
ret = tsip_transac_fsm_act(TSIP_TRANSAC(self), _fsm_action_2xx, msg);
|
|
}
|
|
else if(TSIP_RESPONSE_IS_3456(msg)){
|
|
ret = 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));
|
|
}
|
|
}
|
|
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_ict_timer_callback(const tsip_transac_ict_t* self, tsk_timer_id_t timer_id)
|
|
{
|
|
int ret = -1;
|
|
|
|
if(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->timerD.timeout = TSIP_TRANSAC(self)->reliable ? 0 : TSIP_TIMER_GET(D);
|
|
self->timerM.timeout = TSIP_TIMER_GET(M);
|
|
|
|
return 0;
|
|
}
|
|
|
|
tsip_transac_ict_t* tsip_transac_ict_create(tsk_bool_t reliable, int32_t cseq_value, const char* callid, tsip_dialog_t* dialog)
|
|
{
|
|
return tsk_object_new(tsip_transac_ict_def_t, reliable, cseq_value, callid, dialog);
|
|
}
|
|
|
|
/**
|
|
* 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. */
|
|
if((TSIP_TRANSAC(self)->branch = tsk_strdup(TSIP_TRANSAC_MAGIC_COOKIE))){
|
|
tsk_istr_t branch;
|
|
tsk_strrandom(&branch);
|
|
tsk_strcat(&(TSIP_TRANSAC(self)->branch), 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));
|
|
|
|
/* 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(self)->dialog->callback(TSIP_TRANSAC(self)->dialog, 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(self)->dialog->callback(TSIP_TRANSAC(self)->dialog, 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(self)->dialog->callback(TSIP_TRANSAC(self)->dialog, 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);
|
|
|
|
/* pass the response to the TU (dialog) */
|
|
return TSIP_TRANSAC(self)->dialog->callback(TSIP_TRANSAC(self)->dialog, tsip_dialog_i_msg, response);
|
|
}
|
|
|
|
/* Proceeding -> (1xx) -> Proceeding
|
|
*/
|
|
int tsip_transac_ict_Proceeding_2_Proceeding_X_1xx(va_list *app)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
/* 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. */
|
|
return TSIP_TRANSAC(self)->dialog->callback(TSIP_TRANSAC(self)->dialog, 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);
|
|
|
|
/* pass the response to the TU (dialog) */
|
|
return TSIP_TRANSAC(self)->dialog->callback(TSIP_TRANSAC(self)->dialog, 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(self)->dialog->callback(TSIP_TRANSAC(self)->dialog, 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(self)->dialog->callback(TSIP_TRANSAC(self)->dialog, 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(self)->dialog->callback(TSIP_TRANSAC(self)->dialog, 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);
|
|
}
|
|
}
|
|
// sockfd:
|
|
//request->sockfd = response->sockfd;
|
|
|
|
// 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){
|
|
tsk_bool_t reliable = va_arg(*app, tsk_bool_t);
|
|
int32_t cseq_value = va_arg(*app, int32_t);
|
|
const char *cseq_method = "INVITE";
|
|
const char *callid = va_arg(*app, const char *);
|
|
tsip_dialog_t* dialog = va_arg(*app, tsip_dialog_t*);
|
|
|
|
/* Initialize base class */
|
|
tsip_transac_init(TSIP_TRANSAC(transac), tsip_ict, reliable, cseq_value, cseq_method, callid, dialog, _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 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));
|
|
}
|
|
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;
|