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