doubango/trunk/tinyMSRP/src/session/tmsrp_media.c

471 lines
11 KiB
C

/*
* Copyright (C) 2009 Mamadou Diop.
*
* Contact: Mamadou Diop <diopmamadou@yahoo.fr>
*
* 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 tmsrp_media.c
* @brief MSRP Media definition.
*
* @author Mamadou Diop <diopmamadou(at)yahoo.fr>
*
* @date Created: Sat Nov 8 16:54:58 2009 mdiop
*/
#include "tinyMSRP/session/tmsrp_media.h"
#include "tinySDP/headers/tsdp_header_A.h"
#include "tinySDP/headers/tsdp_header_C.h"
#include "tinySDP/headers/tsdp_header_M.h"
#include "tnet_utils.h"
#include "tsk_string.h"
#include "tsk_memory.h"
#include "tsk_debug.h"
#define TMSRP_CONNECT_TIMEOUT 2500
tmsrp_session_setup_t setup_from_string(const char* setup)
{
tmsrp_session_setup_t ret = setup_active;
if(setup){
if(tsk_strequals(setup, "holdconn")){
ret = setup_holdconn;
}
else if(tsk_strequals(setup, "passive")){
ret = setup_passive;
}
else if(tsk_strequals(setup, "actpass")){
ret = setup_actpass;
}
else{
ret = setup_active;
}
}
return ret;
}
const char* setup_to_string(tmsrp_session_setup_t setup)
{
switch(setup)
{
case setup_active:
return "active";
case setup_passive:
return "passive";
case setup_actpass:
return "actpass";
case setup_holdconn:
return "holdconn";
}
return "active";
}
/* =========================== Plugin ============================= */
int tmsrp_media_set_params(tmedia_t* self, const tsk_params_L_t* params)
{
const tsk_param_t* param;
tmsrp_media_t *msrp = TMSRP_MEDIA(self);
TSK_DEBUG_INFO("tmsrp_media_set_params");
// setup
if((param = tsk_params_get_param_by_name(params, "msrp/setup"))){
msrp->setup = setup_from_string(param->value);
}
return 0;
}
int tmsrp_media_start(tmedia_t* self)
{
int ret = -1;
tmsrp_media_t *msrp = TMSRP_MEDIA(self);
struct sockaddr_storage to;
if(!msrp || !msrp->local.socket || msrp->local.socket->fd <= 0){
goto bail;
}
if(!msrp->local.M || !msrp->remote.M){
ret = -2;
goto bail;
}
if(!msrp->remote.M->C){
ret = -3;
goto bail;
}
switch(msrp->setup){
case setup_active:
case setup_actpass:
{
//
// ACTIVE
//
if((ret = tnet_sockaddr_init(msrp->remote.M->C->addr, msrp->remote.M->port, msrp->local.socket->type, &to))){
goto bail;
}
if((ret = tnet_sockfd_connetto(msrp->local.socket->fd, &to))){
goto bail;
}
else{
msrp->connectedFD = msrp->local.socket->fd;
if((ret = tnet_sockfd_waitUntilWritable(msrp->connectedFD, TMSRP_CONNECT_TIMEOUT))){
TSK_DEBUG_ERROR("%d milliseconds elapsed and the socket is still not connected.", TMSRP_CONNECT_TIMEOUT);
goto bail;
}
/* draft-denis-simple-msrp-comedia-02 - 4.2.3. Setting up the connection
Once the TCP session is established, and if the answerer was the
active connection endpoint, it MUST send an MSRP request. In
particular, if it has no pending data to send, it MUST send an empty
MSRP SEND request. That is necessary for the other endpoint to
authenticate this TCP session.
*/
// ... send bodiless message
}
break;
}
default:
{
//
// PASSIVE
//
break;
}
}
bail:
return ret;
}
int tmsrp_media_pause(tmedia_t* self)
{
tmsrp_media_t *msrp = TMSRP_MEDIA(self);
TSK_DEBUG_INFO("tmsrp_media_pause");
return 0;
}
int tmsrp_media_stop(tmedia_t* self)
{
tmsrp_media_t *msrp = TMSRP_MEDIA(self);
TSK_DEBUG_INFO("tmsrp_media_stop");
return 0;
}
const tsdp_header_M_t* tmsrp_media_get_local_offer(tmedia_t* self)
{
tmsrp_media_t *msrp = TMSRP_MEDIA(self);
const tsdp_header_A_t* A;
const char* proto = "TCP/MSRP";
const char* sheme = "msrp";
tsk_bool_t answer;
tsk_bool_t ipv6;
tsk_istr_t sessionid;
if(!msrp || !msrp->local.socket || msrp->local.socket->fd <= 0){
goto bail;
}
// answer or initial offer?
answer = (msrp->remote.M != tsk_null);
// using ipv6?
ipv6 = TNET_SOCKET_TYPE_IS_IPV6(msrp->local.socket->type);
if(TNET_SOCKET_TYPE_IS_TLS(msrp->local.socket->type)){
proto = "TCP/TLS/MSRP";
sheme = "msrps";
}
if(!msrp->local.M){
char* path = tsk_null;
tsk_strrandom(&sessionid);
tsk_sprintf(&path, "%s://%s:%u/%s;tcp", sheme, msrp->local.socket->ip, msrp->local.socket->port, sessionid); //tcp is ok even if tls is used.
if((msrp->local.M = TSDP_HEADER_M_CREATE(self->plugin->media, msrp->local.socket->port, proto))){
tsdp_header_M_add_headers(msrp->local.M,
TSDP_FMT_VA_ARGS("*"),
TSDP_HEADER_C_VA_ARGS("IN", ipv6?"IP6":"IP4", &msrp->local.socket->ip),
TSDP_HEADER_A_VA_ARGS("sendrecv", tsk_null),
TSDP_HEADER_A_VA_ARGS("path", path),
tsk_null
);
TSK_FREE(path);
if(answer){ /* We are about to send 2xx INVITE(sdp) */
/* RFC 4145 - 4.1. The Setup Attribute in the Offer/Answer Model
Offer Answer
________________
active passive / holdconn
passive active / holdconn
actpass active / passive / holdconn
holdconn holdconn
*/
if((A = tsdp_header_M_findA(msrp->remote.M, "setup"))){
tmsrp_session_setup_t setup = setup_from_string(A->value);
switch(setup){
case setup_actpass:
case setup_passive:
msrp->setup = setup_active;
break;
case setup_active:
msrp->setup = setup_passive;
break;
}
tsdp_header_M_add_headers(msrp->local.M,
TSDP_HEADER_A_VA_ARGS("accept-types", "text/plain"), // FIXME: match with the offer
TSDP_HEADER_A_VA_ARGS("connection", "new"),
tsk_null
);
}
}
else{ /* We are about to send INVITE(sdp) */
tsdp_header_M_add_headers(msrp->local.M,
TSDP_HEADER_A_VA_ARGS("accept-types", "text/plain"),
TSDP_HEADER_A_VA_ARGS("connection", "new"),
tsk_null
);
}
}
// Common headers
tsdp_header_M_add_headers(msrp->local.M,
TSDP_HEADER_A_VA_ARGS("setup", setup_to_string(msrp->setup)),
tsk_null
);
}
bail:
return msrp->local.M;
}
const tsdp_header_M_t* tmsrp_media_get_negotiated_offer(tmedia_t* self)
{
tmsrp_media_t *msrp = TMSRP_MEDIA(self);
TSK_DEBUG_INFO("tmsrp_media_get_negotiated_offer");
return tsk_null;
}
int tmsrp_media_set_remote_offer(tmedia_t* self, const tsdp_message_t* offer)
{
tmsrp_media_t *msrp = TMSRP_MEDIA(self);
const tsdp_header_C_t* C;
const tsdp_header_A_t* A;
const tsdp_header_M_t* M;
int ret = -1;
size_t index;
tsk_bool_t found = tsk_false;
tsk_bool_t answer;
if(!offer || !msrp || !msrp->local.socket || msrp->local.socket->fd <= 0){
goto bail;
}
// answer or initial offer?
answer = (msrp ->local.M != tsk_null);
// Find header M associated to our "media"
for(index = 0; (M = (const tsdp_header_M_t*)tsdp_message_get_headerAt(offer, tsdp_htype_M, index)); index++){
if(tsk_strequals(M->media, self->plugin->media)){
found = tsk_true;
break;
}
}
// media found?
if(found){
TSK_OBJECT_SAFE_FREE(msrp->remote.M);
if((msrp->remote.M = (tsdp_header_M_t*)tsdp_header_clone(TSDP_HEADER(M)))){
// Find remote connection (C)
if(!msrp->remote.M->C){ // Get Session Level Connection
if((C = (const tsdp_header_C_t*)tsdp_message_get_header(offer, tsdp_htype_C))){
msrp->remote.M->C = (tsdp_header_C_t*)tsdp_header_clone(TSDP_HEADER(C));
}
}
}
}
else{
TSK_DEBUG_WARN("Failed to match media(%s)", self->plugin->media);
goto bail;
}
if(answer){ /* We are about to receive 2xx INVITE(sdp) */
}
else{ /* We are about to receive INVITE(sdp) */
msrp->setup = setup_passive; // default value
// OMA-TS-SIMPLE_IM-V1_0-20080903-C - 5.8.1 Negotiate direction of the MSRP connection setup
if((A = tsdp_header_M_findA(msrp->remote.M, "setup"))){
tmsrp_session_setup_t setup = setup_from_string(A->value);
switch(setup){
case setup_actpass:
case setup_passive:
msrp->setup = setup_active;
break;
case setup_active:
msrp->setup = setup_passive;
break;
}
}
}
bail:
return ret;
}
/* ======================================================== */
int tmsrp_send_file(tmsrp_media_t* self, const char* path)
{
if(!self){
return -1;
}
return 0;
}
int tmsrp_send_text(tmsrp_media_t* self, const char* text, const char* ctype)
{
if(!self){
return -1;
}
return 0;
}
//========================================================
// Dummy media object definition
//
static void* tmsrp_media_create(tsk_object_t *self, va_list * app)
{
tmsrp_media_t *msrp = self;
if(msrp)
{
tnet_host_t local;
// Parameters MUST appear in this order
const char* name = va_arg(*app, const char*);
const char* host = va_arg(*app, const char*);
tnet_socket_type_t socket_type = va_arg(*app, tnet_socket_type_t);
tmedia_init(TMEDIA(msrp), name);
msrp->setup = setup_actpass; // draft-denis-simple-msrp-comedia-02 - 4.1.1. Sending the offer
TMEDIA(msrp)->protocol = tsk_strdup("TCP/MSRP");
if(host == TNET_SOCKET_HOST_ANY){
// Because wa cannot use bestsource (no dest)
// if this is not used then the host address will be equal to "0.0.0.0" or "::"
// when used with SIP, the stack will provide a routable IP (e.g. 192.168.16.104)
tnet_gethostname(&local);
msrp->local.socket = TNET_SOCKET_CREATE(local, TNET_SOCKET_PORT_ANY, socket_type);
}
else{
msrp->local.socket = TNET_SOCKET_CREATE(host, TNET_SOCKET_PORT_ANY, socket_type);
}
}
else{
TSK_DEBUG_ERROR("Failed to create new dummy media.");
}
return self;
}
static void* tmsrp_media_destroy(tsk_object_t *self)
{
tmsrp_media_t *msrp = self;
if(msrp){
tsk_bool_t closeFD = (msrp->local.socket && msrp->local.socket->fd != msrp->connectedFD);
tmedia_deinit(TMEDIA(msrp));
// local
TSK_OBJECT_SAFE_FREE(msrp->local.M);
TSK_OBJECT_SAFE_FREE(msrp->local.socket);
// remote
TSK_OBJECT_SAFE_FREE(msrp->remote.M);
//TSK_OBJECT_SAFE_FREE(msrp->remote.C);
// negociated
TSK_OBJECT_SAFE_FREE(msrp->negociated.M);
if(closeFD){
tnet_sockfd_close(&msrp->connectedFD);
}
}
else{
TSK_DEBUG_ERROR("Null dummy media.");
}
return self;
}
static int tmsrp_media_cmp(const tsk_object_t *obj1, const tsk_object_t *obj2)
{
return -1;
}
static const tsk_object_def_t tmsrp_media_def_s =
{
sizeof(tmsrp_media_t),
tmsrp_media_create,
tmsrp_media_destroy,
tmsrp_media_cmp
};
const tsk_object_def_t *tmsrp_media_def_t = &tmsrp_media_def_s;
//========================================================
// Dummy media plugin definition
//
static const tmedia_plugin_def_t tmsrp_media_plugin_def_s =
{
&tmsrp_media_def_s,
"msrp",
"message",
tmsrp_media_set_params,
tmsrp_media_start,
tmsrp_media_pause,
tmsrp_media_stop,
tmsrp_media_get_local_offer,
tmsrp_media_get_negotiated_offer,
tmsrp_media_set_remote_offer
};
const tmedia_plugin_def_t *tmsrp_media_plugin_def_t = &tmsrp_media_plugin_def_s;