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