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