You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
suricata/src/util-storage.c

552 lines
14 KiB
C

/* Copyright (C) 2007-2013 Open Information Security Foundation
*
* You can copy, redistribute or modify this Program under the terms of
* the GNU General Public License version 2 as published by the Free
* Software Foundation.
*
* This program 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
* version 2 along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
/**
* \file
*
* \author Victor Julien <victor@inliniac.net>
*
* Storage API
*/
#include "suricata-common.h"
#include "util-unittest.h"
#include "util-storage.h"
typedef struct StorageMapping_ {
const char *name;
StorageEnum type; // host, flow, tx, stream, ssn, etc
unsigned int size;
void *(*Alloc)(unsigned int);
void (*Free)(void *);
} StorageMapping;
/** \brief list of StorageMapping used at registration time */
typedef struct StorageList_ {
StorageMapping map;
int id;
struct StorageList_ *next;
} StorageList;
static StorageList *storage_list = NULL;
static int storage_max_id[STORAGE_MAX];
static int storage_registraton_closed = 0;
static StorageMapping **storage_map = NULL;
static const char *StoragePrintType(StorageEnum type)
{
switch(type) {
case STORAGE_HOST:
return "host";
case STORAGE_FLOW:
return "flow";
case STORAGE_IPPAIR:
return "ippair";
case STORAGE_DEVICE:
return "livedevice";
case STORAGE_MAX:
return "max";
}
return "invalid";
}
void StorageInit(void)
{
memset(&storage_max_id, 0x00, sizeof(storage_max_id));
storage_list = NULL;
storage_map = NULL;
storage_registraton_closed = 0;
}
void StorageCleanup(void)
{
if (storage_map) {
int i;
for (i = 0; i < STORAGE_MAX; i++) {
if (storage_map[i] != NULL) {
SCFree(storage_map[i]);
storage_map[i] = NULL;
}
}
SCFree(storage_map);
storage_map = NULL;
}
StorageList *entry = storage_list;
while (entry) {
StorageList *next = entry->next;
SCFree(entry);
entry = next;
}
storage_list = NULL;
}
int StorageRegister(const StorageEnum type, const char *name, const unsigned int size, void *(*Alloc)(unsigned int), void (*Free)(void *))
{
if (storage_registraton_closed)
return -1;
if (type >= STORAGE_MAX || name == NULL || strlen(name) == 0 ||
size == 0 || (size != sizeof(void *) && Alloc == NULL) || Free == NULL)
return -1;
StorageList *list = storage_list;
while (list) {
if (strcmp(name, list->map.name) == 0 && type == list->map.type) {
SCLogError(SC_ERR_INVALID_VALUE, "storage for type \"%s\" with "
"name \"%s\" already registered", StoragePrintType(type),
name);
return -1;
}
list = list->next;
}
StorageList *entry = SCMalloc(sizeof(StorageList));
if (unlikely(entry == NULL))
return -1;
memset(entry, 0x00, sizeof(StorageList));
entry->map.type = type;
entry->map.name = name;
entry->map.size = size;
entry->map.Alloc = Alloc;
entry->map.Free = Free;
entry->id = storage_max_id[type]++;
entry->next = storage_list;
storage_list = entry;
return entry->id;
}
int StorageFinalize(void)
{
int count = 0;
int i;
storage_registraton_closed = 1;
for (i = 0; i < STORAGE_MAX; i++) {
if (storage_max_id[i] > 0)
count++;
}
if (count == 0)
return 0;
storage_map = SCMalloc(sizeof(StorageMapping *) * STORAGE_MAX);
if (unlikely(storage_map == NULL)) {
return -1;
}
memset(storage_map, 0x00, sizeof(StorageMapping *) * STORAGE_MAX);
for (i = 0; i < STORAGE_MAX; i++) {
if (storage_max_id[i] > 0) {
storage_map[i] = SCMalloc(sizeof(StorageMapping) * storage_max_id[i]);
if (storage_map[i] == NULL)
return -1;
memset(storage_map[i], 0x00, sizeof(StorageMapping) * storage_max_id[i]);
}
}
StorageList *entry = storage_list;
while (entry) {
if (storage_map[entry->map.type] != NULL) {
storage_map[entry->map.type][entry->id].name = entry->map.name;
storage_map[entry->map.type][entry->id].type = entry->map.type;
storage_map[entry->map.type][entry->id].size = entry->map.size;
storage_map[entry->map.type][entry->id].Alloc = entry->map.Alloc;
storage_map[entry->map.type][entry->id].Free = entry->map.Free;
}
StorageList *next = entry->next;
SCFree(entry);
entry = next;
};
storage_list = NULL;
#ifdef DEBUG
for (i = 0; i < STORAGE_MAX; i++) {
if (storage_map[i] == NULL)
continue;
int j;
for (j = 0; j < storage_max_id[i]; j++) {
StorageMapping *m = &storage_map[i][j];
SCLogDebug("type \"%s\" name \"%s\" size \"%"PRIuMAX"\"",
StoragePrintType(m->type), m->name, (uintmax_t)m->size);
}
}
#endif
return 0;
}
unsigned int StorageGetCnt(StorageEnum type)
{
return storage_max_id[type];
}
/** \brief get the size of the void array used to store
* the pointers
* \retval size size in bytes, can return 0 if not storage is needed
*
* \todo we could return -1 when registration isn't closed yet, however
* this will break lots of tests currently, so not doing it now */
unsigned int StorageGetSize(StorageEnum type)
{
return storage_max_id[type] * sizeof(void *);
}
void *StorageGetById(const Storage *storage, const StorageEnum type, const int id)
{
#ifdef DEBUG
BUG_ON(!storage_registraton_closed);
#endif
SCLogDebug("storage %p id %d", storage, id);
if (storage == NULL)
return NULL;
return storage[id];
}
int StorageSetById(Storage *storage, const StorageEnum type, const int id, void *ptr)
{
#ifdef DEBUG
BUG_ON(!storage_registraton_closed);
#endif
SCLogDebug("storage %p id %d", storage, id);
if (storage == NULL)
return -1;
storage[id] = ptr;
return 0;
}
void *StorageAllocByIdPrealloc(Storage *storage, StorageEnum type, int id)
{
#ifdef DEBUG
BUG_ON(!storage_registraton_closed);
#endif
SCLogDebug("storage %p id %d", storage, id);
StorageMapping *map = &storage_map[type][id];
if (storage[id] == NULL && map->Alloc != NULL) {
storage[id] = map->Alloc(map->size);
if (storage[id] == NULL) {
return NULL;
}
}
return storage[id];
}
void *StorageAllocById(Storage **storage, StorageEnum type, int id)
{
#ifdef DEBUG
BUG_ON(!storage_registraton_closed);
#endif
SCLogDebug("storage %p id %d", storage, id);
StorageMapping *map = &storage_map[type][id];
Storage *store = *storage;
if (store == NULL) {
// coverity[suspicious_sizeof : FALSE]
store = SCMalloc(sizeof(void *) * storage_max_id[type]);
if (unlikely(store == NULL))
return NULL;
memset(store, 0x00, sizeof(void *) * storage_max_id[type]);
}
SCLogDebug("store %p", store);
if (store[id] == NULL && map->Alloc != NULL) {
store[id] = map->Alloc(map->size);
if (store[id] == NULL) {
SCFree(store);
*storage = NULL;
return NULL;
}
}
*storage = store;
return store[id];
}
void StorageFreeById(Storage *storage, StorageEnum type, int id)
{
#ifdef DEBUG
BUG_ON(!storage_registraton_closed);
#endif
#ifdef UNITTESTS
if (storage_map == NULL)
return;
#endif
SCLogDebug("storage %p id %d", storage, id);
Storage *store = storage;
if (store != NULL) {
SCLogDebug("store %p", store);
if (store[id] != NULL) {
StorageMapping *map = &storage_map[type][id];
map->Free(store[id]);
store[id] = NULL;
}
}
}
void StorageFreeAll(Storage *storage, StorageEnum type)
{
if (storage == NULL)
return;
#ifdef DEBUG
BUG_ON(!storage_registraton_closed);
#endif
#ifdef UNITTESTS
if (storage_map == NULL)
return;
#endif
Storage *store = storage;
int i;
for (i = 0; i < storage_max_id[type]; i++) {
if (store[i] != NULL) {
StorageMapping *map = &storage_map[type][i];
map->Free(store[i]);
store[i] = NULL;
}
}
}
void StorageFree(Storage **storage, StorageEnum type)
{
if (*storage == NULL)
return;
#ifdef DEBUG
BUG_ON(!storage_registraton_closed);
#endif
#ifdef UNITTESTS
if (storage_map == NULL)
return;
#endif
Storage *store = *storage;
int i;
for (i = 0; i < storage_max_id[type]; i++) {
if (store[i] != NULL) {
StorageMapping *map = &storage_map[type][i];
map->Free(store[i]);
store[i] = NULL;
}
}
SCFree(*storage);
*storage = NULL;
}
#ifdef UNITTESTS
static void *StorageTestAlloc(unsigned int size)
{
void *x = SCMalloc(size);
return x;
}
static void StorageTestFree(void *x)
{
if (x)
SCFree(x);
}
static int StorageTest01(void)
{
StorageInit();
int id = StorageRegister(STORAGE_HOST, "test", 8, StorageTestAlloc, StorageTestFree);
if (id < 0)
goto error;
id = StorageRegister(STORAGE_HOST, "variable", 24, StorageTestAlloc, StorageTestFree);
if (id < 0)
goto error;
id = StorageRegister(STORAGE_FLOW, "store", sizeof(void *), StorageTestAlloc, StorageTestFree);
if (id < 0)
goto error;
if (StorageFinalize() < 0)
goto error;
StorageCleanup();
return 1;
error:
StorageCleanup();
return 0;
}
struct StorageTest02Data {
int abc;
};
static void *StorageTest02Init(unsigned int size)
{
struct StorageTest02Data *data = (struct StorageTest02Data *)SCMalloc(size);
if (data != NULL)
data->abc = 1234;
return (void *)data;
}
static int StorageTest02(void)
{
struct StorageTest02Data *test = NULL;
StorageInit();
int id1 = StorageRegister(STORAGE_HOST, "test", 4, StorageTest02Init, StorageTestFree);
if (id1 < 0) {
printf("StorageRegister failed (2): ");
goto error;
}
int id2 = StorageRegister(STORAGE_HOST, "test2", 4, StorageTest02Init, StorageTestFree);
if (id2 < 0) {
printf("StorageRegister failed (2): ");
goto error;
}
if (StorageFinalize() < 0) {
printf("StorageFinalize failed: ");
goto error;
}
Storage *storage = NULL;
void *data = StorageAllocById(&storage, STORAGE_HOST, id1);
if (data == NULL) {
printf("StorageAllocById failed, data == NULL, storage %p: ", storage);
goto error;
}
test = (struct StorageTest02Data *)data;
if (test->abc != 1234) {
printf("setup failed, test->abc != 1234, but %d (1):", test->abc);
goto error;
}
test->abc = 4321;
data = StorageAllocById(&storage, STORAGE_HOST, id2);
if (data == NULL) {
printf("StorageAllocById failed, data == NULL, storage %p: ", storage);
goto error;
}
test = (struct StorageTest02Data *)data;
if (test->abc != 1234) {
printf("setup failed, test->abc != 1234, but %d (2):", test->abc);
goto error;
}
data = StorageGetById(storage, STORAGE_HOST, id1);
if (data == NULL) {
printf("StorageAllocById failed, data == NULL, storage %p: ", storage);
goto error;
}
test = (struct StorageTest02Data *)data;
if (test->abc != 4321) {
printf("setup failed, test->abc != 4321, but %d (3):", test->abc);
goto error;
}
//StorageFreeById(storage, STORAGE_HOST, id1);
//StorageFreeById(storage, STORAGE_HOST, id2);
StorageFree(&storage, STORAGE_HOST);
StorageCleanup();
return 1;
error:
StorageCleanup();
return 0;
}
static int StorageTest03(void)
{
StorageInit();
int id = StorageRegister(STORAGE_HOST, "test", 8, StorageTestAlloc, StorageTestFree);
if (id < 0)
goto error;
id = StorageRegister(STORAGE_HOST, "test", 8, StorageTestAlloc, StorageTestFree);
if (id != -1) {
printf("duplicate registration should have failed: ");
goto error;
}
id = StorageRegister(STORAGE_HOST, "test1", 6, NULL, StorageTestFree);
if (id != -1) {
printf("duplicate registration should have failed (2): ");
goto error;
}
id = StorageRegister(STORAGE_HOST, "test2", 8, StorageTestAlloc, NULL);
if (id != -1) {
printf("duplicate registration should have failed (3): ");
goto error;
}
id = StorageRegister(STORAGE_HOST, "test3", 0, StorageTestAlloc, StorageTestFree);
if (id != -1) {
printf("duplicate registration should have failed (4): ");
goto error;
}
id = StorageRegister(STORAGE_HOST, "", 8, StorageTestAlloc, StorageTestFree);
if (id != -1) {
printf("duplicate registration should have failed (5): ");
goto error;
}
id = StorageRegister(STORAGE_HOST, NULL, 8, StorageTestAlloc, StorageTestFree);
if (id != -1) {
printf("duplicate registration should have failed (6): ");
goto error;
}
id = StorageRegister(STORAGE_MAX, "test4", 8, StorageTestAlloc, StorageTestFree);
if (id != -1) {
printf("duplicate registration should have failed (7): ");
goto error;
}
id = StorageRegister(38, "test5", 8, StorageTestAlloc, StorageTestFree);
if (id != -1) {
printf("duplicate registration should have failed (8): ");
goto error;
}
id = StorageRegister(-1, "test6", 8, StorageTestAlloc, StorageTestFree);
if (id != -1) {
printf("duplicate registration should have failed (9): ");
goto error;
}
StorageCleanup();
return 1;
error:
StorageCleanup();
return 0;
}
void StorageRegisterTests(void)
{
UtRegisterTest("StorageTest01", StorageTest01);
UtRegisterTest("StorageTest02", StorageTest02);
UtRegisterTest("StorageTest03", StorageTest03);
}
#endif