diff options
Diffstat (limited to 'tinySIP/src/transactions/tsip_transac_layer.c')
-rw-r--r-- | tinySIP/src/transactions/tsip_transac_layer.c | 344 |
1 files changed, 344 insertions, 0 deletions
diff --git a/tinySIP/src/transactions/tsip_transac_layer.c b/tinySIP/src/transactions/tsip_transac_layer.c new file mode 100644 index 0000000..d6fefa9 --- /dev/null +++ b/tinySIP/src/transactions/tsip_transac_layer.c @@ -0,0 +1,344 @@ +/* +* Copyright (C) 2009-2010 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. +* +*/ + +/**@file tsip_transac_layer.c + * @brief SIP transaction layer. + * + * @author Mamadou Diop <diopmamadou(at)doubango.org> + * + * @date Created: Sat Nov 8 16:54:58 2009 mdiop + */ +#include "tinysip/transactions/tsip_transac_layer.h" + +#include "tinysip/transactions/tsip_transac_ict.h" +#include "tinysip/transactions/tsip_transac_ist.h" +#include "tinysip/transactions/tsip_transac_nict.h" +#include "tinysip/transactions/tsip_transac_nist.h" + +#include "tsk_string.h" +#include "tsk_debug.h" + +tsip_transac_layer_t* tsip_transac_layer_create(tsip_stack_t* stack) +{ + return tsk_object_new(tsip_transac_layer_def_t, stack); +} + +tsip_transac_t* tsip_transac_layer_new(const tsip_transac_layer_t *self, tsk_bool_t isCT, const tsip_message_t* msg, tsip_dialog_t* dialog) +{ + tsip_transac_t *ret = tsk_null; + tsip_transac_t *transac = tsk_null; + + tsk_safeobj_lock(self); + + if(self && msg) + { + if(TSIP_MESSAGE_IS_REQUEST(msg)) + { + if(isCT) /* Client transaction */ + { + if(TSIP_REQUEST_IS_INVITE(msg)){ + // INVITE Client transaction (ICT) + transac = (tsip_transac_t *)tsip_transac_ict_create(self->reliable, msg->CSeq->seq, msg->Call_ID->value, dialog); + } + else{ + // NON-INVITE Client transaction (NICT) + transac = (tsip_transac_t *)tsip_transac_nict_create(self->reliable, msg->CSeq->seq, msg->CSeq->method, msg->Call_ID->value, dialog); + } + } + else /* Server transaction */ + { + if(TSIP_REQUEST_IS_INVITE(msg)){ + // INVITE Server transaction (IST) + transac = (tsip_transac_t *)tsip_transac_ist_create(self->reliable, msg->CSeq->seq, msg->Call_ID->value, dialog); + } + else{ + // NON-INVITE Server transaction (NIST) + transac = (tsip_transac_t *)tsip_transac_nist_create(self->reliable, msg->CSeq->seq, msg->CSeq->method, msg->Call_ID->value, dialog); + } + + if(transac){ /* Copy branch from the message */ + transac->branch = tsk_strdup(msg->firstVia->branch); + } + } + + /* Add new transaction */ + if(transac){ + ret = tsk_object_ref(transac); + tsk_list_push_back_data(self->transactions, (void**)&transac); + } + } + } + + tsk_safeobj_unlock(self); + + return ret; +} + +int tsip_transac_layer_remove(tsip_transac_layer_t *self, const tsip_transac_t *transac) +{ + if(transac && self){ + tsk_safeobj_lock(self); + tsk_list_remove_item_by_data(self->transactions, transac); + tsk_safeobj_unlock(self); + + return 0; + } + + return -1; +} + +/* cancel all transactions related to this dialog */ +int tsip_transac_layer_cancel_by_dialog(tsip_transac_layer_t *self, const struct tsip_dialog_s* dialog) +{ + tsk_list_item_t *item; + int ret = 0; /* Perhaps there is zero transaction */ + + + if(!self || !dialog){ + TSK_DEBUG_WARN("Invalid parameter."); + return -1; + } + + tsk_safeobj_lock(self); +again: + tsk_list_foreach(item, self->transactions){ + if(tsk_object_cmp(dialog, TSIP_TRANSAC(item->data)->dialog) == 0){ + if((ret = tsip_transac_fsm_act(TSIP_TRANSAC(item->data), tsip_atype_cancel, tsk_null))){ /* will call tsip_transac_layer_remove() if succeed */ + /* break; */ + } + else{ + /* we cannot continue because an item has been removed from the list while we are looping through */ + goto again; + } + } + } + tsk_safeobj_unlock(self); + + return 0; +} + +tsip_transac_t* tsip_transac_layer_find_client(const tsip_transac_layer_t *self, const tsip_response_t* response) +{ + /* + RFC 3261 - 17.1.3 Matching Responses to Client Transactions + + When the transport layer in the client receives a response, it has to + determine which client transaction will handle the response, so that + the processing of Sections 17.1.1 and 17.1.2 can take place. The + branch parameter in the top Via header field is used for this + purpose. A response matches a client transaction under two + conditions: + + 1. If the response has the same value of the branch parameter in + the top Via header field as the branch parameter in the top + Via header field of the request that created the transaction. + + 2. If the method parameter in the CSeq header field matches the + method of the request that created the transaction. The + method is needed since a CANCEL request constitutes a + different transaction, but shares the same value of the branch + parameter. + */ + tsip_transac_t *ret = tsk_null; + tsip_transac_t *transac; + tsk_list_item_t *item; + + /* Check first Via/CSeq validity. + */ + if(!response->firstVia || !response->CSeq){ + return tsk_null; + } + + tsk_safeobj_lock(self); + + tsk_list_foreach(item, self->transactions){ + transac = item->data; + if( tsk_strequals(transac->branch, response->firstVia->branch) + && tsk_strequals(transac->cseq_method, response->CSeq->method) + ) + { + ret = tsk_object_ref(transac); + break; + } + } + + tsk_safeobj_unlock(self); + + return ret; +} + +tsip_transac_t* tsip_transac_layer_find_server(const tsip_transac_layer_t *self, const tsip_message_t* message) +{ + /* + RFC 3261 - 17.2.3 Matching Requests to Server Transactions + + When a request is received from the network by the server, it has to + be matched to an existing transaction. This is accomplished in the + following manner. + + The branch parameter in the topmost Via header field of the request + is examined. If it is present and begins with the magic cookie + "z9hG4bK", the request was generated by a client transaction + compliant to this specification. Therefore, the branch parameter + will be unique across all transactions sent by that client. The + request matches a transaction if: + + 1. the branch parameter in the request is equal to the one in the + top Via header field of the request that created the + transaction, and + + 2. the sent-by value in the top Via of the request is equal to the + one in the request that created the transaction, and + + 3. the method of the request matches the one that created the + transaction, except for ACK, where the method of the request + that created the transaction is INVITE. + */ + tsip_transac_t *ret = tsk_null; + tsip_transac_t *transac; + tsk_list_item_t *item; + //const char* sent_by; + + /* Check first Via/CSeq validity */ + if(!message->firstVia || !message->CSeq){ + return tsk_null; + } + + tsk_safeobj_lock(self); + + tsk_list_foreach(item, self->transactions){ + transac = item->data; + if(TSIP_REQUEST_IS_ACK(message) && tsk_strequals(transac->callid, message->Call_ID->value)){ /* 1. ACK branch won't match INVITE's but they MUST have the same CSeq/CallId values */ + if(tsk_striequals(transac->cseq_method, "INVITE") && message->CSeq->seq == transac->cseq_value){ + ret = tsk_object_ref(transac); + break; + } + } + else if(tsk_strequals(transac->branch, message->firstVia->branch) /* 2. Compare branches*/ + && (1 == 1) /* FIXME: compare host:ip */ + ){ + if(tsk_strequals(transac->cseq_method, message->CSeq->method)){ + ret = tsk_object_ref(transac); + break; + } + else if(TSIP_REQUEST_IS_CANCEL(message) || TSIP_RESPONSE_IS_TO_CANCEL(message)){ + ret = tsk_object_ref(transac); + break; + } + } + } + + tsk_safeobj_unlock(self); + + return ret; +} + + + +/** + * @fn int tsip_transac_layer_handle_incoming_msg(const tsip_transac_layer_t *self, const tsip_message_t* message) + * + * @brief Handles SIP/IMS message incoming from the transport layer. + * + * @author Mamadou + * @date 1/8/2010 + * + * @param [in,out] self The transaction layer. + * @param [in,out] message The SIP/IMS message to handle. + * + * @return Zero if a matching transaction have been found and non-zero result code otherwise. +**/ +int tsip_transac_layer_handle_incoming_msg(const tsip_transac_layer_t *self, const tsip_message_t* message) +{ + int ret = -1; + tsip_transac_t *transac = tsk_null; + + //tsk_safeobj_lock(self); + + if(TSIP_MESSAGE_IS_REQUEST(message)){ + transac = tsip_transac_layer_find_server(self, /*TSIP_MESSAGE_AS_REQUEST*/(message)); + } + else{ + transac = tsip_transac_layer_find_client(self, /*TSIP_MESSAGE_AS_RESPONSE*/(message)); + } + + //tsk_safeobj_unlock(self); + + if(transac){ + ret = transac->callback(transac, tsip_transac_incoming_msg, message); + tsk_object_unref(transac); + } + + return ret; +} + + + + + + + + + + + +//======================================================== +// Transaction layer object definition +// +static tsk_object_t* tsip_transac_layer_ctor(tsk_object_t * self, va_list * app) +{ + tsip_transac_layer_t *layer = self; + if(layer){ + layer->stack = va_arg(*app, const tsip_stack_handle_t *); + layer->transactions = tsk_list_create(); + + tsk_safeobj_init(layer); + } + return self; +} + +static tsk_object_t* tsip_transac_layer_dtor(tsk_object_t * self) +{ + tsip_transac_layer_t *layer = self; + if(layer){ + TSK_OBJECT_SAFE_FREE(layer->transactions); + + tsk_safeobj_deinit(layer); + + TSK_DEBUG_INFO("*** Transaction Layer destroyed ***"); + } + return self; +} + +static int tsip_transac_layer_cmp(const tsk_object_t *obj1, const tsk_object_t *obj2) +{ + return -1; +} + +static const tsk_object_def_t tsip_transac_layer_def_s = +{ + sizeof(tsip_transac_layer_t), + tsip_transac_layer_ctor, + tsip_transac_layer_dtor, + tsip_transac_layer_cmp, +}; +const tsk_object_def_t *tsip_transac_layer_def_t = &tsip_transac_layer_def_s; |