• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

proftpd / proftpd / 28531645955

01 Jul 2026 04:14PM UTC coverage: 92.468% (-0.6%) from 93.032%
28531645955

push

github

web-flow
Exercise caution when reading the client-provided file size for SCP uploads, as it could possibly overflow our size type.

Thanks to Fabian Wahle of Hap Security for reporting this issue.

48701 of 52668 relevant lines covered (92.47%)

227.08 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

85.69
/src/table.c
1
/*
2
 * ProFTPD - FTP server daemon
3
 * Copyright (c) 2004-2026 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, see <https://www.gnu.org/licenses/>.
17
 *
18
 * As a special exemption, The ProFTPD Project team and other respective
19
 * copyright holders give permission to link this program with OpenSSL, and
20
 * distribute the resulting executable, without including the source code for
21
 * OpenSSL in the source distribution.
22
 */
23

24
/* Table API implementation */
25

26
#include "conf.h"
27

28
#ifdef PR_USE_OPENSSL
29
#include <openssl/rand.h>
30
#endif /* PR_USE_OPENSSL */
31

32
#define PR_TABLE_DEFAULT_NCHAINS        256
33
#define PR_TABLE_DEFAULT_MAX_ENTS        8192
34
#define PR_TABLE_ENT_POOL_SIZE                64
35

36
struct table_rec {
37
  pool *pool;
38
  unsigned long flags;
39

40
  /* These bytes are randomly generated at table creation time, and
41
   * are used to seed the hashing function, so as to defend/migitate
42
   * against attempts to feed carefully crafted keys which force the
43
   * table into its worst-case performance scenario.
44
   *
45
   * For more information on attacks of this nature, see:
46
   *
47
   *   http://www.cs.rice.edu/~scrosby/hash/CrosbyWallach_UsenixSec2003/
48
   */
49
  unsigned int seed;
50

51
  /* Maximum number of entries that can be stored in this table.  The
52
   * default maximum (PR_TABLE_DEFAULT_MAX_ENTS) is set fairly high.
53
   * This limit is present in order to defend/mitigate against certain abuse
54
   * scenarios.
55
   *
56
   * XXX Note that an additional protective measure can/might be placed on
57
   * the maximum length of a given chain, to detect other types of attacks
58
   * that force the table into the worse-case performance scenario (i.e.
59
   * linear scanning of a long chain).  If such is added, then a Table API
60
   * function should be added for returning the length of the longest chain
61
   * in the table.  Such a function could be used by modules to determine
62
   * if their tables are being abused (or in need of readjustment).
63
   */
64
  unsigned int nmaxents;
65

66
  pr_table_entry_t **chains;
67
  unsigned int nchains;
68
  unsigned int nents;
69

70
  /* List of free structures. */
71
  pr_table_entry_t *free_ents;
72
  pr_table_key_t *free_keys;
73

74
  /* For iterating over all the keys in the entire table. */
75
  pr_table_entry_t *tab_iter_ent;
76

77
  /* For iterating through all of the possible multiple values for a single
78
   * key.  Only used if the PR_TABLE_FL_MULTI_VALUE flag is set.
79
   */
80
  pr_table_entry_t *val_iter_ent;
81

82
  /* Cache of last looked-up entry.  Usage of this field can be enabled
83
   * by using the PR_TABLE_FL_USE_CACHE flag.
84
   */
85
  pr_table_entry_t *cache_ent;
86

87
  /* Table callbacks. */
88
  int (*keycmp)(const void *, size_t, const void *, size_t);
89
  unsigned int (*keyhash)(const void *, size_t);
90
  void (*entinsert)(pr_table_entry_t **, pr_table_entry_t *);
91
  void (*entremove)(pr_table_entry_t **, pr_table_entry_t *);
92
};
93

94
static int handling_signal = FALSE;
95

96
static const char *trace_channel = "table";
97

98
/* Default table callbacks
99
 */
100

101
static int key_cmp(const void *key1, size_t keysz1, const void *key2,
9,152✔
102
    size_t keysz2) {
103
  const char *k1, *k2;
104

105
  if (keysz1 != keysz2) {
9,152✔
106
    return keysz1 < keysz2 ? -1 : 1;
×
107
  }
108

109
  k1 = key1;
9,152✔
110
  k2 = key2;
9,152✔
111

112
  if (keysz1 >= 1) {
9,152✔
113
    /* Basic check of the first character in each key, trying to reduce
114
     * the chances of calling strncmp(3) if not needed.
115
     */
116

117
    if (k1[0] != k2[0]) {
9,152✔
118
      return k1[0] < k2[0] ? -1 : 1;
×
119
    }
120

121
    /* Special case (unlikely, but possible). */
122
    if (keysz1 == 1) {
9,152✔
123
      return 0;
124
    }
125
  }
126

127
  return strncmp((const char *) key1, (const char *) key2, keysz1);
9,151✔
128
}
129

130
/* Use Perl's hashing algorithm by default.
131
 *
132
 * Here's a good article about this hashing algorithm, and about hashing
133
 * functions in general:
134
 *
135
 *  http://www.perl.com/pub/2002/10/01/hashes.html
136
 */
137
static unsigned int key_hash(const void *key, size_t keysz) {
13,485✔
138
  unsigned int i = 0;
13,485✔
139
  size_t sz = !keysz ? strlen((const char *) key) : keysz;
13,485✔
140

141
  while (sz--) {
103,258✔
142
    const char *k = key;
89,773✔
143
    unsigned int c;
144

145
    c = k[sz];
89,773✔
146

147
    if (!handling_signal) {
89,773✔
148
      /* Always handle signals in potentially long-running while loops. */
149
      pr_signals_handle();
89,773✔
150
    }
151

152
    i = (i * 33) + c;
89,773✔
153
  }
154

155
  return i;
13,485✔
156
}
157

