doubango/trunk/tinySIP/src/transports/tsip_transport_layer.c

598 lines
17 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.
*
*/
/**@file tsip_transport_layer.c
* @brief SIP transport layer.
*
* @author Mamadou Diop <diopmamadou(at)doubango.org>
*
* @date Created: Sat Nov 8 16:54:58 2009 mdiop
*/
#include "tinysip/transports/tsip_transport_layer.h"
#include "tinysip/transports/tsip_transport_ipsec.h"
#include "tinysip/transactions/tsip_transac_layer.h"
#include "tinysip/dialogs/tsip_dialog_layer.h"
#include "tinysip/parsers/tsip_parser_message.h"
#include "tsk_thread.h"
#include "tsk_debug.h"
/* max size of a chunck to form a valid SIP message */
#define TSIP_MAX_STREAM_CHUNCK_SIZE 0xFFFF
/* min size of a chunck to form a valid SIP message
* (Request/Response)-Line, Via, From, To, Call-Id, CSeq and Content-Length
* Tests have been done with both compact and full headers */
#define TSIP_MIN_STREAM_CHUNCK_SIZE 0xA0
tsip_transport_layer_t* tsip_transport_layer_create(tsip_stack_t *stack)
{
return tsk_object_new(tsip_transport_layer_def_t, stack);
}
int tsip_transport_layer_handle_incoming_msg(const tsip_transport_t *transport, tsip_message_t *message)
{
int ret = -1;
if(message){
const tsip_transac_layer_t *layer_transac = transport->stack->layer_transac;
const tsip_dialog_layer_t *layer_dialog = transport->stack->layer_dialog;
if((ret=tsip_transac_layer_handle_incoming_msg(layer_transac, message))){
/* NO MATCHING TRANSACTION FOUND ==> LOOK INTO DIALOG LAYER */
ret = tsip_dialog_layer_handle_incoming_msg(layer_dialog, message);
}
}
return ret;
}
/*== Non-blocking callback function (STREAM: TCP, TLS and SCTP)
*/
static int tsip_transport_layer_stream_cb(const tnet_transport_event_t* e)
{
int ret = -1;
tsk_ragel_state_t state;
tsip_message_t *message = tsk_null;
int endOfheaders = -1;
const tsip_transport_t *transport = e->callback_data;
switch(e->type){
case event_data: {
//TSK_DEBUG_INFO("\n\n\nSIP Message:%s\n\n\n", e->data);
break;
}
case event_closed:
case event_connected:
default:{
return 0;
}
}
/* RFC 3261 - 7.5 Framing SIP Messages
Unlike HTTP, SIP implementations can use UDP or other unreliable
datagram protocols. Each such datagram carries one request or
response. See Section 18 on constraints on usage of unreliable
transports.
Implementations processing SIP messages over stream-oriented
transports MUST ignore any CRLF appearing before the start-line
[H4.1].
The Content-Length header field value is used to locate the end of
each SIP message in a stream. It will always be present when SIP
messages are sent over stream-oriented transports.
*/
/* Check if buffer is too big to be valid (have we missed some chuncks?) */
if(TSK_BUFFER_SIZE(transport->buff_stream) >= TSIP_MAX_STREAM_CHUNCK_SIZE){
TSK_DEBUG_ERROR("TCP Buffer is too big to be valid");
tsk_buffer_cleanup(transport->buff_stream);
}
/* Append new content. */
tsk_buffer_append(transport->buff_stream, e->data, e->size);
/* Check if we have all SIP headers. */
parse_buffer:
if((endOfheaders = tsk_strindexOf(TSK_BUFFER_DATA(transport->buff_stream),TSK_BUFFER_SIZE(transport->buff_stream), "\r\n\r\n"/*2CRLF*/)) < 0){
TSK_DEBUG_INFO("No all SIP headers in the TCP buffer.");
goto bail;
}
/* If we are there this mean that we have all SIP headers.
* ==> Parse the SIP message without the content.
*/
tsk_ragel_state_init(&state, TSK_BUFFER_DATA(transport->buff_stream), endOfheaders + 4/*2CRLF*/);
if(tsip_message_parse(&state, &message, tsk_false/* do not extract the content */) == tsk_true
&& message->firstVia && message->Call_ID && message->CSeq && message->From && message->To)
{
tsk_size_t clen = TSIP_MESSAGE_CONTENT_LENGTH(message); /* MUST have content-length header (see RFC 3261 - 7.5). If no CL header then the macro return zero. */
if(clen == 0){ /* No content */
tsk_buffer_remove(transport->buff_stream, 0, (endOfheaders + 4/*2CRLF*/)); /* Remove SIP headers and CRLF */
}
else{ /* There is a content */
if((endOfheaders + 4/*2CRLF*/ + clen) > TSK_BUFFER_SIZE(transport->buff_stream)){ /* There is content but not all the content. */
TSK_DEBUG_INFO("No all SIP content in the TCP buffer.");
goto bail;
}
else{
/* Add the content to the message. */
tsip_message_add_content(message, tsk_null, TSK_BUFFER_TO_U8(transport->buff_stream) + endOfheaders + 4/*2CRLF*/, clen);
/* Remove SIP headers, CRLF and the content. */
tsk_buffer_remove(transport->buff_stream, 0, (endOfheaders + 4/*2CRLF*/ + clen));
}
}
}
if(message){
/* Set fd */
message->sockfd = e->fd;
/* Alert transaction/dialog layer */
ret = tsip_transport_layer_handle_incoming_msg(transport, message);
/* Parse next chunck */
if(TSK_BUFFER_SIZE(transport->buff_stream) >= TSIP_MIN_STREAM_CHUNCK_SIZE){
/* message already passed to the dialog/transac layers */
TSK_OBJECT_SAFE_FREE(message);
goto parse_buffer;
}
}
else{
TSK_DEBUG_ERROR("Failed to parse SIP message");
ret = -15;
}
bail:
TSK_OBJECT_SAFE_FREE(message);
return ret;
}
/*== Non-blocking callback function (DGRAM: UDP)
*/
static int tsip_transport_layer_dgram_cb(const tnet_transport_event_t* e)
{
int ret = -1;
tsk_ragel_state_t state;
tsip_message_t *message = tsk_null;
const tsip_transport_t *transport = e->callback_data;
switch(e->type){
case event_data: {
break;
}
case event_closed:
case event_connected:
default:{
return 0;
}
}
tsk_ragel_state_init(&state, e->data, e->size);
if(tsip_message_parse(&state, &message, tsk_true) == tsk_true
&& message->firstVia && message->Call_ID && message->CSeq && message->From && message->To)
{
/* Set fd */
message->sockfd = e->fd;
/* Alert transaction/dialog layer */
ret = tsip_transport_layer_handle_incoming_msg(transport, message);
}
TSK_OBJECT_SAFE_FREE(message);
return ret;
}
tsip_transport_t* tsip_transport_layer_find(const tsip_transport_layer_t* self, const tsip_message_t *msg, const char* destIP, int32_t *destPort)
{
tsip_transport_t* transport = 0;
destIP = self->stack->network.proxy_cscf;
*destPort = self->stack->network.proxy_cscf_port;
if(!self){
return 0;
}
/* =========== Sending Request =========
*
*/
if(TSIP_MESSAGE_IS_REQUEST(msg)){
/* Request are always sent to the Proxy-CSCF
*/
tsk_list_item_t *item;
tsip_transport_t *curr;
tsk_list_foreach(item, self->transports){
curr = item->data;
if(curr->type == self->stack->network.proxy_cscf_type){
transport = curr;
break;
}
}
}
/* =========== Sending Response =========
*
*/
else if(msg->firstVia)
{
if(TSIP_HEADER_VIA_RELIABLE_TRANS(msg->firstVia)) /*== RELIABLE ===*/
{
/* RFC 3261 - 18.2.2 Sending Responses
If the "sent-protocol" is a reliable transport protocol such as
TCP or SCTP, or TLS over those, the response MUST be sent using
the existing connection to the source of the original request
that created the transaction, if that connection is still open.
This requires the server transport to maintain an association
between server transactions and transport connections. If that
connection is no longer open, the server SHOULD open a
connection to the IP address in the "received" parameter, if
present, using the port in the "sent-by" value, or the default
port for that transport, if no port is specified. If that
connection attempt fails, the server SHOULD use the procedures
in [4] for servers in order to determine the IP address and
port to open the connection and send the response to.
*/
}
else
{
if(msg->firstVia->maddr) /*== UNRELIABLE MULTICAST ===*/
{
/* RFC 3261 - 18.2.2 Sending Responses
Otherwise, if the Via header field value contains a "maddr" parameter, the
response MUST be forwarded to the address listed there, using
the port indicated in "sent-by", or port 5060 if none is present.
If the address is a multicast address, the response SHOULD be
sent using the TTL indicated in the "ttl" parameter, or with a
TTL of 1 if that parameter is not present.
*/
}
else /*=== UNRELIABLE UNICAST ===*/
{
if(msg->firstVia->received)
{
if(msg->firstVia->rport>0)
{
/* RFC 3581 - 4. Server Behavior
When a server attempts to send a response, it examines the topmost
Via header field value of that response. If the "sent-protocol"
component indicates an unreliable unicast transport protocol, such as
UDP, and there is no "maddr" parameter, but there is both a
"received" parameter and an "rport" parameter, the response MUST be
sent to the IP address listed in the "received" parameter, and the
port in the "rport" parameter. The response MUST be sent from the
same address and port that the corresponding request was received on.
This effectively adds a new processing step between bullets two and
three in Section 18.2.2 of SIP [1].
*/
destIP = msg->firstVia->received;
*destPort = msg->firstVia->rport;
}
else
{
/* RFC 3261 - 18.2.2 Sending Responses
Otherwise (for unreliable unicast transports), if the top Via
has a "received" parameter, the response MUST be sent to the
address in the "received" parameter, using the port indicated
in the "sent-by" value, or using port 5060 if none is specified
explicitly. If this fails, for example, elicits an ICMP "port
unreachable" response, the procedures of Section 5 of [4]
SHOULD be used to determine where to send the response.
*/
destIP = msg->firstVia->received;
*destPort = msg->firstVia->port ? msg->firstVia->port : 5060;
}
}
else if(!msg->firstVia->received)
{
/* RFC 3261 - 18.2.2 Sending Responses
Otherwise, if it is not receiver-tagged, the response MUST be
sent to the address indicated by the "sent-by" value, using the
procedures in Section 5 of [4].
*/
destIP = msg->firstVia->host;
if(msg->firstVia->port >0)
{
*destPort = msg->firstVia->port;
}
}
}
}
{ /* Find the transport. */
tsk_list_item_t *item;
tsip_transport_t *curr;
tsk_list_foreach(item, self->transports)
{
curr = item->data;
if(tsip_transport_have_socket(curr,msg->sockfd))
{
transport = curr;
break;
}
}
}
}
return transport;
}
int tsip_transport_layer_add(tsip_transport_layer_t* self, const char* local_host, tnet_port_t local_port, tnet_socket_type_t type, const char* description)
{
// FIXME: CHECK IF already exist
if(self && description)
{
tsip_transport_t *transport =
TNET_SOCKET_TYPE_IS_IPSEC(type) ?
(tsip_transport_t *)tsip_transport_ipsec_create((tsip_stack_t*)self->stack, local_host, local_port, type, description) /* IPSec is a special case. All other are ok. */
: tsip_transport_create((tsip_stack_t*)self->stack, local_host, local_port, type, description); /* UDP, SCTP, TCP, TLS */
if(transport && transport->net_transport && self->stack){
/* Set TLS certs */
if(TNET_SOCKET_TYPE_IS_TLS(type) || self->stack->security.enable_secagree_tls){
tsip_transport_set_tlscerts(transport, self->stack->security.tls.ca, self->stack->security.tls.pbk, self->stack->security.tls.pvk);
}
tsk_list_push_back_data(self->transports, (void**)&transport);
return 0;
}
else {
return -2;
}
}
return -1;
}
int tsip_transport_layer_send(const tsip_transport_layer_t* self, const char *branch, const tsip_message_t *msg)
{
if(msg && self && self->stack){
const char* destIP = 0;
int32_t destPort = 5060;
tsip_transport_t *transport = tsip_transport_layer_find(self, msg, destIP, &destPort);
if(transport){
if(tsip_transport_send(transport, branch, TSIP_MESSAGE(msg), destIP, destPort)){
return 0;
}
else{
return -3;
}
}
else{
return -2;
}
}
else{
TSK_DEBUG_ERROR("Invalid Parameter");
return -1;
}
}
int tsip_transport_createTempSAs(const tsip_transport_layer_t *self)
{
int ret = -1;
tsk_list_item_t *item;
tsip_transport_t* transport;
if(!self){
goto bail;
}
tsk_list_foreach(item, self->transports){
transport = item->data;
if(TNET_SOCKET_TYPE_IS_IPSEC(transport->type)){
ret = tsip_transport_ipsec_createTempSAs(TSIP_TRANSPORT_IPSEC(transport));
break;
}
}
bail:
return ret;
}
int tsip_transport_ensureTempSAs(const tsip_transport_layer_t *self, const tsip_response_t *r401_407, int64_t expires)
{
int ret = -1;
tsk_list_item_t *item;
tsip_transport_t* transport;
if(!self){
goto bail;
}
tsk_list_foreach(item, self->transports){
transport = item->data;
if(TNET_SOCKET_TYPE_IS_IPSEC(transport->type)){
ret = tsip_transport_ipsec_ensureTempSAs(TSIP_TRANSPORT_IPSEC(transport), r401_407, expires);
break;
}
}
bail:
return ret;
}
int tsip_transport_startSAs(const tsip_transport_layer_t* self, const void* ik, const void* ck)
{
int ret = -1;
tsk_list_item_t *item;
tsip_transport_t* transport;
if(!self){
goto bail;
}
tsk_list_foreach(item, self->transports){
transport = item->data;
if(TNET_SOCKET_TYPE_IS_IPSEC(transport->type)){
ret = tsip_transport_ipsec_startSAs(TSIP_TRANSPORT_IPSEC(transport), (const tipsec_key_t*)ik, (const tipsec_key_t*)ck);
break;
}
}
bail:
return ret;
}
int tsip_transport_cleanupSAs(const tsip_transport_layer_t *self)
{
int ret = -1;
tsk_list_item_t *item;
tsip_transport_t* transport;
if(!self){
goto bail;
}
tsk_list_foreach(item, self->transports){
transport = item->data;
if(TNET_SOCKET_TYPE_IS_IPSEC(transport->type)){
ret = tsip_transport_ipsec_cleanupSAs(TSIP_TRANSPORT_IPSEC(transport));
break;
}
}
bail:
return ret;
}
int tsip_transport_layer_start(tsip_transport_layer_t* self)
{
if(self){
if(!self->running){
int ret = 0;
tsk_list_item_t *item;
tsip_transport_t* transport;
/* start() */
tsk_list_foreach(item, self->transports){
transport = item->data;
if(ret = tsip_transport_start(transport)){
return ret;
}
}
/* connect() */
tsk_list_foreach(item, self->transports){
tnet_fd_t fd;
transport = item->data;
tsip_transport_set_callback(transport, TNET_SOCKET_TYPE_IS_DGRAM(transport->type) ? TNET_TRANSPORT_CB_F(tsip_transport_layer_dgram_cb) : TNET_TRANSPORT_CB_F(tsip_transport_layer_stream_cb), transport);
if((fd = tsip_transport_connectto_2(transport, self->stack->network.proxy_cscf, self->stack->network.proxy_cscf_port)) == TNET_INVALID_FD){
return -3;
}
if((ret = tnet_sockfd_waitUntilWritable(fd, TNET_CONNECT_TIMEOUT))){
TSK_DEBUG_ERROR("%d milliseconds elapsed and the socket is still not connected.", TNET_CONNECT_TIMEOUT);
tnet_transport_remove_socket(transport->net_transport, &fd);
return ret;
}
}
self->running = tsk_true;
return 0;
}
else return -2;
}
return -1;
}
int tsip_transport_layer_shutdown(tsip_transport_layer_t* self)
{
if(self){
if(self->running){
/*int ret = 0;*/
tsk_list_item_t *item;
while((item = tsk_list_pop_first_item(self->transports))){
TSK_OBJECT_SAFE_FREE(item); // Network transports are not reusable ==> (shutdow+remove)
}
self->running = tsk_false;
return 0;
}
else{
return 0; /* not running */
}
}
else{
TSK_DEBUG_ERROR("Invalid parameter");
return -1;
}
}
//========================================================
// SIP transport layer object definition
//
static tsk_object_t* tsip_transport_layer_ctor(tsk_object_t * self, va_list * app)
{
tsip_transport_layer_t *layer = self;
if(layer){
layer->stack = va_arg(*app, const tsip_stack_t *);
layer->transports = tsk_list_create();
}
return self;
}
static tsk_object_t* tsip_transport_layer_dtor(tsk_object_t * self)
{
tsip_transport_layer_t *layer = self;
if(layer){
tsip_transport_layer_shutdown(self);
TSK_OBJECT_SAFE_FREE(layer->transports);
TSK_DEBUG_INFO("*** Transport Layer destroyed ***");
}
return self;
}
static int tsip_transport_layer_cmp(const tsk_object_t *obj1, const tsk_object_t *obj2)
{
return -1;
}
static const tsk_object_def_t tsip_transport_layer_def_s =
{
sizeof(tsip_transport_layer_t),
tsip_transport_layer_ctor,
tsip_transport_layer_dtor,
tsip_transport_layer_cmp,
};
const tsk_object_def_t *tsip_transport_layer_def_t = &tsip_transport_layer_def_s;