1 /*
2  * ProFTPD - FTP server daemon
3  * Copyright (c) 2004-2017 The ProFTPD Project team
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA.
18  *
19  * As a special exemption, The ProFTPD Project team and other respective
20  * copyright holders give permission to link this program with OpenSSL, and
21  * distribute the resulting executable, without including the source code for
22  * OpenSSL in the source distribution.
23  */
24 
25 /* Table API implementation */
26 
27 #include "conf.h"
28 
29 #ifdef PR_USE_OPENSSL
30 #include <openssl/rand.h>
31 #endif /* PR_USE_OPENSSL */
32 
33 #define PR_TABLE_DEFAULT_NCHAINS	256
34 #define PR_TABLE_DEFAULT_MAX_ENTS	8192
35 #define PR_TABLE_ENT_POOL_SIZE		64
36 
37 struct table_rec {
38   pool *pool;
39   unsigned long flags;
40 
41   /* These bytes are randomly generated at table creation time, and
42    * are used to seed the hashing function, so as to defend/migitate
43    * against attempts to feed carefully crafted keys which force the
44    * table into its worst-case performance scenario.
45    *
46    * For more information on attacks of this nature, see:
47    *
48    *   http://www.cs.rice.edu/~scrosby/hash/CrosbyWallach_UsenixSec2003/
49    */
50   unsigned int seed;
51 
52   /* Maximum number of entries that can be stored in this table.  The
53    * default maximum (PR_TABLE_DEFAULT_MAX_ENTS) is set fairly high.
54    * This limit is present in order to defend/mitigate against certain abuse
55    * scenarios.
56    *
57    * XXX Note that an additional protective measure can/might be placed on
58    * the maximum length of a given chain, to detect other types of attacks
59    * that force the table into the worse-case performance scenario (i.e.
60    * linear scanning of a long chain).  If such is added, then a Table API
61    * function should be added for returning the length of the longest chain
62    * in the table.  Such a function could be used by modules to determine
63    * if their tables are being abused (or in need of readjustment).
64    */
65   unsigned int nmaxents;
66 
67   pr_table_entry_t **chains;
68   unsigned int nchains;
69   unsigned int nents;
70 
71   /* List of free structures. */
72   pr_table_entry_t *free_ents;
73   pr_table_key_t *free_keys;
74 
75   /* For iterating over all the keys in the entire table. */
76   pr_table_entry_t *tab_iter_ent;
77 
78   /* For iterating through all of the possible multiple values for a single
79    * key.  Only used if the PR_TABLE_FL_MULTI_VALUE flag is set.
80    */
81   pr_table_entry_t *val_iter_ent;
82 
83   /* Cache of last looked-up entry.  Usage of this field can be enabled
84    * by using the PR_TABLE_FL_USE_CACHE flag.
85    */
86   pr_table_entry_t *cache_ent;
87 
88   /* Table callbacks. */
89   int (*keycmp)(const void *, size_t, const void *, size_t);
90   unsigned int (*keyhash)(const void *, size_t);
91   void (*entinsert)(pr_table_entry_t **, pr_table_entry_t *);
92   void (*entremove)(pr_table_entry_t **, pr_table_entry_t *);
93 };
94 
95 static int handling_signal = FALSE;
96 
97 static const char *trace_channel = "table";
98 
99 /* Default table callbacks
100  */
101 
key_cmp(const void * key1,size_t keysz1,const void * key2,size_t keysz2)102 static int key_cmp(const void *key1, size_t keysz1, const void *key2,
103     size_t keysz2) {
104   const char *k1, *k2;
105 
106   if (keysz1 != keysz2) {
107     return keysz1 < keysz2 ? -1 : 1;
108   }
109 
110   k1 = key1;
111   k2 = key2;
112 
113   if (keysz1 >= 1) {
114     /* Basic check of the first character in each key, trying to reduce
115      * the chances of calling strncmp(3) if not needed.
116      */
117 
118     if (k1[0] != k2[0]) {
119       return k1[0] < k2[0] ? -1 : 1;
120     }
121 
122     /* Special case (unlikely, but possible). */
123     if (keysz1 == 1) {
124       return 0;
125     }
126   }
127 
128   return strncmp((const char *) key1, (const char *) key2, keysz1);
129 }
130 
131 /* Use Perl's hashing algorithm by default.
132  *
133  * Here's a good article about this hashing algorithm, and about hashing
134  * functions in general:
135  *
136  *  http://www.perl.com/pub/2002/10/01/hashes.html
137  */
key_hash(const void * key,size_t keysz)138 static unsigned int key_hash(const void *key, size_t keysz) {
139   unsigned int i = 0;
140   size_t sz = !keysz ? strlen((const char *) key) : keysz;
141 
142   while (sz--) {
143     const char *k = key;
144     unsigned int c;
145 
146     c = k[sz];
147 
148     if (!handling_signal) {
149       /* Always handle signals in potentially long-running while loops. */
150       pr_signals_handle();
151     }
152 
153     i = (i * 33) + c;
154   }
155 
156   return i;
157 }
158 
159 /* Default insertion is simply to add the given entry to the end of the
160  * chain.
161  */
entry_insert(pr_table_entry_t ** h,pr_table_entry_t * e)162 static void entry_insert(pr_table_entry_t **h, pr_table_entry_t *e) {
163   pr_table_entry_t *ei;
164 
165   if (*h == NULL) {
166     return;
167   }
168 
169   for (ei = *h; ei != NULL && ei->next; ei = ei->next);
170 
171   /* Now, ei points to the last entry in the chain. */
172   ei->next = e;
173   e->prev = ei;
174 }
175 
176 /* Default removal is simply to remove the entry from the chain. */
entry_remove(pr_table_entry_t ** h,pr_table_entry_t * e)177 static void entry_remove(pr_table_entry_t **h, pr_table_entry_t *e) {
178 
179   if (e->next) {
180     e->next->prev = e->prev;
181   }
182 
183   if (e->prev) {
184     e->prev->next = e->next;
185 
186   } else {
187     /* This entry is the head. */
188     *h = e->next;
189   }
190 
191   e->prev = e->next = NULL;
192   return;
193 }
194 
195 /* Table key management
196  */
197 
tab_key_alloc(pr_table_t * tab)198 static pr_table_key_t *tab_key_alloc(pr_table_t *tab) {
199   pr_table_key_t *k;
200 
201   /* Try to find a free key on the free list first... */
202   if (tab->free_keys) {
203     k = tab->free_keys;
204     tab->free_keys = k->next;
205     k->next = NULL;
206 
207     return k;
208   }
209 
210   /* ...otherwise, allocate a new key. */
211   k = pcalloc(tab->pool, sizeof(pr_table_key_t));
212 
213   return k;
214 }
215 
tab_key_free(pr_table_t * tab,pr_table_key_t * k)216 static void tab_key_free(pr_table_t *tab, pr_table_key_t *k) {
217   /* Clear everything from the given key. */
218   memset(k, 0, sizeof(pr_table_key_t));
219 
220   /* Add this key to the table's free list. */
221   if (tab->free_keys) {
222     pr_table_key_t *i = tab->free_keys;
223 
224     /* Scan to the end of the list. */
225     while (i->next != NULL) {
226       if (!handling_signal) {
227         pr_signals_handle();
228       }
229 
230       i = i->next;
231     }
232 
233     i->next = k;
234 
235   } else {
236     tab->free_keys = k;
237   }
238 }
239 
240 /* Table entry management
241  */
242 
tab_entry_alloc(pr_table_t * tab)243 static pr_table_entry_t *tab_entry_alloc(pr_table_t *tab) {
244   pr_table_entry_t *e;
245 
246   /* Try to find a free entry on the free list first... */
247   if (tab->free_ents) {
248     e = tab->free_ents;
249     tab->free_ents = e->next;
250     e->next = NULL;
251 
252     return e;
253   }
254 
255   /* ...otherwise, allocate a new entry. */
256   e = pcalloc(tab->pool, sizeof(pr_table_entry_t));
257 
258   return e;
259 }
260 
tab_entry_free(pr_table_t * tab,pr_table_entry_t * e)261 static void tab_entry_free(pr_table_t *tab, pr_table_entry_t *e) {
262   /* Clear everything from the given entry. */
263   memset(e, 0, sizeof(pr_table_entry_t));
264 
265   /* Add this entry to the table's free list. */
266   if (tab->free_ents) {
267     pr_table_entry_t *i = tab->free_ents;
268 
269     /* Scan to the end of the list. */
270     while (i->next != NULL) {
271       if (!handling_signal) {
272         pr_signals_handle();
273       }
274 
275       i = i->next;
276     }
277 
278     i->next = e;
279 
280   } else {
281     tab->free_ents = e;
282   }
283 }
284 
tab_entry_insert(pr_table_t * tab,pr_table_entry_t * e)285 static void tab_entry_insert(pr_table_t *tab, pr_table_entry_t *e) {
286   pr_table_entry_t *h = tab->chains[e->idx];
287 
288   if (h &&
289       h != e) {
290 
291     /* Only insert the entry if the head we found is different from the
292      * the given entry.  There is an edge case when the entry being added
293      * is the head of a new chain.
294      */
295     tab->entinsert(&h, e);
296     tab->chains[e->idx] = h;
297   }
298 
299   e->key->nents++;
300   tab->nents++;
301 }
302 
tab_entry_next(pr_table_t * tab)303 static pr_table_entry_t *tab_entry_next(pr_table_t *tab) {
304   pr_table_entry_t *ent = NULL;
305 
306   if (tab->tab_iter_ent) {
307     ent = tab->tab_iter_ent->next;
308 
309     if (!ent) {
310       register unsigned int i;
311 
312       /* Reset ent to be NULL, so that if we don't find a populated chain,
313        * we properly return NULL to the caller.
314        */
315       ent = NULL;
316 
317       /* Skip to the next populated chain. */
318       for (i = tab->tab_iter_ent->idx + 1; i < tab->nchains; i++) {
319         if (tab->chains[i]) {
320           ent = tab->chains[i];
321           break;
322         }
323       }
324     }
325 
326   } else {
327     register unsigned int i;
328 
329     /* Find the first non-empty chain. */
330     for (i = 0; i < tab->nchains; i++) {
331       if (tab->chains[i]) {
332         ent = tab->chains[i];
333         break;
334       }
335     }
336   }
337 
338   tab->tab_iter_ent = ent;
339   return ent;
340 }
341 
tab_entry_remove(pr_table_t * tab,pr_table_entry_t * e)342 static void tab_entry_remove(pr_table_t *tab, pr_table_entry_t *e) {
343   pr_table_entry_t *h = NULL;
344 
345   h = tab->chains[e->idx];
346   tab->entremove(&h, e);
347   tab->chains[e->idx] = h;
348 
349   e->key->nents--;
350   if (e->key->nents == 0) {
351     tab_key_free(tab, e->key);
352     e->key = NULL;
353   }
354 
355   tab->nents--;
356 }
357 
tab_get_seed(void)358 static unsigned int tab_get_seed(void) {
359   unsigned int seed = 0;
360 #ifndef PR_USE_OPENSSL
361   int fd = -1;
362   ssize_t nread = 0;
363 #endif /* Not PR_USE_OPENSSL */
364 
365 #ifdef PR_USE_OPENSSL
366   RAND_bytes((unsigned char *) &seed, sizeof(seed));
367 #else
368   /* Try reading from /dev/urandom, if present.  Use non-blocking IO, so that
369    * we do not block/wait; many platforms alias /dev/urandom to /dev/random.
370    */
371   fd = open("/dev/urandom", O_RDONLY|O_NONBLOCK);
372   if (fd >= 0) {
373     nread = read(fd, &seed, sizeof(seed));
374     (void) close(fd);
375   }
376 
377   if (nread < 0) {
378     time_t now = time(NULL);
379 
380     /* No /dev/urandom present (e.g. we might be in a chroot) or not able
381      * to read the needed amount of data, but we still want a seed.  Fallback
382      * to using rand(3), PID, and current time.
383      */
384     seed = (unsigned int) (rand() ^ getpid() ^ now);
385   }
386 #endif /* PR_USE_OPENSSL */
387 
388   return seed;
389 }
390 
391 /* Public Table API
392  */
393 
pr_table_kadd(pr_table_t * tab,const void * key_data,size_t key_datasz,const void * value_data,size_t value_datasz)394 int pr_table_kadd(pr_table_t *tab, const void *key_data, size_t key_datasz,
395     const void *value_data, size_t value_datasz) {
396   unsigned int h = 0, idx = 0;
397   pr_table_entry_t *e = NULL, *n = NULL;
398 
399   if (tab == NULL ||
400       key_data == NULL ||
401       (value_datasz > 0 && value_data == NULL)) {
402     errno = EINVAL;
403     return -1;
404   }
405 
406   if (tab->nents == tab->nmaxents) {
407     /* Table is full. */
408     errno = ENOSPC;
409     return -1;
410   }
411 
412   /* Don't forget to add in the random seed data. */
413   h = tab->keyhash(key_data, key_datasz) + tab->seed;
414 
415   /* The index of the chain to use is the hash value modulo the number
416    * of chains.
417    */
418   idx = h % tab->nchains;
419 
420   /* Allocate a new entry for the given values. */
421   n = tab_entry_alloc(tab);
422   n->value_data = value_data;
423   n->value_datasz = value_datasz;
424   n->idx = idx;
425 
426   /* Find the current chain entry at this index. */
427   e = tab->chains[idx];
428   if (e != NULL) {
429     pr_table_entry_t *ei;
430 
431     /* There is a chain at this index.  Next step is to see if any entry
432      * on this chain has the exact same key.  If so, increase the entry ref
433      * count on that key, otherwise, create a new key.
434      */
435 
436     for (ei = e; ei; ei = ei->next) {
437       if (ei->key->hash != h) {
438         continue;
439       }
440 
441       /* Hash collision.  Now check if the key data that was hashed
442        * is identical.  If so, we have multiple values for the same key.
443        */
444 
445       if (tab->keycmp(ei->key->key_data, ei->key->key_datasz,
446           key_data, key_datasz) == 0) {
447 
448         /* Check if this table allows multivalues. */
449         if (!(tab->flags & PR_TABLE_FL_MULTI_VALUE)) {
450           errno = EEXIST;
451           return -1;
452         }
453 
454         n->key = ei->key;
455       }
456     }
457 
458   } else {
459 
460     /* This new entry becomes the head of this chain. */
461     tab->chains[idx] = n;
462   }
463 
464   if (!n->key) {
465     pr_table_key_t *k;
466 
467     /* Allocate a new key. */
468     k = tab_key_alloc(tab);
469 
470     k->key_data = (void *) key_data;
471     k->key_datasz = key_datasz;
472     k->hash = h;
473     k->nents = 0;
474 
475     n->key = k;
476   }
477 
478   tab_entry_insert(tab, n);
479   return 0;
480 }
481 
pr_table_kexists(pr_table_t * tab,const void * key_data,size_t key_datasz)482 int pr_table_kexists(pr_table_t *tab, const void *key_data, size_t key_datasz) {
483   unsigned int h, idx;
484   pr_table_entry_t *head, *ent;
485 
486   if (tab == NULL ||
487       key_data == NULL) {
488     errno = EINVAL;
489     return -1;
490   }
491 
492   if (tab->nents == 0) {
493     errno = ENOENT;
494     return -1;
495   }
496 
497   if (tab->flags & PR_TABLE_FL_USE_CACHE) {
498     /* Has the caller already wanted to lookup this same key previously?
499      * If so, reuse that lookup if we can.  In this case, "same key" means
500      * the _exact same pointer_, not identical data.
501      */
502     if (tab->cache_ent &&
503         tab->cache_ent->key->key_data == key_data)
504       return tab->cache_ent->key->nents;
505   }
506 
507   /* Don't forget to add in the random seed data. */
508   h = tab->keyhash(key_data, key_datasz) + tab->seed;
509 
510   idx = h % tab->nchains;
511   head = tab->chains[idx];
512   if (head == NULL) {
513     tab->cache_ent = NULL;
514     return 0;
515   }
516 
517   for (ent = head; ent; ent = ent->next) {
518     if (ent->key == NULL ||
519         ent->key->hash != h) {
520       continue;
521     }
522 
523     /* Matching hashes.  Now to see if the keys themselves match. */
524     if (tab->keycmp(ent->key->key_data, ent->key->key_datasz,
525         key_data, key_datasz) == 0) {
526 
527       if (tab->flags & PR_TABLE_FL_USE_CACHE) {
528         tab->cache_ent = ent;
529       }
530 
531       return ent->key->nents;
532     }
533   }
534 
535   tab->cache_ent = NULL;
536 
537   errno = EINVAL;
538   return 0;
539 }
540 
pr_table_kget(pr_table_t * tab,const void * key_data,size_t key_datasz,size_t * value_datasz)541 const void *pr_table_kget(pr_table_t *tab, const void *key_data,
542     size_t key_datasz, size_t *value_datasz) {
543   unsigned int h = 0;
544   pr_table_entry_t *head = NULL, *ent = NULL;
545 
546   if (tab == NULL) {
547     errno = EINVAL;
548     return NULL;
549   }
550 
551   /* Use a NULL key as a way of rewinding the per-key lookup. */
552   if (key_data == NULL) {
553     tab->cache_ent = NULL;
554     tab->val_iter_ent = NULL;
555 
556     errno = ENOENT;
557     return NULL;
558   }
559 
560   if (tab->nents == 0) {
561     tab->cache_ent = NULL;
562     tab->val_iter_ent = NULL;
563 
564     errno = ENOENT;
565     return NULL;
566   }
567 
568   /* Don't forget to add in the random seed data. */
569   h = tab->keyhash(key_data, key_datasz) + tab->seed;
570 
571   /* Has the caller already looked up this same key previously?
572    * If so, continue the lookup where we left off.  In this case,
573    * "same key" means the _exact same pointer_, not identical data.
574    */
575   if (tab->val_iter_ent &&
576       tab->val_iter_ent->key->key_data == key_data) {
577     head = tab->val_iter_ent->next;
578 
579   } else if ((tab->flags & PR_TABLE_FL_USE_CACHE) &&
580              tab->cache_ent &&
581              tab->cache_ent->key->key_data == key_data) {
582 
583      /* If the cached lookup entry matches, we'll use it. */
584      head = tab->cache_ent->next;
585 
586   } else {
587     unsigned int idx = h % tab->nchains;
588     head = tab->chains[idx];
589   }
590 
591   if (head == NULL) {
592     tab->cache_ent = NULL;
593     tab->val_iter_ent = NULL;
594 
595     errno = ENOENT;
596     return NULL;
597   }
598 
599   for (ent = head; ent; ent = ent->next) {
600     if (ent->key == NULL ||
601         ent->key->hash != h) {
602       continue;
603     }
604 
605     /* Matching hashes.  Now to see if the keys themselves match. */
606     if (tab->keycmp(ent->key->key_data, ent->key->key_datasz,
607         key_data, key_datasz) == 0) {
608 
609       if (tab->flags & PR_TABLE_FL_USE_CACHE) {
610         tab->cache_ent = ent;
611       }
612 
613       if (tab->flags & PR_TABLE_FL_MULTI_VALUE) {
614         tab->val_iter_ent = ent;
615       }
616 
617       if (value_datasz) {
618         *value_datasz = ent->value_datasz;
619       }
620 
621       return ent->value_data;
622     }
623   }
624 
625   tab->cache_ent = NULL;
626   tab->val_iter_ent = NULL;
627 
628   errno = ENOENT;
629   return NULL;
630 }
631 
pr_table_kremove(pr_table_t * tab,const void * key_data,size_t key_datasz,size_t * value_datasz)632 const void *pr_table_kremove(pr_table_t *tab, const void *key_data,
633     size_t key_datasz, size_t *value_datasz) {
634   unsigned int h = 0, idx = 0;
635   pr_table_entry_t *head = NULL, *ent = NULL;
636 
637   if (tab == NULL ||
638       key_data == NULL) {
639     errno = EINVAL;
640     return NULL;
641   }
642 
643   if (tab->nents == 0) {
644     errno = ENOENT;
645     return NULL;
646   }
647 
648   /* Has the caller already wanted to lookup this same key previously?
649    * If so, reuse that lookup if we can.  In this case, "same key" means
650    * the _exact same pointer_, not identical data.
651    */
652   if ((tab->flags & PR_TABLE_FL_USE_CACHE) &&
653       tab->cache_ent &&
654       tab->cache_ent->key->key_data == key_data) {
655     const void *value_data;
656 
657     value_data = tab->cache_ent->value_data;
658     if (value_datasz) {
659       *value_datasz = tab->cache_ent->value_datasz;
660     }
661 
662     tab_entry_remove(tab, tab->cache_ent);
663     tab_entry_free(tab, tab->cache_ent);
664     tab->cache_ent = NULL;
665 
666     return value_data;
667   }
668 
669   /* Don't forget to add in the random seed data. */
670   h = tab->keyhash(key_data, key_datasz) + tab->seed;
671 
672   idx = h % tab->nchains;
673   head = tab->chains[idx];
674   if (head == NULL) {
675     tab->cache_ent = NULL;
676 
677     errno = ENOENT;
678     return NULL;
679   }
680 
681   for (ent = head; ent; ent = ent->next) {
682     if (ent->key == NULL ||
683         ent->key->hash != h) {
684       continue;
685     }
686 
687     /* Matching hashes.  Now to see if the keys themselves match. */
688     if (tab->keycmp(ent->key->key_data, ent->key->key_datasz,
689         key_data, key_datasz) == 0) {
690       const void *value_data;
691 
692       value_data = ent->value_data;
693       if (value_datasz) {
694         *value_datasz = ent->value_datasz;
695       }
696 
697       tab_entry_remove(tab, ent);
698       tab_entry_free(tab, ent);
699       tab->cache_ent = NULL;
700 
701       return value_data;
702     }
703   }
704 
705   tab->cache_ent = NULL;
706 
707   errno = EINVAL;
708   return NULL;
709 }
710 
pr_table_kset(pr_table_t * tab,const void * key_data,size_t key_datasz,const void * value_data,size_t value_datasz)711 int pr_table_kset(pr_table_t *tab, const void *key_data, size_t key_datasz,
712     const void *value_data, size_t value_datasz) {
713   unsigned int h;
714   pr_table_entry_t *head, *ent;
715 
716   /* XXX Should callers be allowed to set NULL values for keys? */
717 
718   if (tab == NULL ||
719       key_data == NULL ||
720       (value_datasz > 0 && value_data == NULL)) {
721     errno = EINVAL;
722     return -1;
723   }
724 
725   if (tab->nents == 0) {
726     errno = ENOENT;
727     return -1;
728   }
729 
730   /* Don't forget to add in the random seed data. */
731   h = tab->keyhash(key_data, key_datasz) + tab->seed;
732 
733   /* Has the caller already looked up this same key previously?
734    * If so, continue the lookup where we left off.  In this case,
735    * "same key" means the _exact same pointer_, not identical data.
736    */
737   if (tab->val_iter_ent &&
738       tab->val_iter_ent->key->key_data == key_data) {
739     head = tab->val_iter_ent->next;
740 
741   } else if ((tab->flags & PR_TABLE_FL_USE_CACHE) &&
742              tab->cache_ent &&
743              tab->cache_ent->key->key_data == key_data) {
744 
745      /* If the cached lookup entry matches, we'll use it. */
746      head = tab->cache_ent->next;
747 
748   } else {
749     unsigned int idx = h % tab->nchains;
750     head = tab->chains[idx];
751   }
752 
753   if (head == NULL) {
754     tab->cache_ent = NULL;
755     tab->val_iter_ent = NULL;
756 
757     errno = ENOENT;
758     return -1;
759   }
760 
761   for (ent = head; ent; ent = ent->next) {
762     if (ent->key == NULL ||
763         ent->key->hash != h) {
764       continue;
765     }
766 
767     /* Matching hashes.  Now to see if the keys themselves match. */
768     if (tab->keycmp(ent->key->key_data, ent->key->key_datasz,
769         key_data, key_datasz) == 0) {
770 
771       if (ent->value_data == value_data) {
772         errno = EEXIST;
773         return -1;
774       }
775 
776       ent->value_data = value_data;
777       ent->value_datasz = value_datasz;
778 
779       if (tab->flags & PR_TABLE_FL_USE_CACHE) {
780         tab->cache_ent = ent;
781       }
782 
783       if (tab->flags & PR_TABLE_FL_MULTI_VALUE) {
784         tab->val_iter_ent = ent;
785       }
786 
787       return 0;
788     }
789   }
790 
791   tab->cache_ent = NULL;
792   tab->val_iter_ent = NULL;
793 
794   errno = EINVAL;
795   return -1;
796 }
797 
pr_table_add(pr_table_t * tab,const char * key_data,const void * value_data,size_t value_datasz)798 int pr_table_add(pr_table_t *tab, const char *key_data, const void *value_data,
799     size_t value_datasz) {
800 
801   if (tab == NULL ||
802       key_data == NULL) {
803     errno = EINVAL;
804     return -1;
805   }
806 
807   if (value_data &&
808       value_datasz == 0) {
809     value_datasz = strlen((char *) value_data) + 1;
810   }
811 
812   return pr_table_kadd(tab, key_data, strlen(key_data) + 1, value_data,
813     value_datasz);
814 }
815 
pr_table_add_dup(pr_table_t * tab,const char * key_data,const void * value_data,size_t value_datasz)816 int pr_table_add_dup(pr_table_t *tab, const char *key_data,
817     const void *value_data, size_t value_datasz) {
818   void *dup_data;
819 
820   if (tab == NULL ||
821       key_data == NULL) {
822     errno = EINVAL;
823     return -1;
824   }
825 
826   if (value_data == NULL &&
827       value_datasz != 0) {
828     errno = EINVAL;
829     return -1;
830   }
831 
832   if (value_data != NULL &&
833       value_datasz == 0) {
834     value_datasz = strlen((const char *) value_data) + 1;
835   }
836 
837   dup_data = pcalloc(tab->pool, value_datasz);
838   memcpy(dup_data, value_data, value_datasz);
839 
840   return pr_table_add(tab, key_data, dup_data, value_datasz);
841 }
842 
pr_table_nalloc(pool * p,int flags,unsigned int nchains)843 pr_table_t *pr_table_nalloc(pool *p, int flags, unsigned int nchains) {
844   pr_table_t *tab;
845   pool *tab_pool;
846 
847   if (p == NULL ||
848       nchains == 0) {
849     errno = EINVAL;
850     return NULL;
851   }
852 
853   tab_pool = make_sub_pool(p);
854   pr_pool_tag(tab_pool, "table pool");
855 
856   tab = pcalloc(tab_pool, sizeof(pr_table_t));
857   tab->pool = tab_pool;
858   tab->flags = flags;
859   tab->nchains = nchains;
860   tab->chains = pcalloc(tab_pool,
861     sizeof(pr_table_entry_t *) * tab->nchains);
862 
863   tab->keycmp = key_cmp;
864   tab->keyhash = key_hash;
865   tab->entinsert = entry_insert;
866   tab->entremove = entry_remove;
867 
868   tab->seed = tab_get_seed();
869   tab->nmaxents = PR_TABLE_DEFAULT_MAX_ENTS;
870 
871   return tab;
872 }
873 
pr_table_alloc(pool * p,int flags)874 pr_table_t *pr_table_alloc(pool *p, int flags) {
875   return pr_table_nalloc(p, flags, PR_TABLE_DEFAULT_NCHAINS);
876 }
877 
pr_table_count(pr_table_t * tab)878 int pr_table_count(pr_table_t *tab) {
879   if (tab == NULL) {
880     errno = EINVAL;
881     return -1;
882   }
883 
884   return tab->nents;
885 }
886 
pr_table_do(pr_table_t * tab,int (* cb)(const void * key_data,size_t key_datasz,const void * value_data,size_t value_datasz,void * user_data),void * user_data,int flags)887 int pr_table_do(pr_table_t *tab, int (*cb)(const void *key_data,
888     size_t key_datasz, const void *value_data, size_t value_datasz,
889     void *user_data), void *user_data, int flags) {
890   register unsigned int i;
891 
892   if (tab == NULL ||
893       cb == NULL) {
894     errno = EINVAL;
895     return -1;
896   }
897 
898   if (tab->nents == 0) {
899     return 0;
900   }
901 
902   for (i = 0; i < tab->nchains; i++) {
903     pr_table_entry_t *ent;
904 
905     ent = tab->chains[i];
906     while (ent != NULL) {
907       pr_table_entry_t *next_ent;
908       int res;
909 
910       next_ent = ent->next;
911 
912       if (!handling_signal) {
913         pr_signals_handle();
914       }
915 
916       res = cb(ent->key->key_data, ent->key->key_datasz, ent->value_data,
917         ent->value_datasz, user_data);
918       if (res < 0 &&
919           !(flags & PR_TABLE_DO_FL_ALL)) {
920         errno = EPERM;
921         return -1;
922       }
923 
924       ent = next_ent;
925     }
926   }
927 
928   return 0;
929 }
930 
pr_table_empty(pr_table_t * tab)931 int pr_table_empty(pr_table_t *tab) {
932   register unsigned int i;
933 
934   if (tab == NULL) {
935     errno = EINVAL;
936     return -1;
937   }
938 
939   if (tab->nents == 0) {
940     return 0;
941   }
942 
943   for (i = 0; i < tab->nchains; i++) {
944     pr_table_entry_t *e;
945 
946     e = tab->chains[i];
947     while (e != NULL) {
948       if (!handling_signal) {
949         pr_signals_handle();
950       }
951 
952       tab_entry_remove(tab, e);
953       tab_entry_free(tab, e);
954 
955       e = tab->chains[i];
956     }
957 
958     tab->chains[i] = NULL;
959   }
960 
961   return 0;
962 }
963 
pr_table_exists(pr_table_t * tab,const char * key_data)964 int pr_table_exists(pr_table_t *tab, const char *key_data) {
965   if (tab == NULL ||
966       key_data == NULL) {
967     errno = EINVAL;
968     return -1;
969   }
970 
971   return pr_table_kexists(tab, key_data, strlen(key_data) + 1);
972 }
973 
pr_table_free(pr_table_t * tab)974 int pr_table_free(pr_table_t *tab) {
975 
976   if (tab == NULL) {
977     errno = EINVAL;
978     return -1;
979   }
980 
981   if (tab->nents != 0) {
982     errno = EPERM;
983     return -1;
984   }
985 
986   destroy_pool(tab->pool);
987   return 0;
988 }
989 
pr_table_get(pr_table_t * tab,const char * key_data,size_t * value_datasz)990 const void *pr_table_get(pr_table_t *tab, const char *key_data,
991     size_t *value_datasz) {
992   size_t key_datasz = 0;
993 
994   if (tab == NULL) {
995     errno = EINVAL;
996     return NULL;
997   }
998 
999   if (key_data) {
1000     key_datasz = strlen(key_data) + 1;
1001   }
1002 
1003   return pr_table_kget(tab, key_data, key_datasz, value_datasz);
1004 }
1005 
pr_table_knext(pr_table_t * tab,size_t * key_datasz)1006 const void *pr_table_knext(pr_table_t *tab, size_t *key_datasz) {
1007   pr_table_entry_t *ent, *prev;
1008 
1009   if (tab == NULL) {
1010     errno = EINVAL;
1011     return NULL;
1012   }
1013 
1014   prev = tab->tab_iter_ent;
1015 
1016   ent = tab_entry_next(tab);
1017   while (ent != NULL) {
1018     if (!handling_signal) {
1019       pr_signals_handle();
1020     }
1021 
1022     if (prev &&
1023         ent->key == prev->key) {
1024       ent = tab_entry_next(tab);
1025       continue;
1026     }
1027 
1028     break;
1029   }
1030 
1031   if (ent == NULL) {
1032     errno = EPERM;
1033     return NULL;
1034   }
1035 
1036   if (key_datasz != NULL) {
1037     *key_datasz = ent->key->key_datasz;
1038   }
1039 
1040   return ent->key->key_data;
1041 }
1042 
pr_table_next(pr_table_t * tab)1043 const void *pr_table_next(pr_table_t *tab) {
1044   return pr_table_knext(tab, NULL);
1045 }
1046 
pr_table_remove(pr_table_t * tab,const char * key_data,size_t * value_datasz)1047 const void *pr_table_remove(pr_table_t *tab, const char *key_data,
1048     size_t *value_datasz) {
1049 
1050   if (tab == NULL ||
1051       key_data == NULL) {
1052     errno = EINVAL;
1053     return NULL;
1054   }
1055 
1056   return pr_table_kremove(tab, key_data, strlen(key_data) + 1, value_datasz);
1057 }
1058 
pr_table_rewind(pr_table_t * tab)1059 int pr_table_rewind(pr_table_t *tab) {
1060   if (tab == NULL) {
1061     errno = EINVAL;
1062     return -1;
1063   }
1064 
1065   tab->tab_iter_ent = NULL;
1066   return 0;
1067 }
1068 
pr_table_set(pr_table_t * tab,const char * key_data,const void * value_data,size_t value_datasz)1069 int pr_table_set(pr_table_t *tab, const char *key_data, const void *value_data,
1070     size_t value_datasz) {
1071 
1072   if (tab == NULL ||
1073       key_data == NULL) {
1074     errno = EINVAL;
1075     return -1;
1076   }
1077 
1078   if (value_data &&
1079       value_datasz == 0) {
1080     value_datasz = strlen((const char *) value_data) + 1;
1081   }
1082 
1083   return pr_table_kset(tab, key_data, strlen(key_data) + 1, value_data,
1084     value_datasz);
1085 }
1086 
pr_table_ctl(pr_table_t * tab,int cmd,void * arg)1087 int pr_table_ctl(pr_table_t *tab, int cmd, void *arg) {
1088 
1089   if (tab == NULL) {
1090     errno = EINVAL;
1091     return -1;
1092   }
1093 
1094   /* Set control operations can only be performed on an empty table. */
1095   if (tab->nents != 0) {
1096     errno = EPERM;
1097     return -1;
1098   }
1099 
1100   switch (cmd) {
1101     case PR_TABLE_CTL_SET_KEY_HASH:
1102       tab->keyhash = arg ?
1103         (unsigned int (*)(const void *, size_t)) arg :
1104         (unsigned int (*)(const void *, size_t)) key_hash;
1105       return 0;
1106 
1107     case PR_TABLE_CTL_SET_FLAGS:
1108       if (arg == NULL) {
1109         errno = EINVAL;
1110         return -1;
1111       }
1112 
1113       tab->flags = *((unsigned long *) arg);
1114       return 0;
1115 
1116     case PR_TABLE_CTL_SET_KEY_CMP:
1117       tab->keycmp = arg ?
1118         (int (*)(const void *, size_t, const void *, size_t)) arg :
1119         (int (*)(const void *, size_t, const void *, size_t)) key_cmp;
1120       return 0;
1121 
1122     case PR_TABLE_CTL_SET_ENT_INSERT:
1123       tab->entinsert = arg ?
1124         (void (*)(pr_table_entry_t **, pr_table_entry_t *)) arg :
1125         (void (*)(pr_table_entry_t **, pr_table_entry_t *)) entry_insert;
1126       return 0;
1127 
1128     case PR_TABLE_CTL_SET_ENT_REMOVE:
1129       tab->entremove = arg ?
1130         (void (*)(pr_table_entry_t **, pr_table_entry_t *)) arg :
1131         (void (*)(pr_table_entry_t **, pr_table_entry_t *)) entry_remove;
1132       return 0;
1133 
1134     case PR_TABLE_CTL_SET_NCHAINS: {
1135       unsigned int new_nchains;
1136 
1137       if (arg == NULL) {
1138         errno = EINVAL;
1139         return -1;
1140       }
1141 
1142       new_nchains = *((unsigned int *) arg);
1143       if (new_nchains == 0) {
1144         errno = EINVAL;
1145         return -1;
1146       }
1147 
1148       tab->nchains = new_nchains;
1149 
1150       /* Note: by not freeing the memory of the previously allocated
1151        * chains, this constitutes a minor leak of the table's memory pool.
1152        */
1153       tab->chains = pcalloc(tab->pool,
1154         sizeof(pr_table_entry_t *) * tab->nchains);
1155       return 0;
1156     }
1157 
1158     case PR_TABLE_CTL_SET_MAX_ENTS: {
1159       unsigned int nmaxents;
1160 
1161       nmaxents = *((unsigned int *) arg);
1162 
1163       if (nmaxents == 0) {
1164         errno = EINVAL;
1165         return -1;
1166       }
1167 
1168       /* If the given maximum is less than the number of entries currently
1169        * in the table, we have to return an error; we do not want to get
1170        * into the business of table truncation just yet.
1171        */
1172       if (tab->nents > nmaxents) {
1173         errno = EPERM;
1174         return -1;
1175       }
1176 
1177       tab->nmaxents = nmaxents;
1178       return 0;
1179     }
1180 
1181     default:
1182       errno = EINVAL;
1183   }
1184 
1185   return -1;
1186 }
1187 
pr_table_pcalloc(pr_table_t * tab,size_t sz)1188 void *pr_table_pcalloc(pr_table_t *tab, size_t sz) {
1189   if (tab == NULL ||
1190       sz == 0) {
1191     errno = EINVAL;
1192     return NULL;
1193   }
1194 
1195   return pcalloc(tab->pool, sz);
1196 }
1197 
table_printf(const char * fmt,...)1198 static void table_printf(const char *fmt, ...) {
1199   char buf[PR_TUNABLE_BUFFER_SIZE+1];
1200   va_list msg;
1201 
1202   memset(buf, '\0', sizeof(buf));
1203   va_start(msg, fmt);
1204   pr_vsnprintf(buf, sizeof(buf)-1, fmt, msg);
1205   va_end(msg);
1206 
1207   buf[sizeof(buf)-1] = '\0';
1208   pr_trace_msg(trace_channel, 19, "dump: %s", buf);
1209 }
1210 
pr_table_load(pr_table_t * tab)1211 float pr_table_load(pr_table_t *tab) {
1212   float load_factor;
1213 
1214   if (tab == NULL) {
1215     errno = EINVAL;
1216     return -1.0;
1217   }
1218 
1219   load_factor = (tab->nents / tab->nchains);
1220   return load_factor;
1221 }
1222 
pr_table_dump(void (* dumpf)(const char * fmt,...),pr_table_t * tab)1223 void pr_table_dump(void (*dumpf)(const char *fmt, ...), pr_table_t *tab) {
1224   register unsigned int i;
1225 
1226   if (tab == NULL) {
1227     return;
1228   }
1229 
1230   if (dumpf == NULL) {
1231     dumpf = table_printf;
1232   }
1233 
1234   if (tab->flags == 0) {
1235     dumpf("%s", "[table flags]: None");
1236 
1237   } else {
1238     if ((tab->flags & PR_TABLE_FL_MULTI_VALUE) &&
1239         (tab->flags & PR_TABLE_FL_USE_CACHE)) {
1240       dumpf("%s", "[table flags]: MultiValue, UseCache");
1241 
1242     } else {
1243       if (tab->flags & PR_TABLE_FL_MULTI_VALUE) {
1244         dumpf("%s", "[table flags]: MultiValue");
1245       }
1246 
1247       if (tab->flags & PR_TABLE_FL_USE_CACHE) {
1248         dumpf("%s", "[table flags]: UseCache");
1249       }
1250     }
1251   }
1252 
1253   if (tab->nents == 0) {
1254     dumpf("[empty table]");
1255     return;
1256   }
1257 
1258   dumpf("[table count]: %u", tab->nents);
1259   for (i = 0; i < tab->nchains; i++) {
1260     register unsigned int j = 0;
1261     pr_table_entry_t *ent = tab->chains[i];
1262 
1263     while (ent) {
1264       if (!handling_signal) {
1265         pr_signals_handle();
1266       }
1267 
1268       dumpf("[hash %u (%u chains) chain %u#%u] '%s' => '%s' (%u)",
1269         ent->key->hash, tab->nchains, i, j++, ent->key->key_data,
1270         ent->value_data, ent->value_datasz);
1271       ent = ent->next;
1272     }
1273   }
1274 
1275   return;
1276 }
1277 
table_handling_signal(int bool)1278 int table_handling_signal(int bool) {
1279   if (bool == TRUE ||
1280       bool == FALSE) {
1281     handling_signal = bool;
1282     return 0;
1283   }
1284 
1285   errno = EINVAL;
1286   return -1;
1287 }
1288