1 /* Copyright (C) 2007-2013 Open Information Security Foundation
2  *
3  * You can copy, redistribute or modify this Program under the terms of
4  * the GNU General Public License version 2 as published by the Free
5  * Software Foundation.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * version 2 along with this program; if not, write to the Free Software
14  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15  * 02110-1301, USA.
16  */
17 
18 /**
19  *  \file
20  *
21  *  \author Victor Julien <victor@inliniac.net>
22  *
23  *  Storage API
24  */
25 
26 #include "suricata-common.h"
27 #include "util-unittest.h"
28 #include "util-storage.h"
29 
30 typedef struct StorageMapping_ {
31     const char *name;
32     StorageEnum type; // host, flow, tx, stream, ssn, etc
33     unsigned int size;
34     void *(*Alloc)(unsigned int);
35     void (*Free)(void *);
36 } StorageMapping;
37 
38 /** \brief list of StorageMapping used at registration time */
39 typedef struct StorageList_ {
40     StorageMapping map;
41     int id;
42     struct StorageList_ *next;
43 } StorageList;
44 
45 static StorageList *storage_list = NULL;
46 static int storage_max_id[STORAGE_MAX];
47 static int storage_registraton_closed = 0;
48 static StorageMapping **storage_map = NULL;
49 
StoragePrintType(StorageEnum type)50 static const char *StoragePrintType(StorageEnum type)
51 {
52     switch(type) {
53         case STORAGE_HOST:
54             return "host";
55         case STORAGE_FLOW:
56             return "flow";
57         case STORAGE_IPPAIR:
58             return "ippair";
59         case STORAGE_DEVICE:
60             return "livedevice";
61         case STORAGE_MAX:
62             return "max";
63     }
64     return "invalid";
65 }
66 
StorageInit(void)67 void StorageInit(void)
68 {
69     memset(&storage_max_id, 0x00, sizeof(storage_max_id));
70     storage_list = NULL;
71     storage_map = NULL;
72     storage_registraton_closed = 0;
73 }
74 
StorageCleanup(void)75 void StorageCleanup(void)
76 {
77     if (storage_map) {
78         int i;
79         for (i = 0; i < STORAGE_MAX; i++) {
80             if (storage_map[i] != NULL) {
81                 SCFree(storage_map[i]);
82                 storage_map[i] = NULL;
83             }
84         }
85         SCFree(storage_map);
86         storage_map = NULL;
87     }
88 
89     StorageList *entry = storage_list;
90     while (entry) {
91         StorageList *next = entry->next;
92         SCFree(entry);
93         entry = next;
94     }
95 
96     storage_list = NULL;
97 }
98 
StorageRegister(const StorageEnum type,const char * name,const unsigned int size,void * (* Alloc)(unsigned int),void (* Free)(void *))99 int StorageRegister(const StorageEnum type, const char *name, const unsigned int size, void *(*Alloc)(unsigned int), void (*Free)(void *))
100 {
101     if (storage_registraton_closed)
102         return -1;
103 
104     if (type >= STORAGE_MAX || name == NULL || strlen(name) == 0 ||
105             size == 0 || (size != sizeof(void *) && Alloc == NULL) || Free == NULL)
106         return -1;
107 
108     StorageList *list = storage_list;
109     while (list) {
110         if (strcmp(name, list->map.name) == 0 && type == list->map.type) {
111             SCLogError(SC_ERR_INVALID_VALUE, "storage for type \"%s\" with "
112                 "name \"%s\" already registered", StoragePrintType(type),
113                 name);
114             return -1;
115         }
116 
117         list = list->next;
118     }
119 
120     StorageList *entry = SCMalloc(sizeof(StorageList));
121     if (unlikely(entry == NULL))
122         return -1;
123 
124     memset(entry, 0x00, sizeof(StorageList));
125 
126     entry->map.type = type;
127     entry->map.name = name;
128     entry->map.size = size;
129     entry->map.Alloc = Alloc;
130     entry->map.Free = Free;
131 
132     entry->id = storage_max_id[type]++;
133     entry->next = storage_list;
134     storage_list = entry;
135 
136     return entry->id;
137 }
138 
StorageFinalize(void)139 int StorageFinalize(void)
140 {
141     int count = 0;
142     int i;
143 
144     storage_registraton_closed = 1;
145 
146     for (i = 0; i < STORAGE_MAX; i++) {
147         if (storage_max_id[i] > 0)
148             count++;
149     }
150     if (count == 0)
151         return 0;
152 
153     storage_map = SCMalloc(sizeof(StorageMapping *) * STORAGE_MAX);
154     if (unlikely(storage_map == NULL)) {
155         return -1;
156     }
157     memset(storage_map, 0x00, sizeof(StorageMapping *) * STORAGE_MAX);
158 
159     for (i = 0; i < STORAGE_MAX; i++) {
160         if (storage_max_id[i] > 0) {
161             storage_map[i] = SCMalloc(sizeof(StorageMapping) * storage_max_id[i]);
162             if (storage_map[i] == NULL)
163                 return -1;
164             memset(storage_map[i], 0x00, sizeof(StorageMapping) * storage_max_id[i]);
165         }
166     }
167 
168     StorageList *entry = storage_list;
169     while (entry) {
170         if (storage_map[entry->map.type] != NULL) {
171             storage_map[entry->map.type][entry->id].name = entry->map.name;
172             storage_map[entry->map.type][entry->id].type = entry->map.type;
173             storage_map[entry->map.type][entry->id].size = entry->map.size;
174             storage_map[entry->map.type][entry->id].Alloc = entry->map.Alloc;
175             storage_map[entry->map.type][entry->id].Free = entry->map.Free;
176         }
177 
178         StorageList *next = entry->next;
179         SCFree(entry);
180         entry = next;
181     };
182     storage_list = NULL;
183 
184 #ifdef DEBUG
185     for (i = 0; i < STORAGE_MAX; i++) {
186         if (storage_map[i] == NULL)
187             continue;
188 
189         int j;
190         for (j = 0; j < storage_max_id[i]; j++) {
191             StorageMapping *m = &storage_map[i][j];
192             SCLogDebug("type \"%s\" name \"%s\" size \"%"PRIuMAX"\"",
193                     StoragePrintType(m->type), m->name, (uintmax_t)m->size);
194         }
195     }
196 #endif
197     return 0;
198 }
199 
StorageGetCnt(StorageEnum type)200 unsigned int StorageGetCnt(StorageEnum type)
201 {
202     return storage_max_id[type];
203 }
204 
205 /** \brief get the size of the void array used to store
206  *         the pointers
207  *  \retval size size in bytes, can return 0 if not storage is needed
208  *
209  *  \todo we could return -1 when registration isn't closed yet, however
210  *        this will break lots of tests currently, so not doing it now */
StorageGetSize(StorageEnum type)211 unsigned int StorageGetSize(StorageEnum type)
212 {
213     return storage_max_id[type] * sizeof(void *);
214 }
215 
StorageGetById(const Storage * storage,const StorageEnum type,const int id)216 void *StorageGetById(const Storage *storage, const StorageEnum type, const int id)
217 {
218 #ifdef DEBUG
219     BUG_ON(!storage_registraton_closed);
220 #endif
221     SCLogDebug("storage %p id %d", storage, id);
222     if (storage == NULL)
223         return NULL;
224     return storage[id];
225 }
226 
StorageSetById(Storage * storage,const StorageEnum type,const int id,void * ptr)227 int StorageSetById(Storage *storage, const StorageEnum type, const int id, void *ptr)
228 {
229 #ifdef DEBUG
230     BUG_ON(!storage_registraton_closed);
231 #endif
232     SCLogDebug("storage %p id %d", storage, id);
233     if (storage == NULL)
234         return -1;
235     storage[id] = ptr;
236     return 0;
237 }
238 
StorageAllocByIdPrealloc(Storage * storage,StorageEnum type,int id)239 void *StorageAllocByIdPrealloc(Storage *storage, StorageEnum type, int id)
240 {
241 #ifdef DEBUG
242     BUG_ON(!storage_registraton_closed);
243 #endif
244     SCLogDebug("storage %p id %d", storage, id);
245 
246     StorageMapping *map = &storage_map[type][id];
247     if (storage[id] == NULL && map->Alloc != NULL) {
248         storage[id] = map->Alloc(map->size);
249         if (storage[id] == NULL) {
250             return NULL;
251         }
252     }
253 
254     return storage[id];
255 }
256 
StorageAllocById(Storage ** storage,StorageEnum type,int id)257 void *StorageAllocById(Storage **storage, StorageEnum type, int id)
258 {
259 #ifdef DEBUG
260     BUG_ON(!storage_registraton_closed);
261 #endif
262     SCLogDebug("storage %p id %d", storage, id);
263 
264     StorageMapping *map = &storage_map[type][id];
265     Storage *store = *storage;
266     if (store == NULL) {
267         // coverity[suspicious_sizeof : FALSE]
268         store = SCMalloc(sizeof(void *) * storage_max_id[type]);
269         if (unlikely(store == NULL))
270         return NULL;
271         memset(store, 0x00, sizeof(void *) * storage_max_id[type]);
272     }
273     SCLogDebug("store %p", store);
274 
275     if (store[id] == NULL && map->Alloc != NULL) {
276         store[id] = map->Alloc(map->size);
277         if (store[id] == NULL) {
278             SCFree(store);
279             *storage = NULL;
280             return NULL;
281         }
282     }
283 
284     *storage = store;
285     return store[id];
286 }
287 
StorageFreeById(Storage * storage,StorageEnum type,int id)288 void StorageFreeById(Storage *storage, StorageEnum type, int id)
289 {
290 #ifdef DEBUG
291     BUG_ON(!storage_registraton_closed);
292 #endif
293 #ifdef UNITTESTS
294     if (storage_map == NULL)
295         return;
296 #endif
297     SCLogDebug("storage %p id %d", storage, id);
298 
299     Storage *store = storage;
300     if (store != NULL) {
301         SCLogDebug("store %p", store);
302         if (store[id] != NULL) {
303             StorageMapping *map = &storage_map[type][id];
304             map->Free(store[id]);
305             store[id] = NULL;
306         }
307     }
308 }
309 
StorageFreeAll(Storage * storage,StorageEnum type)310 void StorageFreeAll(Storage *storage, StorageEnum type)
311 {
312     if (storage == NULL)
313         return;
314 #ifdef DEBUG
315     BUG_ON(!storage_registraton_closed);
316 #endif
317 #ifdef UNITTESTS
318     if (storage_map == NULL)
319         return;
320 #endif
321 
322     Storage *store = storage;
323     int i;
324     for (i = 0; i < storage_max_id[type]; i++) {
325         if (store[i] != NULL) {
326             StorageMapping *map = &storage_map[type][i];
327             map->Free(store[i]);
328             store[i] = NULL;
329         }
330     }
331 }
332 
StorageFree(Storage ** storage,StorageEnum type)333 void StorageFree(Storage **storage, StorageEnum type)
334 {
335     if (*storage == NULL)
336         return;
337 
338 #ifdef DEBUG
339     BUG_ON(!storage_registraton_closed);
340 #endif
341 #ifdef UNITTESTS
342     if (storage_map == NULL)
343         return;
344 #endif
345 
346     Storage *store = *storage;
347     int i;
348     for (i = 0; i < storage_max_id[type]; i++) {
349         if (store[i] != NULL) {
350             StorageMapping *map = &storage_map[type][i];
351             map->Free(store[i]);
352             store[i] = NULL;
353         }
354     }
355     SCFree(*storage);
356     *storage = NULL;
357 }
358 
359 #ifdef UNITTESTS
360 
StorageTestAlloc(unsigned int size)361 static void *StorageTestAlloc(unsigned int size)
362 {
363     void *x = SCMalloc(size);
364     return x;
365 }
StorageTestFree(void * x)366 static void StorageTestFree(void *x)
367 {
368     if (x)
369         SCFree(x);
370 }
371 
StorageTest01(void)372 static int StorageTest01(void)
373 {
374     StorageInit();
375 
376     int id = StorageRegister(STORAGE_HOST, "test", 8, StorageTestAlloc, StorageTestFree);
377     if (id < 0)
378         goto error;
379     id = StorageRegister(STORAGE_HOST, "variable", 24, StorageTestAlloc, StorageTestFree);
380     if (id < 0)
381         goto error;
382     id = StorageRegister(STORAGE_FLOW, "store", sizeof(void *), StorageTestAlloc, StorageTestFree);
383     if (id < 0)
384         goto error;
385 
386     if (StorageFinalize() < 0)
387         goto error;
388 
389     StorageCleanup();
390     return 1;
391 error:
392     StorageCleanup();
393     return 0;
394 }
395 
396 struct StorageTest02Data {
397     int abc;
398 };
399 
StorageTest02Init(unsigned int size)400 static void *StorageTest02Init(unsigned int size)
401 {
402     struct StorageTest02Data *data = (struct StorageTest02Data *)SCMalloc(size);
403     if (data != NULL)
404         data->abc = 1234;
405     return (void *)data;
406 }
407 
StorageTest02(void)408 static int StorageTest02(void)
409 {
410     struct StorageTest02Data *test = NULL;
411 
412     StorageInit();
413 
414     int id1 = StorageRegister(STORAGE_HOST, "test", 4, StorageTest02Init, StorageTestFree);
415     if (id1 < 0) {
416         printf("StorageRegister failed (2): ");
417         goto error;
418     }
419     int id2 = StorageRegister(STORAGE_HOST, "test2", 4, StorageTest02Init, StorageTestFree);
420     if (id2 < 0) {
421         printf("StorageRegister failed (2): ");
422         goto error;
423     }
424 
425     if (StorageFinalize() < 0) {
426         printf("StorageFinalize failed: ");
427         goto error;
428     }
429 
430     Storage *storage = NULL;
431     void *data = StorageAllocById(&storage, STORAGE_HOST, id1);
432     if (data == NULL) {
433         printf("StorageAllocById failed, data == NULL, storage %p: ", storage);
434         goto error;
435     }
436     test = (struct StorageTest02Data *)data;
437     if (test->abc != 1234) {
438         printf("setup failed, test->abc != 1234, but %d (1):", test->abc);
439         goto error;
440     }
441     test->abc = 4321;
442 
443     data = StorageAllocById(&storage, STORAGE_HOST, id2);
444     if (data == NULL) {
445         printf("StorageAllocById failed, data == NULL, storage %p: ", storage);
446         goto error;
447     }
448     test = (struct StorageTest02Data *)data;
449     if (test->abc != 1234) {
450         printf("setup failed, test->abc != 1234, but %d (2):", test->abc);
451         goto error;
452     }
453 
454     data = StorageGetById(storage, STORAGE_HOST, id1);
455     if (data == NULL) {
456         printf("StorageAllocById failed, data == NULL, storage %p: ", storage);
457         goto error;
458     }
459     test = (struct StorageTest02Data *)data;
460     if (test->abc != 4321) {
461         printf("setup failed, test->abc != 4321, but %d (3):", test->abc);
462         goto error;
463     }
464 
465     //StorageFreeById(storage, STORAGE_HOST, id1);
466     //StorageFreeById(storage, STORAGE_HOST, id2);
467 
468     StorageFree(&storage, STORAGE_HOST);
469 
470     StorageCleanup();
471     return 1;
472 error:
473     StorageCleanup();
474     return 0;
475 }
476 
StorageTest03(void)477 static int StorageTest03(void)
478 {
479     StorageInit();
480 
481     int id = StorageRegister(STORAGE_HOST, "test", 8, StorageTestAlloc, StorageTestFree);
482     if (id < 0)
483         goto error;
484     id = StorageRegister(STORAGE_HOST, "test", 8, StorageTestAlloc, StorageTestFree);
485     if (id != -1) {
486         printf("duplicate registration should have failed: ");
487         goto error;
488     }
489 
490     id = StorageRegister(STORAGE_HOST, "test1", 6, NULL, StorageTestFree);
491     if (id != -1) {
492         printf("duplicate registration should have failed (2): ");
493         goto error;
494     }
495 
496     id = StorageRegister(STORAGE_HOST, "test2", 8, StorageTestAlloc, NULL);
497     if (id != -1) {
498         printf("duplicate registration should have failed (3): ");
499         goto error;
500     }
501 
502     id = StorageRegister(STORAGE_HOST, "test3", 0, StorageTestAlloc, StorageTestFree);
503     if (id != -1) {
504         printf("duplicate registration should have failed (4): ");
505         goto error;
506     }
507 
508     id = StorageRegister(STORAGE_HOST, "", 8, StorageTestAlloc, StorageTestFree);
509     if (id != -1) {
510         printf("duplicate registration should have failed (5): ");
511         goto error;
512     }
513 
514     id = StorageRegister(STORAGE_HOST, NULL, 8, StorageTestAlloc, StorageTestFree);
515     if (id != -1) {
516         printf("duplicate registration should have failed (6): ");
517         goto error;
518     }
519 
520     id = StorageRegister(STORAGE_MAX, "test4", 8, StorageTestAlloc, StorageTestFree);
521     if (id != -1) {
522         printf("duplicate registration should have failed (7): ");
523         goto error;
524     }
525 
526     id = StorageRegister(38, "test5", 8, StorageTestAlloc, StorageTestFree);
527     if (id != -1) {
528         printf("duplicate registration should have failed (8): ");
529         goto error;
530     }
531 
532     id = StorageRegister(-1, "test6", 8, StorageTestAlloc, StorageTestFree);
533     if (id != -1) {
534         printf("duplicate registration should have failed (9): ");
535         goto error;
536     }
537 
538     StorageCleanup();
539     return 1;
540 error:
541     StorageCleanup();
542     return 0;
543 }
544 
StorageRegisterTests(void)545 void StorageRegisterTests(void)
546 {
547     UtRegisterTest("StorageTest01", StorageTest01);
548     UtRegisterTest("StorageTest02", StorageTest02);
549     UtRegisterTest("StorageTest03", StorageTest03);
550 }
551 #endif
552