158
/* Default insertion is simply to add the given entry to the end of the
159
 * chain.
160
 */
161
static void entry_insert(pr_table_entry_t **h, pr_table_entry_t *e) {
116✔
162
  pr_table_entry_t *ei;
163

164
  if (*h == NULL) {
116✔
165
    return;
166
  }
167

168
  for (ei = *h; ei != NULL && ei->next; ei = ei->next) {
2,978✔
169
  }
170

171
  /* Now, ei points to the last entry in the chain. */
172
  ei->next = e;
116✔
173
  e->prev = ei;
116✔
174
}
175

176
/* Default removal is simply to remove the entry from the chain. */
177
static void entry_remove(pr_table_entry_t **h, pr_table_entry_t *e) {
88✔
178

179
  if (e->next) {
88✔
180
    e->next->prev = e->prev;
1✔
181
  }
182

183
  if (e->prev) {
88✔
184
    e->prev->next = e->next;
×
185

186
  } else {
187
    /* This entry is the head. */
188
    *h = e->next;
88✔
189
  }
190

191
  e->prev = e->next = NULL;
88✔
192
}
88✔
193

194
/* Table key management
195
 */
196

197
static pr_table_key_t *tab_key_alloc(pr_table_t *tab) {
198
  pr_table_key_t *k;
199

200
  /* Try to find a free key on the free list first... */
201
  if (tab->free_keys) {
2,024✔
202
    k = tab->free_keys;
38✔
203
    tab->free_keys = k->next;
38✔
204
    k->next = NULL;
38✔
205

206
    return k;
207
  }
208

209
  /* ...otherwise, allocate a new key. */
210
  k = pcalloc(tab->pool, sizeof(pr_table_key_t));
1,986✔
211

212
  return k;
213
}
214

215
static void tab_key_free(pr_table_t *tab, pr_table_key_t *k) {
88✔
216
  /* Clear everything from the given key. */
217
  memset(k, 0, sizeof(pr_table_key_t));
88✔
218

219
  /* Add this key to the table's free list. */
220
  if (tab->free_keys) {
88✔
221
    pr_table_key_t *i = tab->free_keys;
222

223
    /* Scan to the end of the list. */
224
    while (i->next != NULL) {
7✔
225
      if (!handling_signal) {
×
226
        pr_signals_handle();
×
227
      }
228

229
      i = i->next;
×
230
    }
231

232
    i->next = k;
7✔
233

234
  } else {
235
    tab->free_keys = k;
81✔
236
  }
237
}
88✔
238

239
/* Table entry management
240
 */
241

242
static pr_table_entry_t *tab_entry_alloc(pr_table_t *tab) {
243
  pr_table_entry_t *e;
244

245
  /* Try to find a free entry on the free list first... */
246
  if (tab->free_ents) {
2,034✔
247
    e = tab->free_ents;
38✔
248
    tab->free_ents = e->next;
38✔
249
    e->next = NULL;
38✔
250

251
    return e;
252
  }
253

254
  /* ...otherwise, allocate a new entry. */
255
  e = pcalloc(tab->pool, sizeof(pr_table_entry_t));
1,996✔
256

257
  return e;
258
}
259

260
static void tab_entry_free(pr_table_t *tab, pr_table_entry_t *e) {
88✔
261
  /* Clear everything from the given entry. */
262
  memset(e, 0, sizeof(pr_table_entry_t));
88✔
263

264
  /* Add this entry to the table's free list. */
265
  if (tab->free_ents) {
88✔
266
    pr_table_entry_t *i = tab->free_ents;
267

268
    /* Scan to the end of the list. */
269
    while (i->next != NULL) {
7✔
270
      if (!handling_signal) {
×
271
        pr_signals_handle();
×
272
      }
273

274
      i = i->next;
×
275
    }
276

277
    i->next = e;
7✔
278

279
  } else {
280
    tab->free_ents = e;
81✔
281
  }
282
}
88✔
283

284
static void tab_entry_insert(pr_table_t *tab, pr_table_entry_t *e) {
2,025✔
285
  pr_table_entry_t *h = tab->chains[e->idx];
2,025✔
286

287
  if (h &&
4,050✔
288
      h != e) {
2,025✔
289

290
    /* Only insert the entry if the head we found is different from the
291
     * the given entry.  There is an edge case when the entry being added
292
     * is the head of a new chain.
293
     */
294
    tab->entinsert(&h, e);
116✔
295
    tab->chains[e->idx] = h;
116✔
296
  }
297

298
  e->key->nents++;
2,025✔
299
  tab->nents++;
2,025✔
300
}
2,025✔
301

302
static pr_table_entry_t *tab_entry_next(pr_table_t *tab) {
23✔
303
  pr_table_entry_t *ent = NULL;
23✔
304

305
  if (tab->tab_iter_ent) {
23✔
306
    ent = tab->tab_iter_ent->next;
10✔
307

308
    if (!ent) {
10✔
309
      register unsigned int i;
310

311
      /* Reset ent to be NULL, so that if we don't find a populated chain,
312
       * we properly return NULL to the caller.
313
       */
314
      ent = NULL;
10✔
315

316
      /* Skip to the next populated chain. */
317
      for (i = tab->tab_iter_ent->idx + 1; i < tab->nchains; i++) {
1,340✔
318
        if (tab->chains[i]) {
1,331✔
319
          ent = tab->chains[i];
320
          break;
321
        }
322
      }
323
    }
324

325
  } else {
326
    register unsigned int i;
327

328
    /* Find the first non-empty chain. */
329
    for (i = 0; i < tab->nchains; i++) {
1,940✔
330
      if (tab->chains[i]) {
1,951✔
331
        ent = tab->chains[i];
332
        break;
333
      }
334
    }
335
  }
336

337
  tab->tab_iter_ent = ent;
23✔
338
  return ent;
23✔
339
}
340

