#if HAVE_CRT #define _CRTDBG_MAP_ALLOC #include <stdlib.h> #include <crtdbg.h> #endif //HAVE_CRT /* * Copyright (C) 2020, University of the Basque Country (UPV/EHU) * Contact for licensing options: <licensing-mcpttclient(at)mcopenplatform(dot)com> * * The original file was part of Open Source Doubango Framework * Copyright (C) 2010-2011 Mamadou Diop. * Copyright (C) 2012 Doubango Telecom <http://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 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;