1 /****************************************************************************
2 *
3 * Copyright (C) 2014-2021 Cisco and/or its affiliates. All rights reserved.
4 * Copyright (C) 2003-2013 Sourcefire, Inc.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License Version 2 as
8 * published by the Free Software Foundation. You may not use, modify or
9 * distribute this program under any other version of the GNU General
10 * Public License.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 ****************************************************************************/
22
23 /*
24 *
25 * sfghash.c
26 *
27 * Generic hash table library.
28 *
29 * This hash table maps unique keys to void data pointers.
30 *
31 * Features:
32 * 1) Keys may be ascii strings of variable size, or
33 * fixed length (per table) binary byte sequences. This
34 * allows use as a Mapping for String+Data pairs, or a
35 * generic hashing.
36 * 2) User can allocate keys, or pass copies and we can
37 * allocate space and save keys.
38 * 3) User can pass a free function to free up user data
39 * when the table is deleted.
40 * 4) Table rows sizes can be automatically adjusted to
41 * the nearest prime number size.
42 *
43 * 6/10/03 - man - Upgraded the hash function to a Hardened hash function,
44 * it has no predictable cycles, and each hash table gets a different
45 * randomized hashing function. So even with the source code, you cannot predict
46 * anything with this function. If an attacker can setup a feedback
47 * loop he might gain some knowledge of how to muck with us, but even in that case
48 * his odds are astronomically skinny. This is actually the same problem as solved
49 * early on with hashing functions where degenerate data with close keys could
50 * produce very long bucket chains.
51 *
52 * 8/31/06 - man - Added prime tables to speed up prime number lookup.
53 *
54 * Author: Marc Norton
55 *
56 */
57 #include <stdio.h>
58 #include <stdlib.h>
59 #include <string.h>
60 #include <time.h>
61
62 #ifdef HAVE_CONFIG_H
63 #include "config.h"
64 #endif
65
66 #include "sfghash.h"
67
68 #include "sfprimetable.h"
69 /*
70 * Private Malloc
71 */
72 static
s_alloc(int n)73 void * s_alloc( int n )
74 {
75 return calloc( 1,n );
76 }
77
78 /*
79 * Private Free
80 */
81 static
s_free(void * p)82 void s_free( void * p )
83 {
84 if( p )free( p );
85 }
86
87 /*
88 *
89 * Create a new hash table
90 *
91 * nrows : number of rows in hash table, primes are best.
92 * > 0 => we use the nearest prime internally
93 * < 0 => we use the magnitude as nrows.
94 * keysize : > 0 => bytes in each key, keys are binary bytes,
95 * all keys are the same size.
96 * ==0 => keys are strings and are null terminated,
97 * allowing random key lengths.
98 * userkeys : > 0 => indicates user owns the key data
99 * and we should not allocate or free space for it,
100 * nor should we attempt to free the user key. We just
101 * save the pointer to the key.
102 * ==0 => we should copy the keys and manage them internally
103 * userfree : routine to free users data, null if we should not
104 * free user data in sfghash_delete(). The routine
105 * should be of the form 'void userfree(void * userdata)',
106 * 'free' works for simple allocations.
107 */
sfghash_new(int nrows,int keysize,int userkeys,void (* userfree)(void * p))108 SFGHASH * sfghash_new( int nrows, int keysize, int userkeys, void (*userfree)(void*p) )
109 {
110 int i;
111 SFGHASH * h;
112
113 if( nrows > 0 ) /* make sure we have a prime number */
114 {
115 nrows = sf_nearest_prime( nrows );
116 }
117 else /* use the magnitude or nrows as is */
118 {
119 nrows = -nrows;
120 }
121
122 h = (SFGHASH*)s_alloc( sizeof(SFGHASH) );
123 if( !h ) return 0;
124
125 memset( h, 0, sizeof(SFGHASH) );
126
127 h->sfhashfcn = sfhashfcn_new( nrows );
128 if( !h->sfhashfcn )
129 {
130 free(h);
131 return 0;
132 }
133
134 h->table = (SFGHASH_NODE**) s_alloc( sizeof(SFGHASH_NODE*) * nrows );
135 if( !h->table )
136 {
137 free(h->sfhashfcn);
138 free(h);
139 return 0;
140 }
141
142 for( i=0; i<nrows; i++ )
143 {
144 h->table[i] = 0;
145 }
146
147 h->userkey = userkeys;
148
149 h->keysize = keysize;
150
151 h->nrows = nrows;
152
153 h->count = 0;
154
155 h->userfree = userfree;
156
157 h->crow = 0; // findfirst/next current row
158
159 h->cnode = 0; // findfirst/next current node ptr
160
161 return h;
162 }
163
164 /*
165 * Set Splay mode : Splays nodes to front of list on each access
166 */
sfghash_splaymode(SFGHASH * t,int n)167 void sfghash_splaymode( SFGHASH * t, int n )
168 {
169 if ( t )
170 t->splay = n;
171 }
172
173 /*
174 * Delete the hash Table
175 *
176 * free key's, free node's, and free the users data, if they
177 * supply a free function
178 */
sfghash_delete(SFGHASH * h)179 void sfghash_delete( SFGHASH * h )
180 {
181 int i;
182 SFGHASH_NODE * node, * onode;
183
184 if( !h ) return;
185
186 sfhashfcn_free( h->sfhashfcn );
187
188 if( h->table )
189 {
190 for(i=0;i<h->nrows;i++)
191 {
192 for( node=h->table[i]; node; )
193 {
194 onode = node;
195 node = node->next;
196
197 if( !h->userkey && onode->key )
198 s_free( (void *)onode->key );
199
200 if( h->userfree && onode->data )
201 h->userfree( onode->data ); /* free users data, with users function */
202
203 s_free( onode );
204 }
205 }
206 s_free( h->table );
207 h->table = 0;
208 }
209
210 s_free( h );
211 }
212
213 /*
214 * Get the # of Nodes in HASH the table
215 */
sfghash_count(SFGHASH * t)216 int sfghash_count( SFGHASH * t )
217 {
218 if( !t ) return 0;
219 return t->count;
220 }
221
222
223
224
225 /*
226 * Add a key + data pair
227 * ---------------------
228 *
229 * key + data should both be non-zero, although data can be zero
230 *
231 * t - hash table
232 * key - users key data (should be unique in this table)
233 * may be ascii strings or fixed size binary keys
234 * data - users data pointer
235 *
236 * returns SF_HASH_NOMEM: alloc error
237 * SF_HASH_INTABLE : key already in table (t->cnode points to the node)
238 * SF_OK: added a node for this key + data pair
239 *
240 * Notes:
241 * If the key node already exists, then t->cnode points to it on return,
242 * this allows you to do something with the node - like add the data to a
243 * linked list of data items held by the node, or track a counter, or whatever.
244 *
245 */
sfghash_add(SFGHASH * t,const void * const key,void * const data)246 int sfghash_add( SFGHASH * t, const void * const key, void * const data )
247 {
248 unsigned hashkey;
249 int klen;
250 int index;
251 SFGHASH_NODE *hnode;
252
253 if (t == NULL)
254 return SFGHASH_ERR;
255
256 /*
257 * Get proper Key Size
258 */
259 if( t->keysize > 0 )
260 {
261 klen = t->keysize;
262 }
263 else
264 {
265 /* need the null byte for strcmp() in sfghash_find() */
266 klen = strlen( (char*)key ) + 1;
267 }
268
269 hashkey = t->sfhashfcn->hash_fcn( t->sfhashfcn, (unsigned char*) key, klen );
270
271 index = hashkey % t->nrows;
272
273 /*
274 * Uniqueness:
275 * Check 1st to see if the key is already in the table
276 * Just bail if it is.
277 */
278 for( hnode=t->table[index]; hnode; hnode=hnode->next )
279 {
280 if( t->keysize > 0 )
281 {
282 if( !t->sfhashfcn->keycmp_fcn(hnode->key,key,klen) )
283 {
284 t->cnode = hnode; /* save pointer to the node */
285 return SFGHASH_INTABLE; /* found it */
286 }
287 }
288 else
289 {
290 if( !strcmp((const char *)hnode->key,(const char*)key) )
291 {
292 t->cnode = hnode; /* save pointer to the node */
293 return SFGHASH_INTABLE; /* found it */
294 }
295 }
296 }
297
298 /*
299 * Create new node
300 */
301 hnode = (SFGHASH_NODE*)s_alloc(sizeof(SFGHASH_NODE));
302 if( !hnode )
303 return SFGHASH_NOMEM;
304
305 /* Add the Key */
306 if( t->userkey )
307 {
308 /* Use the Users key */
309 hnode->key = key;
310 }
311 else
312 {
313 /* Create new key */
314 hnode->key = s_alloc( klen );
315 if( !hnode->key )
316 {
317 free(hnode);
318 return SFGHASH_NOMEM;
319 }
320
321 /* Copy key */
322 memcpy((void*)hnode->key,key,klen);
323 }
324
325 /* Add The Node */
326 if( t->table[index] ) /* add the node to the existing list */
327 {
328 hnode->prev = 0; // insert node as head node
329 hnode->next=t->table[index];
330 hnode->data=data;
331 t->table[index]->prev = hnode;
332 t->table[index] = hnode;
333 }
334 else /* 1st node in this list */
335 {
336 hnode->prev=0;
337 hnode->next=0;
338 hnode->data=data;
339 t->table[index] = hnode;
340 }
341
342 t->count++;
343
344 return SFGHASH_OK;
345 }
346
347 /*
348 * move a node to the front of the list
349 */
movetofront(SFGHASH * t,int index,SFGHASH_NODE * n)350 static void movetofront( SFGHASH *t , int index, SFGHASH_NODE * n )
351 {
352 if( t->table[index] != n ) // if not at front of list already...
353 {
354 /* Unlink the node */
355 if( n->prev ) n->prev->next = n->next;
356 if( n->next ) n->next->prev = n->prev;
357
358 /* Link at front of list */
359 n->prev=0;
360 n->next=t->table[index];
361 t->table[index]->prev=n;
362 }
363 }
364
365 /*
366 * Find a Node based on the key, return users data.
367 */
sfghash_find_node(SFGHASH * t,const void * const key)368 SFGHASH_NODE * sfghash_find_node( SFGHASH * t, const void * const key)
369 {
370 unsigned hashkey;
371 int index, klen;
372 SFGHASH_NODE *hnode;
373
374 if ( !t ) return NULL;
375 if( t->keysize )
376 {
377 klen = t->keysize;
378 }
379 else
380 {
381 klen = strlen( (char*) key ) + 1;
382 }
383
384 hashkey = t->sfhashfcn->hash_fcn( t->sfhashfcn, (unsigned char*) key, klen );
385
386 index = hashkey % t->nrows;
387
388 for( hnode=t->table[index]; hnode; hnode=hnode->next )
389 {
390 if( t->keysize == 0 )
391 {
392 if( !strcmp((char*)hnode->key,(char*)key) )
393 {
394 if( t->splay > 0 )
395 movetofront(t,index,hnode);
396
397 return hnode;
398 }
399 }
400 else
401 {
402 if( !t->sfhashfcn->keycmp_fcn(hnode->key,key,t->keysize) )
403 {
404 if( t->splay > 0 )
405 movetofront(t,index,hnode);
406
407 return hnode;
408 }
409 }
410 }
411
412 return NULL;
413 }
414
415 /*
416 * Find a Node based on the key, return users data.
417 */
sfghash_find(SFGHASH * t,const void * const key)418 void * sfghash_find( SFGHASH * t, const void * const key)
419 {
420 SFGHASH_NODE * hnode;
421 if ( !t ) return NULL;
422
423 hnode = sfghash_find_node( t, key );
424
425 if( hnode ) return hnode->data;
426
427 return NULL;
428 }
429
430 /* Returns whether or not the there is an entry in the table with key
431 * Sets argument data to data in hash node which could be NULL.
432 * This function is used to both make sure there is an entry in the
433 * table and get potential data associated with entry */
sfghash_find2(SFGHASH * t,void * key,void ** data)434 int sfghash_find2(SFGHASH *t, void *key, void **data)
435 {
436 SFGHASH_NODE * hnode;
437
438 if (t == NULL)
439 return 0;
440
441 hnode = sfghash_find_node(t, key);
442
443 if (hnode != NULL)
444 {
445 *data = hnode->data;
446 return 1;
447 }
448
449 return 0;
450 }
451
452 /*
453 * Unlink and free the node
454 */
sfghash_free_node(SFGHASH * t,unsigned index,SFGHASH_NODE * hnode)455 static int sfghash_free_node( SFGHASH * t, unsigned index, SFGHASH_NODE * hnode )
456 {
457 if( !t->userkey && hnode->key )
458 s_free( (void *)hnode->key );
459 hnode->key = 0;
460
461 if( t->userfree)
462 t->userfree( hnode->data); /* free users data, with users function */
463
464 if( hnode->prev ) // not the 1st node
465 {
466 hnode->prev->next = hnode->next;
467 if( hnode->next ) hnode->next->prev = hnode->prev;
468 }
469 else if( t->table[index] ) // 1st node
470 {
471 t->table[index] = t->table[index]->next;
472 if( t->table[index] )t->table[index]->prev = 0;
473 }
474
475 s_free( hnode );
476
477 t->count--;
478
479 return SFGHASH_OK;
480 }
481
482 /*
483 * Remove a Key/Data Pair from the table - find it, unlink it, and free the memory for it.
484 *
485 * returns : 0 - OK
486 * -1 - node not found
487 */
sfghash_remove(SFGHASH * t,const void * const key)488 int sfghash_remove( SFGHASH * t, const void * const key)
489 {
490 SFGHASH_NODE * hnode;
491 int klen;
492 unsigned hashkey, index;
493
494 if ( !t ) return 0;
495
496 if( t->keysize > 0 )
497 {
498 klen = t->keysize;
499 }
500 else
501 {
502 klen = strlen((char*)key) + 1;
503 }
504
505 hashkey = t->sfhashfcn->hash_fcn( t->sfhashfcn, (unsigned char*) key, klen );
506
507 index = hashkey % t->nrows;
508
509 for( hnode=t->table[index]; hnode; hnode=hnode->next )
510 {
511 if( t->keysize > 0 )
512 {
513 if( !t->sfhashfcn->keycmp_fcn(hnode->key,key,klen) )
514 {
515 return sfghash_free_node( t, index, hnode );
516 }
517 }
518 else
519 {
520 if( !strcmp((const char *)hnode->key,(const char*)key) )
521 {
522 return sfghash_free_node( t, index, hnode );
523 }
524 }
525 }
526
527 return SFGHASH_ERR;
528 }
529
530 /*
531 * Get First Hash Table Node
532 */
sfghash_findfirst1(SFGHASH * t)533 SFGHASH_NODE * sfghash_findfirst1( SFGHASH * t )
534 {
535 if ( !t ) return NULL;
536 /* Start with 1st row */
537 for( t->crow=0; t->crow < t->nrows; t->crow++ )
538 {
539 /* Get 1st Non-Null node in row list */
540 t->cnode = t->table[t->crow];
541
542 if( t->cnode ) return t->cnode;
543 }
544 return NULL;
545 }
546
547 /*
548 * Get Next Hash Table Node
549 */
sfghash_findnext1(SFGHASH * t)550 SFGHASH_NODE * sfghash_findnext1( SFGHASH * t )
551 {
552 if ( !t ) return NULL;
553 if( t->cnode ) /* get next in this list */
554 {
555 /* Next node in current node list */
556 t->cnode = t->cnode->next;
557 if( t->cnode )
558 {
559 return t->cnode;
560 }
561 }
562
563 /* Get 1st node in next non-empty row/node list */
564 for( t->crow++; t->crow < t->nrows; t->crow++ )
565 {
566 t->cnode = t->table[ t->crow ];
567 if( t->cnode )
568 {
569 return t->cnode;
570 }
571 }
572
573 return NULL;
574 }
575
576 /* Internal use only */
sfghash_next(SFGHASH * t)577 static void sfghash_next( SFGHASH * t )
578 {
579 if ( !t ) return;
580 if( !t->cnode )
581 return ;
582
583 /* Next node in current node list */
584 t->cnode = t->cnode->next;
585 if( t->cnode )
586 {
587 return;
588 }
589
590 /* Next row */
591 /* Get 1st node in next non-emtoy row/node list */
592 for( t->crow++; t->crow < t->nrows; t->crow++ )
593 {
594 t->cnode = t->table[ t->crow ];
595 if( t->cnode )
596 {
597 return;
598 }
599 }
600 }
601 /*
602 * Get First Hash Table Node
603 */
sfghash_findfirst(SFGHASH * t)604 SFGHASH_NODE * sfghash_findfirst( SFGHASH * t )
605 {
606 SFGHASH_NODE * n;
607
608 if ( !t ) return NULL;
609
610 /* Start with 1st row */
611 for( t->crow=0; t->crow < t->nrows; t->crow++ )
612 {
613 /* Get 1st Non-Null node in row list */
614 t->cnode = t->table[ t->crow ];
615
616 if( t->cnode )
617 {
618 n = t->cnode;
619
620 sfghash_next( t ); // load t->cnode with the next entry
621
622 return n;
623 }
624 }
625 return NULL;
626 }
627
628 /*
629 * Get Next Hash Table Node
630 */
sfghash_findnext(SFGHASH * t)631 SFGHASH_NODE * sfghash_findnext( SFGHASH * t )
632 {
633 SFGHASH_NODE * n;
634
635 if ( !t ) return NULL;
636
637 n = t->cnode;
638
639 if( !n ) /* Done, no more entries */
640 {
641 return NULL;
642 }
643
644 /*
645 Preload next node into current node
646 */
647 sfghash_next( t );
648
649 return n;
650 }
651
652
653 /**
654 * Make sfhashfcn use a separate set of operators for the backend.
655 *
656 * @param h sfhashfcn ptr
657 * @param hash_fcn user specified hash function
658 * @param keycmp_fcn user specified key comparisoin function
659 */
660
sfghash_set_keyops(SFGHASH * h,unsigned (* hash_fcn)(SFHASHFCN * p,unsigned char * d,int n),int (* keycmp_fcn)(const void * s1,const void * s2,size_t n))661 int sfghash_set_keyops( SFGHASH *h ,
662 unsigned (*hash_fcn)( SFHASHFCN * p,
663 unsigned char *d,
664 int n),
665 int (*keycmp_fcn)( const void *s1,
666 const void *s2,
667 size_t n))
668 {
669 if(h && hash_fcn && keycmp_fcn)
670 {
671 return sfhashfcn_set_keyops(h->sfhashfcn, hash_fcn, keycmp_fcn);
672 }
673
674 return -1;
675 }
676
677
678 /*
679 *
680 * Test Driver for Hashing
681 *
682 */
683
684 #ifdef SFGHASH_MAIN
685
myfree(void * p)686 void myfree ( void * p )
687 {
688 printf("freeing '%s'\n",p);
689 free(p);
690 }
691
692 /*
693 * Hash test program
694 */
main(int argc,char ** argv)695 int main ( int argc, char ** argv )
696 {
697 int i;
698 SFGHASH * t;
699 SFGHASH_NODE * n, *m;
700 char str[256],*p;
701 int num=100;
702
703 if( argc > 1 )
704 num = atoi(argv[1]);
705
706 sfatom_init();
707
708 /* Create a Hash Table */
709 t = sfghash_new( 1000, 0 , GH_COPYKEYS , myfree );
710
711 /* Add Nodes to the Hash Table */
712 for(i=0;i<num;i++)
713 {
714 snprintf(str, sizeof(str), "KeyWord%d",i+1);
715 str[sizeof(str) - 1] = '\0';
716 sfghash_add( t, str, strupr(strdup(str)) );
717
718 sfatom_add( str, strupr(strdup(str)) );
719 }
720
721 /* Find and Display Nodes in the Hash Table */
722 printf("\n** FIND KEY TEST\n");
723
724 for(i=0;i<num;i++)
725 {
726 snprintf(str, sizeof(str), "KeyWord%d",i+1);
727 str[sizeof(str) - 1] = '\0';
728
729 p = (char*) sfghash_find( t, str );
730
731 printf("Hash-key=%*s, data=%*s\n", strlen(str),str, strlen(str), p );
732
733 p = (char*) sfatom_find( str );
734
735 printf("Atom-key=%*s, data=%*s\n", strlen(str),str, strlen(str), p );
736 }
737
738 /* Display All Nodes in the Hash Table */
739 printf("\n** FINDFIRST / FINDNEXT TEST\n");
740
741 for( n = sfghash_findfirst(t); n; n = sfghash_findnext(t) )
742 {
743 printf("hash-findfirst/next: key=%s, data=%s\n", n->key, n->data );
744
745 // hashing code frees user data using 'myfree' above ....
746 if( sfghash_remove(t,n->key) )
747 printf("Could not remove the key node\n");
748 else
749 printf("key node removed\n");
750 }
751
752 for( n = sfatom_findfirst(); n; n = sfatom_findnext() )
753 {
754 printf("atom-findfirst/next: key=%s, data=%s\n", n->key, n->data );
755
756 free( n->data ); //since atom data is not freed automatically
757 }
758
759 /* Free the table and it's user data */
760 printf("****sfghash_delete\n");
761 sfghash_delete( t );
762
763 printf("****sfatom_reset\n");
764 sfatom_reset();
765
766 printf("\nnormal pgm finish\n\n");
767
768 return 0;
769 }
770
771
772
773 #endif
774
775
776