341
static void tab_entry_remove(pr_table_t *tab, pr_table_entry_t *e) {
88✔
342
  pr_table_entry_t *h = NULL;
88✔
343

344
  h = tab->chains[e->idx];
88✔
345
  tab->entremove(&h, e);
88✔
346
  tab->chains[e->idx] = h;
88✔
347

348
  e->key->nents--;
88✔
349
  if (e->key->nents == 0) {
88✔
350
    tab_key_free(tab, e->key);
88✔
351
    e->key = NULL;
88✔
352
  }
353

354
  tab->nents--;
88✔
355
}
88✔
356

357
static unsigned int tab_get_seed(void) {
358
  unsigned int seed = 0;
2,064✔
359
#ifndef PR_USE_OPENSSL
360
  int fd = -1;
361
  ssize_t nread = 0;
362
#endif /* Not PR_USE_OPENSSL */
363

364
#ifdef PR_USE_OPENSSL
365
  RAND_bytes((unsigned char *) &seed, sizeof(seed));
2,064✔
366
#else
367
  /* Try reading from /dev/urandom, if present.  Use non-blocking IO, so that
368
   * we do not block/wait; many platforms alias /dev/urandom to /dev/random.
369
   */
370
  fd = open("/dev/urandom", O_RDONLY|O_NONBLOCK);
371
  if (fd >= 0) {
372
    nread = read(fd, &seed, sizeof(seed));
373
    (void) close(fd);
374
  }
375

376
  if (nread < 0) {
377
    time_t now = time(NULL);
378

379
    /* No /dev/urandom present (e.g. we might be in a chroot) or not able
380
     * to read the needed amount of data, but we still want a seed.  Fallback
381
     * to using rand(3), PID, and current time.
382
     */
383
    seed = (unsigned int) (rand() ^ getpid() ^ now);
384
  }
385
#endif /* PR_USE_OPENSSL */
386

387
  return seed;
2,064✔
388
}
389

390
/* Public Table API
391
 */
392

393
int pr_table_kadd(pr_table_t *tab, const void *key_data, size_t key_datasz,
2,036✔
394
    const void *value_data, size_t value_datasz) {
395
  unsigned int h = 0, idx = 0;
2,036✔
396
  pr_table_entry_t *e = NULL, *n = NULL;
2,036✔
397

398
  if (tab == NULL ||
4,072✔
399
      key_data == NULL ||
4,072✔
400
      (value_datasz > 0 && value_data == NULL)) {
2,036✔
401
    errno = EINVAL;
1✔
402
    return -1;
1✔
403
  }
404

405
  if (tab->nents == tab->nmaxents) {
2,035✔
406
    /* Table is full. */
407
    errno = ENOSPC;
1✔
408
    return -1;
1✔
409
  }
410

411
  /* Don't forget to add in the random seed data. */
412
  h = tab->keyhash(key_data, key_datasz) + tab->seed;
2,034✔
413

414
  /* The index of the chain to use is the hash value modulo the number
415
   * of chains.
416
   */
417
  idx = h % tab->nchains;
2,034✔
418

419
  /* Allocate a new entry for the given values. */
420
  n = tab_entry_alloc(tab);
4,068✔
421
  n->value_data = value_data;
2,034✔
422
  n->value_datasz = value_datasz;
2,034✔
423
  n->idx = idx;
2,034✔
424

425
  /* Find the current chain entry at this index. */
426
  e = tab->chains[idx];
2,034✔
427
  if (e != NULL) {
2,034✔
428
    pr_table_entry_t *ei;
429

430
    /* There is a chain at this index.  Next step is to see if any entry
431
     * on this chain has the exact same key.  If so, increase the entry ref
432
     * count on that key, otherwise, create a new key.
433
     */
434

435
    for (ei = e; ei; ei = ei->next) {
2,978✔
436
      if (ei->key->hash != h) {
2,987✔
437
        continue;
2,977✔
438
      }
439

440
      /* Hash collision.  Now check if the key data that was hashed
441
       * is identical.  If so, we have multiple values for the same key.
442
       */
443

444
      if (tab->keycmp(ei->key->key_data, ei->key->key_datasz,
10✔
445
          key_data, key_datasz) == 0) {
446

447
        /* Check if this table allows multivalues. */
448
        if (!(tab->flags & PR_TABLE_FL_MULTI_VALUE)) {
10✔
449
          errno = EEXIST;
9✔
450
          return -1;
9✔
451
        }
452

453
        n->key = ei->key;
1✔
454
      }
455
    }
456

457
  } else {
458

459
    /* This new entry becomes the head of this chain. */
460
    tab->chains[idx] = n;
1,909✔
461
  }
462

463
  if (!n->key) {
2,025✔
464
    pr_table_key_t *k;
465

466
    /* Allocate a new key. */
467
    k = tab_key_alloc(tab);
4,048✔
468

469
    k->key_data = (void *) key_data;
2,024✔
470
    k->key_datasz = key_datasz;
2,024✔
471
    k->hash = h;
2,024✔
472
    k->nents = 0;
2,024✔
473

474
    n->key = k;
2,024✔
475
  }
476

477
  tab_entry_insert(tab, n);
2,025✔
478
  return 0;
2,025✔
479
}
480

