doubango/tinySIGCOMP/src/tcomp_decompressordisp.c

421 lines
12 KiB
C
Executable File

/*
* Copyright (C) 2010-2011 Mamadou Diop.
*
* Contact: Mamadou Diop <diopmamadou(at)doubango[dot]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 tcomp_decompressordisp.c
* @brief Entity that receives SigComp messages, invokes a UDVM, and forwards the resulting decompressed messages to the application.
*
* @author Mamadou Diop <diopmamadou(at)yahoo.fr>
*
*/
#include "tcomp_decompressordisp.h"
#include "tcomp_message.h"
#include "tcomp_udvm.h"
#include "tsk_debug.h"
#include <assert.h>
#include <string.h>
#define TCOMP_MAX_STREAM_BUFFER_SIZE 65535
#define TCOMP_NACK_SUPPORTED(dispatcher) (dispatcher->stateHandler->sigcomp_parameters->SigComp_version >= 0x02)
/**Prefdicate function
*/
static int pred_find_streambuffer_by_id(const tsk_list_item_t *item, const void *id)
{
if(item && item->data) {
tcomp_stream_buffer_t *streambuffer = item->data;
int64_t res = (streambuffer->id - *((int64_t*)id));
return res > 0 ? (int)1 : (res < 0 ? (int)-1 : (int)0);
}
return -1;
}
tcomp_stream_buffer_t* tcomp_stream_buffer_create(uint64_t id)
{
return tsk_object_new(tcomp_stream_buffer_def_t, id);
}
tcomp_decompressordisp_t* tcomp_decompressordisp_create(const tcomp_statehandler_t* statehandler)
{
return tsk_object_new(tcomp_decompressordisp_def_t, statehandler);
}
/**Decompress a message.
*/
tsk_bool_t tcomp_decompressordisp_decompress(tcomp_decompressordisp_t *dispatcher, const void* input_ptr, tsk_size_t input_size, tcomp_result_t *lpResult)
{
tsk_bool_t ret = tsk_true;
uint64_t streamId = 0;
const tsk_list_item_t *item_const;
if(!dispatcher) {
TSK_DEBUG_ERROR("Invalid parameter.");
return tsk_false;
}
/*
* Check if transport type changed.
*/
if(lpResult->isStreamBased) {
if(!dispatcher->streamBuffers) {
dispatcher->streamBuffers = tsk_list_create();
}
streamId = lpResult->streamId;
ret = tcomp_decompressordisp_appendStream(dispatcher, input_ptr, input_size, streamId);
if(!ret) {
TSK_DEBUG_ERROR("Failed to append new stream buffer.");
return 0;
}
}
if(lpResult->isStreamBased) {
tsk_size_t size = 0;
uint32_t discard_count = 0;
tcomp_stream_buffer_t *lpBuffer;
item_const = tsk_list_find_item_by_pred(dispatcher->streamBuffers, pred_find_streambuffer_by_id, &streamId);
if(!item_const || !(lpBuffer = item_const->data)) {
TSK_DEBUG_ERROR("Failed to find stream buffer by id %llu.", streamId);
return 0;
}
if(ret && tcomp_decompressordisp_getNextStreamMsg(dispatcher, streamId, &discard_count, &size)) {
ret &= tcomp_decompressordisp_internalDecompress(dispatcher, tcomp_buffer_getBuffer(lpBuffer->buffer), size, &lpResult);
/* remove buffer and discard */
tcomp_buffer_discardLastBytes(lpBuffer->buffer, discard_count);
ret &= tcomp_buffer_removeBuff(lpBuffer->buffer, 0, size);
}
if(discard_count) {
tcomp_buffer_discardLastBytes(lpBuffer->buffer, discard_count);
}
if(size) {
//ret&= lpBuffer->removeBuff(0, (size));
}
}
else {
ret &= tcomp_decompressordisp_internalDecompress(dispatcher, input_ptr, input_size, &lpResult);
}
return ret;
}
/**Gets the next message from the queue.
*/
tsk_bool_t tcomp_decompressordisp_getNextMessage(tcomp_decompressordisp_t *dispatcher, tcomp_result_t *lpResult)
{
tsk_bool_t ret = tsk_true;
tsk_size_t size=0;
uint32_t discard_count = 0;
uint64_t streamId;
tcomp_stream_buffer_t *lpBuffer;
const tsk_list_item_t *item_const;
if(!dispatcher) {
TSK_DEBUG_ERROR("Invalid parameter.");
return tsk_false;
}
streamId = lpResult->streamId;
item_const = tsk_list_find_item_by_pred(dispatcher->streamBuffers, pred_find_streambuffer_by_id, &streamId);
if(!item_const || !(lpBuffer = item_const->data)) {
TSK_DEBUG_ERROR("Failed to find stream buffer by id %llu.", streamId);
return tsk_false;
}
if(ret && tcomp_decompressordisp_getNextStreamMsg(dispatcher, streamId, &discard_count, &size)) {
ret &= tcomp_decompressordisp_internalDecompress(dispatcher, tcomp_buffer_getBuffer(lpBuffer->buffer), size, &lpResult);
/* remove buffer and discard */
tcomp_buffer_discardLastBytes(lpBuffer->buffer, discard_count);
ret &= tcomp_buffer_removeBuff(lpBuffer->buffer, 0, size);
}
else {
ret = tsk_false; /* Is it right? */
}
if(discard_count) {
tcomp_buffer_discardLastBytes(lpBuffer->buffer, discard_count);
}
return ret;
}
/**Decompress a message.
*/
tsk_bool_t tcomp_decompressordisp_internalDecompress(tcomp_decompressordisp_t *dispatcher, const void* input_ptr, const tsk_size_t input_size, tcomp_result_t **lpResult)
{
tcomp_message_t *sigCompMessage = tsk_null;
tcomp_udvm_t *sigCompUDVM = tsk_null;
tsk_bool_t ret = tsk_false;
int32_t nack_code = NACK_NONE;
if(!dispatcher) {
TSK_DEBUG_ERROR("Invalid parameter.");
goto bail;
}
sigCompMessage = tcomp_message_create(input_ptr, input_size, (*lpResult)->isStreamBased, &nack_code);
if(!sigCompMessage || !sigCompMessage->isOK) {
TSK_DEBUG_ERROR("Failed to create new sigcomp message");
if(nack_code != NACK_NONE && ((*lpResult)->isNack = TCOMP_NACK_SUPPORTED(dispatcher))) {
tcomp_nackinfo_write_3((*lpResult)->nack_info,
nack_code,
input_ptr, input_size);
}
goto bail;
}
else if(sigCompMessage->isNack && TCOMP_NACK_SUPPORTED(dispatcher)) {
/* Remote party send us a NACK --> handle it */
tcomp_statehandler_handleNack((tcomp_statehandler_t*)dispatcher->stateHandler, (const tcomp_nackinfo_t*)sigCompMessage->nack_info);
(*lpResult)->isNack = tsk_true;
goto bail;
}
/* Create new UDVM entity for each SigComp message */
sigCompUDVM = tcomp_udvm_create(sigCompMessage, (tcomp_statehandler_t*)dispatcher->stateHandler, *lpResult);
/* Decompress message */
ret = tcomp_udvm_decompress(sigCompUDVM);
/* decompression failed --> returns nack if supported */
if(!ret) {
/* Decompression failed --> return NACK message to the application layer */
(*lpResult)->isNack = TCOMP_NACK_SUPPORTED(dispatcher);
}
bail:
/* Delete Message */
TSK_OBJECT_SAFE_FREE(sigCompMessage);
/* Delete UDVM entity */
TSK_OBJECT_SAFE_FREE(sigCompUDVM);
return ret;
}
/**Appends stream buffer.
*/
tsk_bool_t tcomp_decompressordisp_appendStream(tcomp_decompressordisp_t *dispatcher, const void* input_ptr, tsk_size_t input_size, uint64_t streamId)
{
tcomp_stream_buffer_t* lpBuffer = tsk_null;
const tsk_list_item_t *item_const;
if(!dispatcher) {
TSK_DEBUG_ERROR("Invalid parameter.");
return tsk_false;
}
item_const = tsk_list_find_item_by_pred(dispatcher->streamBuffers, pred_find_streambuffer_by_id, &streamId);
if(!item_const || !(lpBuffer = item_const->data)) {
/* First time we get this stream ID */
tcomp_buffer_handle_t *newbuf = tcomp_stream_buffer_create(streamId);
if(newbuf) {
lpBuffer = newbuf;
lpBuffer->buffer = tcomp_buffer_create_null();
tsk_list_push_back_data(dispatcher->streamBuffers, ((void**) &newbuf));
}
else {
TSK_DEBUG_ERROR("Failed to create new stream buffer.");
return tsk_false;
}
}
/* Check if buffer is too large */
if(lpBuffer->buffer && (tcomp_buffer_getSize(lpBuffer->buffer) + input_size) > TCOMP_MAX_STREAM_BUFFER_SIZE) {
tcomp_buffer_freeBuff(lpBuffer->buffer);
return tsk_false;
}
/* append new buffer */
if(!tcomp_buffer_appendBuff(lpBuffer->buffer, input_ptr, input_size)) {
TSK_DEBUG_ERROR("Failed to append new buffer.");
tcomp_buffer_freeBuff(lpBuffer->buffer);
return tsk_false;
}
return tsk_true;
}
/**Gets the next message from the queue.
*/
tsk_bool_t tcomp_decompressordisp_getNextStreamMsg(tcomp_decompressordisp_t *dispatcher, uint64_t streamId, uint32_t *discard_count, tsk_size_t *size)
{
tcomp_stream_buffer_t *lpBuffer;
const tsk_list_item_t *item_const;
uint8_t quote_count = 0;
uint8_t* start;
uint8_t* end;
if(!dispatcher) {
TSK_DEBUG_ERROR("Invalid parameter.");
return tsk_false;
}
/*
* RFC 3320 - 4.2.1. Decompressor Dispatcher Strategies [strategie 1]
*/
item_const = tsk_list_find_item_by_pred(dispatcher->streamBuffers, pred_find_streambuffer_by_id, &streamId);
if(!item_const || !(lpBuffer = item_const->data)) {
TSK_DEBUG_ERROR("Failed to find stream buffer by id %llu.", streamId);
return tsk_false;
}
*size = 0;
*discard_count = 0;
quote_count = 0;
start = tcomp_buffer_getBuffer(lpBuffer->buffer);
end = (start + tcomp_buffer_getSize(lpBuffer->buffer));
while(start<end) {
if(*start==0xff) {
start++;
if(*start==0xff) {
/* end message */
if(*size) {
return tsk_true;
}
else { /* message is empty --> delete this empty message(length=2) */
start--;
memcpy(start, (start+2), (end-start));
(*discard_count)+=2;
end-=2;
continue;
}
}
quote_count = *start;
memcpy((start), (start+1), (end-start));
end--;
(*discard_count)++;
start+=(quote_count);
(*size)+=(1+quote_count);
}
else {
start++;
(*size)++;
}
}
return tsk_false;
}
//========================================================
// SigComp decompressor dispatcher object definition
//
static tsk_object_t* tcomp_decompressordisp_ctor(tsk_object_t* self, va_list * app)
{
tcomp_decompressordisp_t *decompressordisp = self;
if(decompressordisp) {
decompressordisp->stateHandler = va_arg(*app, const tcomp_statehandler_t*);
/* Initialize safeobject */
tsk_safeobj_init(decompressordisp);
}
else {
TSK_DEBUG_ERROR("Failed to create new decompressor dispatcher.");
}
return self;
}
static tsk_object_t* tcomp_decompressordisp_dtor(tsk_object_t *self)
{
tcomp_decompressordisp_t *decompressordisp = self;
if(decompressordisp) {
/* Deinitialize safeobject */
tsk_safeobj_deinit(decompressordisp);
TSK_OBJECT_SAFE_FREE(decompressordisp->streamBuffers);
}
else {
TSK_DEBUG_ERROR("Null dispatcher.");
}
return self;
}
static const tsk_object_def_t tcomp_decompressordisp_def_s = {
sizeof(tcomp_decompressordisp_t),
tcomp_decompressordisp_ctor,
tcomp_decompressordisp_dtor,
tsk_null
};
const tsk_object_def_t *tcomp_decompressordisp_def_t = &tcomp_decompressordisp_def_s;
//========================================================
// SigComp stream buffer object definition
//
static tsk_object_t* tcomp_stream_buffer_ctor(tsk_object_t* self, va_list * app)
{
tcomp_stream_buffer_t *stream_buffer = self;
if(stream_buffer) {
stream_buffer->id = va_arg(*app, uint64_t);
}
else {
TSK_DEBUG_ERROR("Failed to create new stream buffer.");
}
return self;
}
static tsk_object_t* tcomp_stream_buffer_dtor(tsk_object_t* self)
{
tcomp_stream_buffer_t *stream_buffer = self;
if(stream_buffer) {
TSK_OBJECT_SAFE_FREE(stream_buffer->buffer);
}
else {
TSK_DEBUG_ERROR("Null stream buffer.");
}
return self;
}
static const tsk_object_def_t tcomp_stream_buffer_def_s = {
sizeof(tcomp_stream_buffer_t),
tcomp_stream_buffer_ctor,
tcomp_stream_buffer_dtor,
tsk_null
};
const tsk_object_def_t* tcomp_stream_buffer_def_t = &tcomp_stream_buffer_def_s;