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