481
int pr_table_kexists(pr_table_t *tab, const void *key_data, size_t key_datasz) {
2,742✔
482
  unsigned int h, idx;
483
  pr_table_entry_t *head, *ent;
484

485
  if (tab == NULL ||
5,484✔
486
      key_data == NULL) {
2,742✔
487
    errno = EINVAL;
2✔
488
    return -1;
2✔
489
  }
490

491
  if (tab->nents == 0) {
2,740✔
492
    errno = ENOENT;
900✔
493
    return -1;
900✔
494
  }
495

496
  if (tab->flags & PR_TABLE_FL_USE_CACHE) {
1,840✔
497
    /* Has the caller already wanted to lookup this same key previously?
498
     * If so, reuse that lookup if we can.  In this case, "same key" means
499
     * the _exact same pointer_, not identical data.
500
     */
501
    if (tab->cache_ent &&
×
502
        tab->cache_ent->key->key_data == key_data) {
×
503
      return tab->cache_ent->key->nents;
×
504
    }
505
  }
506

507
  /* Don't forget to add in the random seed data. */
508
  h = tab->keyhash(key_data, key_datasz) + tab->seed;
1,840✔
509

510
  idx = h % tab->nchains;
1,840✔
511
  head = tab->chains[idx];
1,840✔
512
  if (head == NULL) {
1,840✔
513
    tab->cache_ent = NULL;
546✔
514
    return 0;
546✔
515
  }
516

517
  for (ent = head; ent; ent = ent->next) {
6✔
518
    if (ent->key == NULL ||
2,594✔
519
        ent->key->hash != h) {
1,297✔
520
      continue;
6✔
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,
1,291✔
525
        key_data, key_datasz) == 0) {
526

527
      if (tab->flags & PR_TABLE_FL_USE_CACHE) {
1,291✔
528
        tab->cache_ent = ent;
×
529
      }
530

531
      return ent->key->nents;
1,291✔
532
    }
533
  }
534

535
  tab->cache_ent = NULL;
3✔
536

537
  errno = EINVAL;
3✔
538
  return 0;
3✔
539
}
540

541
const void *pr_table_kget(pr_table_t *tab, const void *key_data,
9,283✔
542
    size_t key_datasz, size_t *value_datasz) {
543
  unsigned int h = 0;
9,283✔
544
  pr_table_entry_t *head = NULL, *ent = NULL;
9,283✔
545

546
  if (tab == NULL) {
9,283✔
547
    errno = EINVAL;
1✔
548
    return NULL;
1✔
549
  }
550

551
  /* Use a NULL key as a way of rewinding the per-key lookup. */
552
  if (key_data == NULL) {
9,282✔
553
    tab->cache_ent = NULL;
4✔
554
    tab->val_iter_ent = NULL;
4✔
555

556
    errno = ENOENT;
4✔
557
    return NULL;
4✔
558
  }
559

560
  if (tab->nents == 0) {
9,278✔
561
    tab->cache_ent = NULL;
837✔
562
    tab->val_iter_ent = NULL;
837✔
563

564
    errno = ENOENT;
837✔
565
    return NULL;
837✔
566
  }
567

568
  /* Don't forget to add in the random seed data. */
569
  h = tab->keyhash(key_data, key_datasz) + tab->seed;
8,441✔
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 &&
8,441✔
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) &&
8,444✔
580
             tab->cache_ent &&
4✔
581
             tab->cache_ent->key->key_data == key_data) {
1✔
582

583
     /* If the cached lookup entry matches, we'll use it. */
584
     head = tab->cache_ent->next;
1✔
585

586
  } else {
587
    unsigned int idx = h % tab->nchains;
8,440✔
588
    head = tab->chains[idx];
8,440✔
589
  }
590

591
  if (head == NULL) {
8,441✔
592
    tab->cache_ent = NULL;
1,882✔
593
    tab->val_iter_ent = NULL;
1,882✔
594

595
    errno = ENOENT;
1,882✔
596
    return NULL;
1,882✔
597
  }
598

599
  for (ent = head; ent; ent = ent->next) {
88✔
600
    if (ent->key == NULL ||
13,284✔
601
        ent->key->hash != h) {
6,642✔
602
      continue;
88✔
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,
6,554✔
607
        key_data, key_datasz) == 0) {
608

609
      if (tab->flags & PR_TABLE_FL_USE_CACHE) {
6,554✔
610
        tab->cache_ent = ent;
2✔
611
      }
612

613
      if (tab->flags & PR_TABLE_FL_MULTI_VALUE) {
6,554✔
614
        tab->val_iter_ent = ent;
×
615
      }
616

617
      if (value_datasz) {
6,554✔
618
        *value_datasz = ent->value_datasz;
9✔
619
      }
620

621
      return ent->value_data;
6,554✔
622
    }
623
  }
624

625
  tab->cache_ent = NULL;
5✔
626
  tab->val_iter_ent = NULL;
5✔
627

628
  errno = ENOENT;
5✔
629
  return NULL;
5✔
630
}
631

632
const void *pr_table_kremove(pr_table_t *tab, const void *key_data,
81✔
633
    size_t key_datasz, size_t *value_datasz) {
634
  unsigned int h = 0, idx = 0;
81✔
635
  pr_table_entry_t *head = NULL, *ent = NULL;
81✔
636

637
  if (tab == NULL ||
162✔
638
      key_data == NULL) {
81✔
639
    errno = EINVAL;
2✔
640
    return NULL;
2✔
641
  }
642

643
  if (tab->nents == 0) {
79✔
644
    errno = ENOENT;
15✔
645
    return NULL;
15✔
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) &&
65✔
653
      tab->cache_ent &&
2✔
654
      tab->cache_ent->key->key_data == key_data) {
1✔
655
    const void *value_data;
656

657
    value_data = tab->cache_ent->value_data;
1✔
658
    if (value_datasz) {
1✔
659
      *value_datasz = tab->cache_ent->value_datasz;
×
660
    }
661

662
    tab_entry_remove(tab, tab->cache_ent);
1✔
663
    tab_entry_free(tab, tab->cache_ent);
1✔
664
    tab->cache_ent = NULL;
1✔
665

666
    return value_data;
1✔
667
  }
