#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_compartment.c * @brief SigComp compartment. * An application-specific grouping of messages that relate to a peer endpoint. Depending on the signaling protocol, this grouping may * relate to application concepts such as "session", "dialog", "connection", or "association". The application allocates state * memory on a per-compartment basis, and determines when a compartment should be created or closed. * * @author Mamadou Diop <diopmamadou(at)yahoo.fr> * */ #include "tcomp_compartment.h" #include "tsk_debug.h" #include <assert.h> static void _tcomp_compartment_freeState(tcomp_compartment_t *compartment, tcomp_state_t **lpState); tcomp_compartment_t* tcomp_compartment_create(uint64_t id, uint32_t sigCompParameters, tsk_bool_t useOnlyACKedStates) { tcomp_compartment_t *compartment; if((compartment = tsk_object_new(tcomp_compartment_def_t))){ /* +---+---+---+---+---+---+---+---+ | cpb | dms | sms | +---+---+---+---+---+---+---+---+ | SigComp_version | +---+---+---+---+---+---+---+---+ */ // I always assume that remote params are equal to local params /* Identifier */ compartment->identifier = id; /* Remote parameters */ compartment->remote_parameters = tcomp_params_create(); tcomp_params_setParameters(compartment->remote_parameters, sigCompParameters); /* Local parameters */ compartment->local_parameters = tcomp_params_create(); tcomp_params_setParameters(compartment->local_parameters, sigCompParameters); /* Total size */ compartment->total_memory_size = compartment->total_memory_left = compartment->local_parameters->smsValue; /* Empty list. */ compartment->nacks = tsk_list_create(); /* Empty list. */ compartment->local_states = tsk_list_create(); /* Whether to use only ACKed states */ compartment->useOnlyACKedStates = useOnlyACKedStates; } else{ TSK_DEBUG_ERROR("Null Compartment"); } return compartment; } int tcomp_compartment_setUseOnlyACKedStates(tcomp_compartment_t* self, tsk_bool_t useOnlyACKedStates) { if(!self){ TSK_DEBUG_ERROR("Invalid parameter"); return -1; } self->useOnlyACKedStates = useOnlyACKedStates; return 0; } /**Sets remote parameters */ void tcomp_compartment_setRemoteParams(tcomp_compartment_t *compartment, tcomp_params_t *lpParams) { if(!compartment){ TSK_DEBUG_ERROR("Invalid parameter"); return; } /* CPB||DMS||SMS [1-BYTE] */ if(tcomp_params_hasCpbDmsSms(lpParams)){ tcomp_params_setCpbCode(compartment->remote_parameters, lpParams->cpbCode); tcomp_params_setDmsCode(compartment->remote_parameters, lpParams->dmsCode); tcomp_params_setSmsCode(compartment->remote_parameters, lpParams->smsCode); } /* SigComp version */ if(lpParams->SigComp_version){ compartment->remote_parameters->SigComp_version = lpParams->SigComp_version; } /* * Returned states * FIXME: check states about quota * FIXME: not tic tac * FIXME: what about returned feedback? */ if(lpParams->returnedStates && tcomp_buffer_getSize(lpParams->returnedStates)){ TSK_OBJECT_SAFE_FREE(compartment->remote_parameters->returnedStates); /* swap */ compartment->remote_parameters->returnedStates = lpParams->returnedStates; lpParams->returnedStates = 0; } } /**Sets requested feedback */ void tcomp_compartment_setReqFeedback(tcomp_compartment_t *compartment, tcomp_buffer_handle_t *feedback) { if(!compartment){ TSK_DEBUG_ERROR("Invalid parameter."); return; } tsk_safeobj_lock(compartment); /* Delete old */ TSK_OBJECT_SAFE_FREE(compartment->lpReqFeedback); compartment->lpReqFeedback = tcomp_buffer_create(tcomp_buffer_getBuffer(feedback), tcomp_buffer_getSize(feedback)); tsk_safeobj_unlock(compartment); } /**Sets returned feedback */ void tcomp_compartment_setRetFeedback(tcomp_compartment_t *compartment, tcomp_buffer_handle_t *feedback) { if(!compartment){ TSK_DEBUG_ERROR("Invalid parameter."); return; } tsk_safeobj_lock(compartment); // Delete old TSK_OBJECT_SAFE_FREE(compartment->lpRetFeedback); compartment->lpRetFeedback = tcomp_buffer_create(tcomp_buffer_getBuffer(feedback), tcomp_buffer_getSize(feedback)); /* * ACK STATE ==> Returned feedback contains the partial state-id. */ if(compartment->compressorData/* && !compartment->compressorData_isStream*/){ tcomp_buffer_handle_t *stateid = tcomp_buffer_create(tcomp_buffer_getBufferAtPos(feedback, 1), tcomp_buffer_getSize(feedback)-1); compartment->ackGhost(compartment->compressorData, stateid); TSK_OBJECT_SAFE_FREE(stateid); } tsk_safeobj_unlock(compartment); } /**Clears are compartment from the history. */ void tcomp_compartment_clearStates(tcomp_compartment_t *compartment) { if(!compartment){ TSK_DEBUG_ERROR("NULL sigcomp compartment."); return; } tsk_safeobj_lock(compartment); tsk_list_clear_items(compartment->local_states); compartment->total_memory_left = compartment->total_memory_size; tsk_safeobj_unlock(compartment); } /**Removes a state from the compartment by priority. */ void tcomp_compartment_freeStateByPriority(tcomp_compartment_t *compartment) { tcomp_state_t *lpState; tsk_list_item_t *item; if(!compartment){ TSK_DEBUG_ERROR("Invalid parameter."); return; } tsk_safeobj_lock(compartment); lpState = 0; item = 0; /* * The order in which the existing state items are freed is determined by the state_retention_priority, which is set when the * state items are created. The state_retention_priority of 65535 is reserved for locally available states; these states must always be * freed first. Apart from this special case, states with the lowest state_retention_priority are always freed first. In the event of * a tie, then the state item created first in the compartment is also the first to be freed. */ tsk_list_foreach(item, compartment->local_states){ tcomp_state_t *curr = item->data; if(!curr){ continue; } /* Local state ? */ if(curr->retention_priority == 65535){ lpState = curr; break; } /* Lower priority? */ if(!lpState || curr->retention_priority < lpState->retention_priority){ lpState = curr; continue; } } if(lpState){ compartment->total_memory_left += TCOMP_GET_STATE_SIZE(lpState); tsk_list_remove_item_by_data(compartment->local_states, lpState); } tsk_safeobj_unlock(compartment); } /**Removes a state from the compartment. */ static void _tcomp_compartment_freeState(tcomp_compartment_t *compartment, tcomp_state_t **lpState) { if(!compartment){ TSK_DEBUG_ERROR("Invalid parameter."); return; } tsk_safeobj_lock(compartment); compartment->total_memory_left += TCOMP_GET_STATE_SIZE(*lpState); tsk_list_remove_item_by_data(compartment->local_states, *lpState); *lpState = tsk_null; tsk_safeobj_unlock(compartment); } /**Remove states * Called by UDVM after STATE_FREE instruction */ void tcomp_compartment_freeStates(tcomp_compartment_t *compartment, tcomp_tempstate_to_free_t **tempStates, uint8_t size) { uint8_t i; tcomp_state_t *lpState; tsk_list_item_t *item; if(!compartment){ TSK_DEBUG_ERROR("Invalid parameter."); return; } if(!tempStates || !size){ return; } lpState = tsk_null; item = tsk_null; for (i = 0; i < size; i++){ /* lock */ tsk_safeobj_lock(compartment); tsk_list_foreach(item, compartment->local_states){ tcomp_state_t *curr = item->data; /* Compare current state with provided partial state */ if(tcomp_buffer_startsWith(curr->identifier, tempStates[i]->identifier)){ /* * If more than one state item in the compartment matches the partial state identifier, * then the state free request is ignored. */ TSK_DEBUG_INFO("Request to free state with usage_count=%d", curr->usage_count); if(tcomp_state_dec_usage_count(curr) == 0){ if(lpState){ lpState = tsk_null; break; } else{ lpState = curr; } } } } /* unlock */ tsk_safeobj_unlock(compartment); if(lpState){ _tcomp_compartment_freeState(compartment, &lpState); } } } /**Adds a state to the compartment. */ void tcomp_compartment_addState(tcomp_compartment_t *compartment, tcomp_state_t **lpState) { tsk_list_item_t *item; int32_t usage_count = 0; const tcomp_buffer_handle_t *identifier; if(!compartment || !lpState || !*lpState){ TSK_DEBUG_ERROR("Invalid parameter."); return; } tsk_safeobj_lock(compartment); tcomp_state_makeValid(*lpState); identifier = (*lpState)->identifier; // check if the state exist tsk_list_foreach(item, compartment->local_states){ if(tcomp_buffer_startsWith(((tcomp_state_t*)item->data)->identifier, (*lpState)->identifier)){ *lpState = ((tcomp_state_t*)item->data); // override usage_count = tcomp_state_inc_usage_count(*lpState); break; } } if(usage_count == 0){ // alread exist? compartment->total_memory_left -= TCOMP_GET_STATE_SIZE(*lpState); usage_count = tcomp_state_inc_usage_count(*lpState); tsk_list_push_back_data(compartment->local_states, ((void**) lpState)); } TSK_DEBUG_INFO("SigComp - Add new state with usage_count=%d and id=", usage_count); tcomp_buffer_print(identifier); *lpState = tsk_null; tsk_safeobj_unlock(compartment); } /**Finds a state. */ uint32_t tcomp_compartment_findState(tcomp_compartment_t *compartment, const tcomp_buffer_handle_t *partial_identifier, tcomp_state_t **lpState) { uint32_t count = 0; tsk_list_item_t *item; if(!compartment){ TSK_DEBUG_ERROR("Invalid parameter"); return 0; } tsk_safeobj_lock(compartment); tsk_list_foreach(item, compartment->local_states){ tcomp_state_t *curr = item->data; if(tcomp_buffer_startsWith(curr->identifier, partial_identifier)){ *lpState = curr; // override count++; } } tsk_safeobj_unlock(compartment); return count; } /**Removes a Ghost state. */ void tcomp_compartment_freeGhostState(tcomp_compartment_t *compartment) { if(!compartment){ TSK_DEBUG_ERROR("Invalid parameter."); return; } tsk_safeobj_lock(compartment); if(compartment->compressorData){ compartment->freeGhostState(compartment->compressorData); } else{ TSK_DEBUG_WARN("No compression data to free."); } tsk_safeobj_unlock(compartment); } /**Adds a NACK to the compartment. */ void tcomp_compartment_addNack(tcomp_compartment_t *compartment, const uint8_t nackId[TSK_SHA1_DIGEST_SIZE]) { tcomp_buffer_handle_t *id; if(!compartment){ TSK_DEBUG_ERROR("Invalid parameter."); return; } #if 0 { int i; TSK_DEBUG_INFO("Save NACK with id:"); for(i = 0; i < TSK_SHA1_DIGEST_SIZE; ++i){ printf("%x ", nackId[i]); } printf("\n"); } #endif tsk_safeobj_lock(compartment); if(compartment->nacks_history_count >= NACK_MAX_HISTORY_SIZE){ tsk_list_remove_last_item(compartment->nacks); compartment->nacks_history_count--; } id = tcomp_buffer_create(nackId, TSK_SHA1_DIGEST_SIZE); tsk_list_push_back_data(compartment->nacks, ((void**) &id)); compartment->nacks_history_count++; tsk_safeobj_unlock(compartment); } /**Checks if the NACK exist. */ tsk_bool_t tcomp_compartment_hasNack(tcomp_compartment_t *compartment, const tcomp_buffer_handle_t *nackId) { tsk_bool_t ret = tsk_false; tsk_list_item_t *item; if(!compartment){ TSK_DEBUG_ERROR("Invalid parameter."); return tsk_false; } tsk_safeobj_lock(compartment); item = tsk_null; tsk_list_foreach(item, compartment->nacks){ tcomp_buffer_handle_t *curr = item->data; if(tcomp_buffer_equals(curr, nackId)){ TSK_DEBUG_INFO("SigComp - Nack found."); ret = tsk_true; break; } } tsk_safeobj_unlock(compartment); return ret; } //======================================================== // State object definition // static tsk_object_t* tcomp_compartment_ctor(tsk_object_t* self, va_list * app) { tcomp_compartment_t *compartment = self; if(compartment){ /* Initialize safeobject */ tsk_safeobj_init(compartment); } return self; } static tsk_object_t* tcomp_compartment_dtor(tsk_object_t* self) { tcomp_compartment_t *compartment = self; if(compartment){ /* Deinitialize safeobject */ tsk_safeobj_deinit(compartment); /* Delete feedbacks */ TSK_OBJECT_SAFE_FREE(compartment->lpReqFeedback); TSK_OBJECT_SAFE_FREE(compartment->lpRetFeedback); /* Delete Nacks */ TSK_OBJECT_SAFE_FREE(compartment->nacks); /* Delete Compressor data */ TSK_OBJECT_SAFE_FREE(compartment->compressorData); compartment->ackGhost = tsk_null; compartment->freeGhostState = tsk_null; /* Delete params */ TSK_OBJECT_SAFE_FREE(compartment->local_parameters); TSK_OBJECT_SAFE_FREE(compartment->remote_parameters); /* Delete NACKS */ TSK_OBJECT_SAFE_FREE(compartment->nacks); /* Delete local states */ TSK_OBJECT_SAFE_FREE(compartment->local_states); } else{ TSK_DEBUG_ERROR("Null Compartment"); } return self; } static int tcomp_compartment_cmp(const tsk_object_t *obj1, const tsk_object_t *obj2) { const tcomp_compartment_t *compartment1 = obj1; const tcomp_compartment_t *compartment2 = obj2; int64_t res = (compartment1->identifier - compartment2->identifier); return res > 0 ? (int)1 : (res < 0 ? (int)-1 : (int)0); } static const tsk_object_def_t tsk_compartment_def_s = { sizeof(tcomp_compartment_t), tcomp_compartment_ctor, tcomp_compartment_dtor, tcomp_compartment_cmp }; const tsk_object_def_t *tcomp_compartment_def_t = &tsk_compartment_def_s;