668

669
  /* Don't forget to add in the random seed data. */
670
  h = tab->keyhash(key_data, key_datasz) + tab->seed;
63✔
671

672
  idx = h % tab->nchains;
63✔
673
  head = tab->chains[idx];
63✔
674
  if (head == NULL) {
63✔
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 ||
126✔
683
        ent->key->hash != h) {
63✔
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,
63✔
689
        key_data, key_datasz) == 0) {
690
      const void *value_data;
691

692
      value_data = ent->value_data;
63✔
693
      if (value_datasz) {
63✔
694
        *value_datasz = ent->value_datasz;
1✔
695
      }
696

697
      tab_entry_remove(tab, ent);
63✔
698
      tab_entry_free(tab, ent);
63✔
699
      tab->cache_ent = NULL;
63✔
700

701
      return value_data;
63✔
702
    }
703
  }
704

705
  tab->cache_ent = NULL;
×
706

707
  errno = EINVAL;
×
708
  return NULL;
×
709
}
710

711
int pr_table_kset(pr_table_t *tab, const void *key_data, size_t key_datasz,
1,255✔
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 ||
2,510✔
719
      key_data == NULL ||
2,510✔
720
      (value_datasz > 0 && value_data == NULL)) {
1,255✔
721
    errno = EINVAL;
1✔
722
    return -1;
1✔
723
  }
724

725
  if (tab->nents == 0) {
1,254✔
726
    errno = ENOENT;
1✔
727
    return -1;
1✔
728
  }
729

730
  /* Don't forget to add in the random seed data. */
731
  h = tab->keyhash(key_data, key_datasz) + tab->seed;
1,253✔
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 &&
1,253✔
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) &&
1,253✔
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;
1,253✔
750
    head = tab->chains[idx];
1,253✔
751
  }
752

753
  if (head == NULL) {
1,253✔
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) {
3✔
762
    if (ent->key == NULL ||
2,512✔
763
        ent->key->hash != h) {
1,256✔
764
      continue;
3✔
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,
1,253✔
769
        key_data, key_datasz) == 0) {
770

771
      if (ent->value_data == value_data) {
1,253✔
772
        errno = EEXIST;
×
773
        return -1;
×
774
      }
775

776
      ent->value_data = value_data;
1,253✔
777
      ent->value_datasz = value_datasz;
1,253✔
778

779
      if (tab->flags & PR_TABLE_FL_USE_CACHE) {
1,253✔
780
        tab->cache_ent = ent;
×
781
      }
782

783
      if (tab->flags & PR_TABLE_FL_MULTI_VALUE) {
1,253✔
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

798
int pr_table_add(pr_table_t *tab, const char *key_data, const void *value_data,
1,913✔
799
    size_t value_datasz) {
800

801
  if (tab == NULL ||
3,826✔
802
      key_data == NULL) {
1,913✔
803
    errno = EINVAL;
3✔
804
    return -1;
3✔
805
  }
806

807
  if (value_data &&
3,820✔
808
      value_datasz == 0) {
1,910✔
809
    value_datasz = strlen((char *) value_data) + 1;
29✔
810
  }
811

812
  return pr_table_kadd(tab, key_data, strlen(key_data) + 1, value_data,
1,910✔
813
    value_datasz);
814
}
815

816
int pr_table_add_dup(pr_table_t *tab, const char *key_data,
22✔
817
    const void *value_data, size_t value_datasz) {
818
  void *dup_data;
819

820
  if (tab == NULL ||
44✔
821
      key_data == NULL) {
22✔
822
    errno = EINVAL;
2✔
823
    return -1;
2✔
824
  }
825

826
  if (value_data == NULL) {
20✔
827
    if (value_datasz == 0) {
3✔
828
      return 0;
829
    }
830

831
    errno = EINVAL;
1✔
832
    return -1;
1✔
833
  }
834

835
  if (value_data != NULL &&
17✔
836
      value_datasz == 0) {
17✔
837
    value_datasz = strlen((const char *) value_data) + 1;
15✔
838
  }
839

840
  dup_data = pcalloc(tab->pool, value_datasz);
17✔
841
  memcpy(dup_data, value_data, value_datasz);
17✔
842

843
  return pr_table_add(tab, key_data, dup_data, value_datasz);
17✔
844
}
845

846
pr_table_t *pr_table_nalloc(pool *p, int flags, unsigned int nchains) {
2,067✔
847
  pr_table_t *tab;
848
  pool *tab_pool;
849

850
  if (p == NULL ||
4,134✔
851
      nchains == 0) {
2,067✔
852
    errno = EINVAL;
3✔
853
    return NULL;
3✔
854
  }
855

856
  tab_pool = make_sub_pool(p);
2,064✔
857
  pr_pool_tag(tab_pool, "table pool");
2,064✔
858

859
  tab = pcalloc(tab_pool, sizeof(pr_table_t));
2,064✔
860
  tab->pool = tab_pool;
2,064✔
861
  tab->flags = flags;
2,064✔
862
  tab->nchains = nchains;
2,064✔
863
  tab->chains = pcalloc(tab_pool,
2,064✔
864
    sizeof(pr_table_entry_t *) * tab->nchains);
2,064✔
865

866
  tab->keycmp = key_cmp;
2,064✔
867
  tab->keyhash = key_hash;
2,064✔
868
  tab->entinsert = entry_insert;
2,064✔
869
  tab->entremove = entry_remove;
2,064✔
870

871
  tab->seed = tab_get_seed();
2,064✔
872
  tab->nmaxents = PR_TABLE_DEFAULT_MAX_ENTS;
2,064✔
873

874
  return tab;
2,064✔
875
}
876

877
pr_table_t *pr_table_alloc(pool *p, int flags) {
1,668✔
878
  return pr_table_nalloc(p, flags, PR_TABLE_DEFAULT_NCHAINS);
1,668✔
879
}
880

881
int pr_table_count(pr_table_t *tab) {
2,458✔
882
  if (tab == NULL) {
2,458✔
883
    errno = EINVAL;
1,561✔
884
    return -1;
1,561✔
885
  }
886

887
  return tab->nents;
897✔
888
}
889

890
int pr_table_do(pr_table_t *tab, int (*cb)(const void *key_data,
6✔
891
    size_t key_datasz, const void *value_data, size_t value_datasz,
892
    void *user_data), void *user_data, int flags) {
893
  register unsigned int i;
894

895
  if (tab == NULL ||
12✔
896
      cb == NULL) {
6✔
897
    errno = EINVAL;
2✔
898
    return -1;
2✔
899
  }
900

901
  if (tab->nents == 0) {
4✔
902
    return 0;
903
  }
904

905
  for (i = 0; i < tab->nchains; i++) {
732✔
906
    pr_table_entry_t *ent;
907

908
    ent = tab->chains[i];
733✔
909
    while (ent != NULL) {
1,470✔
910
      pr_table_entry_t *next_ent;
911
      int res;
912

913
      next_ent = ent->next;
5✔
914

915
      if (!handling_signal) {
5✔
916
        pr_signals_handle();
5✔
917
      }
918

919
      res = cb(ent->key->key_data, ent->key->key_datasz, ent->value_data,
5✔
920
        ent->value_datasz, user_data);
921
      if (res < 0 &&
8✔
922
          !(flags & PR_TABLE_DO_FL_ALL)) {
3✔
923
        errno = EPERM;
1✔
924
        return -1;
1✔
925
      }
926

927
      ent = next_ent;
928
    }
929
  }
930

931
  return 0;
932
}
933

934
int pr_table_empty(pr_table_t *tab) {
112✔
935
  register unsigned int i;
936

937
  if (tab == NULL) {
112✔
938
    errno = EINVAL;
1✔
939
    return -1;
1✔
940
  }
941

942
  if (tab->nents == 0) {
111✔
943
    return 0;
944
  }
945

946
  for (i = 0; i < tab->nchains; i++) {
5,377✔
947
    pr_table_entry_t *e;
948

949
    e = tab->chains[i];
5,377✔
950
    while (e != NULL) {
10,778✔
951
      if (!handling_signal) {
24✔
952
        pr_signals_handle();
24✔
953
      }
954

955
      tab_entry_remove(tab, e);
24✔
956
      tab_entry_free(tab, e);
24✔
957

958
      e = tab->chains[i];
24✔
959
    }
960

961
    tab->chains[i] = NULL;
5,377✔
962
  }
963

964
  return 0;
965
}
966

967
int pr_table_exists(pr_table_t *tab, const char *key_data) {
2,728✔
968
  if (tab == NULL ||
5,456✔
969
      key_data == NULL) {
2,728✔
970
    errno = EINVAL;
3✔
971
    return -1;
3✔
972
  }
973

974
  return pr_table_kexists(tab, key_data, strlen(key_data) + 1);
2,725✔
975
}
976

977
int pr_table_free(pr_table_t *tab) {
110✔
978

979
  if (tab == NULL) {
110✔
980
    errno = EINVAL;
1✔
981
    return -1;
1✔
982
  }
983

984
  if (tab->nents != 0) {
109✔
985
    errno = EPERM;
1✔
986
    return -1;
1✔
987
  }
988

989
  destroy_pool(tab->pool);
108✔
990
  return 0;
108✔
991
}
992

993
const void *pr_table_get(pr_table_t *tab, const char *key_data,
10,861✔
994
    size_t *value_datasz) {
995
  size_t key_datasz = 0;
10,861✔
996

997
  if (tab == NULL) {
10,861✔
998
    errno = EINVAL;
1,604✔
999
    return NULL;
1,604✔
1000
  }
1001

1002
  if (key_data) {
9,257✔
1003
    key_datasz = strlen(key_data) + 1;
9,253✔
1004
  }
1005

1006
  return pr_table_kget(tab, key_data, key_datasz, value_datasz);
9,257✔
1007
}
1008

1009
const void *pr_table_knext(pr_table_t *tab, size_t *key_datasz) {
24✔
1010
  pr_table_entry_t *ent, *prev;
1011

1012
  if (tab == NULL) {
24✔
1013
    errno = EINVAL;
1✔
1014
    return NULL;
1✔
1015
  }
1016

1017
  prev = tab->tab_iter_ent;
23✔
1018

1019
  ent = tab_entry_next(tab);
23✔
1020
  while (ent != NULL) {
46✔
1021
    if (!handling_signal) {
12✔
1022
      pr_signals_handle();
12✔
1023
    }
1024

1025
    if (prev &&
13✔
1026
        ent->key == prev->key) {
1✔
1027
      ent = tab_entry_next(tab);
×
1028
      continue;
×
1029
    }
1030

1031
    break;
1032
  }
1033

1034
  if (ent == NULL) {
23✔
1035
    errno = EPERM;
11✔
1036
    return NULL;
11✔
1037
  }
1038

1039
  if (key_datasz != NULL) {
12✔
1040
    *key_datasz = ent->key->key_datasz;
3✔
1041
  }
1042

1043
  return ent->key->key_data;
12✔
1044
}
1045

1046
const void *pr_table_next(pr_table_t *tab) {
19✔
1047
  return pr_table_knext(tab, NULL);
19✔
1048
}
1049

1050
const void *pr_table_remove(pr_table_t *tab, const char *key_data,
79✔
1051
    size_t *value_datasz) {
1052

1053
  if (tab == NULL ||
158✔
1054
      key_data == NULL) {
79✔
1055
    errno = EINVAL;
8✔
1056
    return NULL;
8✔
1057
  }
1058

1059
  return pr_table_kremove(tab, key_data, strlen(key_data) + 1, value_datasz);
71✔
1060
}
1061

1062
int pr_table_rewind(pr_table_t *tab) {
26✔
1063
  if (tab == NULL) {
26✔
1064
    errno = EINVAL;
1✔
1065
    return -1;
1✔
1066
  }
1067

1068
  tab->tab_iter_ent = NULL;
25✔
1069
  return 0;
25✔
1070
}
1071

1072
int pr_table_set(pr_table_t *tab, const char *key_data, const void *value_data,
1,257✔
1073
    size_t value_datasz) {
1074

1075
  if (tab == NULL ||
2,514✔
1076
      key_data == NULL) {
1,257✔
1077
    errno = EINVAL;
2✔
1078
    return -1;
2✔
1079
  }
1080

1081
  if (value_data &&
2,510✔
1082
      value_datasz == 0) {
1,255✔
1083
    value_datasz = strlen((const char *) value_data) + 1;
1✔
1084
  }
1085

1086
  return pr_table_kset(tab, key_data, strlen(key_data) + 1, value_data,
1,255✔
1087
    value_datasz);
1088
}
1089

1090
int pr_table_ctl(pr_table_t *tab, int cmd, void *arg) {
89✔
1091

1092
  if (tab == NULL) {
89✔
1093
    errno = EINVAL;
1✔
1094
    return -1;
1✔
1095
  }
1096

1097
  /* Set control operations can only be performed on an empty table. */
1098
  if (tab->nents != 0) {
88✔
1099
    errno = EPERM;
3✔
1100
    return -1;
3✔
1101
  }
1102

1103
  switch (cmd) {
85✔
1104
    case PR_TABLE_CTL_SET_KEY_HASH:
17✔
1105
      tab->keyhash = arg ?
17✔
1106
        (unsigned int (*)(const void *, size_t)) arg :
17✔
1107
        (unsigned int (*)(const void *, size_t)) key_hash;
1108
      return 0;
17✔
1109

1110
    case PR_TABLE_CTL_SET_FLAGS:
2✔
1111
      if (arg == NULL) {
2✔
1112
        errno = EINVAL;
1✔
1113
        return -1;
1✔
1114
      }
1115

1116
      tab->flags = *((unsigned long *) arg);
1✔
1117
      return 0;
1✔
1118

1119
    case PR_TABLE_CTL_SET_KEY_CMP:
16✔
1120
      tab->keycmp = arg ?
16✔
1121
        (int (*)(const void *, size_t, const void *, size_t)) arg :
16✔
1122
        (int (*)(const void *, size_t, const void *, size_t)) key_cmp;
1123
      return 0;
16✔
1124

1125
    case PR_TABLE_CTL_SET_ENT_INSERT:
1✔
1126
      tab->entinsert = arg ?
1✔
1127
        (void (*)(pr_table_entry_t **, pr_table_entry_t *)) arg :
1✔
1128
        (void (*)(pr_table_entry_t **, pr_table_entry_t *)) entry_insert;
1129
      return 0;
1✔
1130

1131
    case PR_TABLE_CTL_SET_ENT_REMOVE:
1✔
1132
      tab->entremove = arg ?
1✔
1133
        (void (*)(pr_table_entry_t **, pr_table_entry_t *)) arg :
1✔
1134
        (void (*)(pr_table_entry_t **, pr_table_entry_t *)) entry_remove;
1135
      return 0;
1✔
1136

1137
    case PR_TABLE_CTL_SET_NCHAINS: {
3✔
1138
      unsigned int new_nchains;
1139

1140
      if (arg == NULL) {
3✔
1141
        errno = EINVAL;
1✔
1142
        return -1;
1✔
1143
      }
1144

1145
      new_nchains = *((unsigned int *) arg);
2✔
1146
      if (new_nchains == 0) {
2✔
1147
        errno = EINVAL;
1✔
1148
        return -1;
1✔
1149
      }
1150

1151
      tab->nchains = new_nchains;
1✔
1152

1153
      /* Note: by not freeing the memory of the previously allocated
1154
       * chains, this constitutes a minor leak of the table's memory pool.
1155
       */
1156
      tab->chains = pcalloc(tab->pool,
1✔
1157
        sizeof(pr_table_entry_t *) * tab->nchains);
1✔
1158
      return 0;
1✔
1159
    }
1160

1161
    case PR_TABLE_CTL_SET_MAX_ENTS: {
44✔
1162
      unsigned int nmaxents;
1163

1164
      nmaxents = *((unsigned int *) arg);
44✔
1165

1166
      if (nmaxents == 0) {
44✔
1167
        errno = EINVAL;
1✔
1168
        return -1;
1✔
1169
      }
1170

1171
      /* If the given maximum is less than the number of entries currently
1172
       * in the table, we have to return an error; we do not want to get
1173
       * into the business of table truncation just yet.
1174
       */
1175
      if (tab->nents > nmaxents) {
1176
        errno = EPERM;
1177
        return -1;
1178
      }
1179

1180
      tab->nmaxents = nmaxents;
43✔
1181
      return 0;
43✔
1182
    }
1183

1184
    default:
1✔
1185
      errno = EINVAL;
1✔
1186
  }
1187

1188
  return -1;
1✔
1189
}
1190

1191
void *pr_table_pcalloc(pr_table_t *tab, size_t sz) {
43✔
1192
  if (tab == NULL ||
86✔
1193
      sz == 0) {
43✔
1194
    errno = EINVAL;
3✔
1195
    return NULL;
3✔
1196
  }
1197

1198
  return pcalloc(tab->pool, sz);
40✔
1199
}
1200

1201
static void table_printf(const char *fmt, ...) {
2✔
1202
  char buf[PR_TUNABLE_BUFFER_SIZE+1];
1203
  va_list msg;
1204

1205
  memset(buf, '\0', sizeof(buf));
2✔
1206
  va_start(msg, fmt);
2✔
1207
  pr_vsnprintf(buf, sizeof(buf)-1, fmt, msg);
2✔
1208
  va_end(msg);
2✔
1209

1210
  buf[sizeof(buf)-1] = '\0';
2✔
1211
  pr_trace_msg(trace_channel, 19, "dump: %s", buf);
2✔
1212
}
2✔
1213

1214
float pr_table_load(pr_table_t *tab) {
2✔
1215
  float load_factor;
1216

1217
  if (tab == NULL) {
2✔
1218
    errno = EINVAL;
1✔
1219
    return -1.0;
1✔
1220
  }
1221

1222
  load_factor = (tab->nents / tab->nchains);
1✔
1223
  return load_factor;
1✔
1224
}
1225

1226
static int tab_copy_cb(const void *key_data, size_t key_datasz,
×
1227
    const void *value_data, size_t value_datasz, void *user_data) {
1228
  int res;
1229
  pr_table_t *dst_tab;
1230

1231
  dst_tab = user_data;
×
1232

1233
  res = pr_table_kexists(dst_tab, key_data, key_datasz);
×
1234
  if (res > 0) {
×
1235
    res = pr_table_kset(dst_tab, key_data, key_datasz, value_data,
×
1236
      value_datasz);
1237

1238
  } else {
1239
    res = pr_table_kadd(dst_tab, key_data, key_datasz, value_data,
×
1240
      value_datasz);
1241
  }
1242

1243
  return res;
×
1244
}
1245

1246
int pr_table_copy(pr_table_t *dst_tab, pr_table_t *src_tab, int flags) {
×
1247
  int res;
1248

1249
  if (dst_tab == NULL ||
×
1250
      src_tab == NULL) {
×
1251
    errno = EINVAL;
×
1252
    return -1;
×
1253
  }
1254

1255
  /* Future flags may support a DEEP_COPY flag, which would use the
1256
   * dst_tab->pool.
1257
   */
1258
  res = pr_table_do(src_tab, tab_copy_cb, dst_tab, PR_TABLE_DO_FL_ALL);
×
1259
  return res;
×
1260
}
1261

1262
void pr_table_dump(void (*dumpf)(const char *fmt, ...), pr_table_t *tab) {
5✔
1263
  register unsigned int i;
1264

1265
  if (tab == NULL) {
5✔
1266
    return;
1267
  }
1268

1269
  if (dumpf == NULL) {
3✔
1270
    dumpf = table_printf;
1✔
1271
  }
1272

1273
  if (tab->flags == 0) {
3✔
1274
    dumpf("%s", "[table flags]: None");
3✔
1275

1276
  } else {
1277
    if ((tab->flags & PR_TABLE_FL_MULTI_VALUE) &&
×
1278
        (tab->flags & PR_TABLE_FL_USE_CACHE)) {
1279
      dumpf("%s", "[table flags]: MultiValue, UseCache");
×
1280

1281
    } else {
1282
      if (tab->flags & PR_TABLE_FL_MULTI_VALUE) {
×
1283
        dumpf("%s", "[table flags]: MultiValue");
×
1284
      }
1285

1286
      if (tab->flags & PR_TABLE_FL_USE_CACHE) {
×
1287
        dumpf("%s", "[table flags]: UseCache");
×
1288
      }
1289
    }
1290
  }
1291

1292
  if (tab->nents == 0) {
3✔
1293
    dumpf("[empty table]");
3✔
1294
    return;
3✔
1295
  }
1296

1297
  dumpf("[table count]: %u", tab->nents);
×
1298
  for (i = 0; i < tab->nchains; i++) {
×
1299
    register unsigned int j = 0;
×
1300
    pr_table_entry_t *ent = tab->chains[i];
×
1301

1302
    while (ent) {
×
1303
      if (!handling_signal) {
×
1304
        pr_signals_handle();
×
1305
      }
1306

1307
      dumpf("[hash %u (%u chains) chain %u#%u] '%s' => '%s' (%u)",
×
1308
        ent->key->hash, tab->nchains, i, j++, ent->key->key_data,
×
1309
        ent->value_data, ent->value_datasz);
1310
      ent = ent->next;
×
1311
    }
1312
  }
1313
}
1314

1315
int table_handling_signal(int do_handle) {
×
1316
  if (do_handle == TRUE ||
×
1317
      do_handle == FALSE) {
1318
    handling_signal = do_handle;
×
1319
    return 0;
×
1320
  }
1321

1322
  errno = EINVAL;
×
1323
  return -1;
×
1324
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc