source: npl/mailserver/dspam/dspam-3.10.2/src/hash_drv.c @ c5c522c

gcc484ntopperl-5.22
Last change on this file since c5c522c was c5c522c, checked in by Edwin Eefting <edwin@datux.nl>, 8 years ago

initial commit, transferred from cleaned syn3 svn tree

  • Property mode set to 100644
File size: 33.1 KB
Line 
1/* $Id: hash_drv.c,v 1.296 2011/06/28 00:13:48 sbajic Exp $ */
2
3/*
4 DSPAM
5 COPYRIGHT (C) 2002-2012 DSPAM PROJECT
6
7 This program is free software: you can redistribute it and/or modify
8 it under the terms of the GNU Affero General Public License as
9 published by the Free Software Foundation, either version 3 of the
10 License, or (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU Affero General Public License for more details.
16
17 You should have received a copy of the GNU Affero General Public License
18 along with this program.  If not, see <http://www.gnu.org/licenses/>.
19
20*/
21
22/*
23 * hash_drv.c - hash-based storage driver
24 *              mmap'd flat-file storage for fast storage
25 *              inspired by crm114 sparse spectra algorithm
26 *
27 * DESCRIPTION
28 *   This driver uses a random access file for storage. It is exceptionally fast
29 *   and does not require any third-party dependencies. The auto-extend
30 *   functionality allows the file to grow as needed.
31 */
32
33#define READ_ATTRIB(A)     _ds_read_attribute(CTX->config->attributes, A)
34#define MATCH_ATTRIB(A, B) _ds_match_attribute(CTX->config->attributes, A, B)
35
36#ifdef HAVE_CONFIG_H
37#include <auto-config.h>
38#endif
39
40#include <string.h>
41#include <sys/types.h>
42#include <sys/mman.h>
43#include <sys/stat.h>
44#include <sys/uio.h>
45#include <dirent.h>
46#include <unistd.h>
47#include <errno.h>
48#include <stdlib.h>
49#include <stdio.h>
50#include <fcntl.h>
51#include <signal.h>
52
53#ifdef TIME_WITH_SYS_TIME
54#   include <sys/time.h>
55#   include <time.h>
56#else
57#   ifdef HAVE_SYS_TIME_H
58#       include <sys/time.h>
59#   else
60#       include <time.h>
61#   endif
62#endif
63
64#include "storage_driver.h"
65#include "config_shared.h"
66#include "hash_drv.h"
67#include "libdspam.h"
68#include "config.h"
69#include "error.h"
70#include "language.h"
71#include "util.h"
72
73int
74dspam_init_driver (DRIVER_CTX *DTX)
75{
76  DSPAM_CTX *CTX;
77  char *HashConcurrentUser;
78#ifdef DAEMON
79   unsigned long connection_cache = 1;
80#endif
81
82  if (DTX == NULL)
83    return 0;
84
85  CTX = DTX->CTX;
86  HashConcurrentUser = READ_ATTRIB("HashConcurrentUser");
87
88#ifdef DAEMON
89
90  /*
91   *  Stateful concurrent hash databases are preloaded into memory and
92   *  shared using a reader-writer lock. At the present moment, only a single
93   *  user can be loaded into any instance of the daemon, so it is only useful
94   *  if you are running with a system-wide filtering user.
95   */
96
97  if (DTX->flags & DRF_STATEFUL) {
98    char filename[MAX_FILENAME_LENGTH];
99    hash_drv_map_t map;
100    unsigned long hash_rec_max = HASH_REC_MAX;
101    unsigned long max_seek     = HASH_SEEK_MAX;
102    unsigned long max_extents  = 0;
103    unsigned long extent_size  = HASH_EXTENT_MAX;
104    int pctincrease = 0;
105    int flags = HMAP_AUTOEXTEND;
106    int ret;
107    unsigned long i;
108
109    if (READ_ATTRIB("HashConnectionCache") && !HashConcurrentUser)
110      connection_cache = strtol(READ_ATTRIB("HashConnectionCache"), NULL, 0);
111
112    DTX->connection_cache = connection_cache;
113
114    if (READ_ATTRIB("HashRecMax"))
115      hash_rec_max = strtol(READ_ATTRIB("HashRecMax"), NULL, 0);
116
117    if (READ_ATTRIB("HashExtentSize"))
118      extent_size = strtol(READ_ATTRIB("HashExtentSize"), NULL, 0);
119
120    if (READ_ATTRIB("HashMaxExtents"))
121      max_extents = strtol(READ_ATTRIB("HashMaxExtents"), NULL, 0);
122
123    if (!MATCH_ATTRIB("HashAutoExtend", "on"))
124      flags = 0;
125
126    if (READ_ATTRIB("HashPctIncrease")) {
127      pctincrease = atoi(READ_ATTRIB("HashPctIncrease"));
128      if (pctincrease > 100) {
129          LOG(LOG_ERR, "HashPctIncrease out of range; ignoring");
130          pctincrease = 0;
131      }
132    }
133
134    if (READ_ATTRIB("HashMaxSeek"))
135      max_seek = strtol(READ_ATTRIB("HashMaxSeek"), NULL, 0);
136
137    /* Connection array (just one single connection for hash_drv) */
138    DTX->connections = calloc(1, sizeof(struct _ds_drv_connection *) * connection_cache);
139    if (DTX->connections == NULL)
140      goto memerr;
141
142    /* Initialize Connections */
143    for(i=0;i<connection_cache;i++) {
144      DTX->connections[i] = calloc(1, sizeof(struct _ds_drv_connection));
145      if (DTX->connections[i] == NULL)
146        goto memerr;
147
148      /* Our connection's storage structure */
149      if (HashConcurrentUser) {
150        DTX->connections[i]->dbh = calloc(1, sizeof(struct _hash_drv_map));
151        if (DTX->connections[i]->dbh == NULL)
152          goto memerr;
153        pthread_rwlock_init(&DTX->connections[i]->rwlock, NULL);
154      } else {
155        DTX->connections[i]->dbh = NULL;
156        pthread_mutex_init(&DTX->connections[i]->lock, NULL);
157      }
158    }
159
160    /* Load concurrent database into resident memory */
161    if (HashConcurrentUser) {
162      map = (hash_drv_map_t) DTX->connections[0]->dbh;
163
164      /* Tell the server our connection lock will be reader/writer based */
165      if (!(DTX->flags & DRF_RWLOCK))
166        DTX->flags |= DRF_RWLOCK;
167
168      _ds_userdir_path(filename, DTX->CTX->home, HashConcurrentUser, "css");
169      _ds_prepare_path_for(filename);
170      LOGDEBUG("preloading %s into memory via mmap()", filename);
171      ret = _hash_drv_open(filename, map, hash_rec_max,
172          max_seek, max_extents, extent_size, pctincrease, flags);
173
174      if (ret) {
175        LOG(LOG_CRIT, "_hash_drv_open(%s) failed on error %d: %s",
176                      filename, ret, strerror(errno));
177        free(DTX->connections[0]->dbh);
178        free(DTX->connections[0]);
179        free(DTX->connections);
180        return EFAILURE;
181      }
182    }
183  }
184#endif
185
186  return 0;
187
188#ifdef DAEMON
189memerr:
190  if (DTX) {
191    if (DTX->connections) {
192      unsigned long i;
193      for(i=0;i<connection_cache;i++) {
194        if (DTX->connections[i])
195          free(DTX->connections[i]->dbh);
196        free(DTX->connections[i]);
197      }
198    }
199    free(DTX->connections);
200  }
201  LOG(LOG_CRIT, ERR_MEM_ALLOC);
202  return EUNKNOWN;
203#endif
204 
205}
206
207int
208dspam_shutdown_driver (DRIVER_CTX *DTX)
209{
210#ifdef DAEMON
211  DSPAM_CTX *CTX;
212
213  if (DTX && DTX->CTX) {
214    char *HashConcurrentUser;
215    CTX = DTX->CTX;
216    HashConcurrentUser = READ_ATTRIB("HashConcurrentUser");
217
218    if (DTX->flags & DRF_STATEFUL) {
219      int connection_cache = 1;
220
221      if (READ_ATTRIB("HashConnectionCache") && !HashConcurrentUser)
222        connection_cache = strtol(READ_ATTRIB("HashConnectionCache"), NULL, 0);
223
224      LOGDEBUG("unloading hash database from memory");
225      if (DTX->connections) {
226        int i;
227        for(i=0;i<connection_cache;i++) {
228          LOGDEBUG("unloading connection object %d", i);
229          if (DTX->connections[i]) {
230            if (!HashConcurrentUser) {
231              pthread_mutex_destroy(&DTX->connections[i]->lock);
232            }
233            else {
234              pthread_rwlock_destroy(&DTX->connections[i]->rwlock);
235              hash_drv_map_t map = (hash_drv_map_t) DTX->connections[i]->dbh;
236              if (map)
237                _hash_drv_close(map);
238            }
239            free(DTX->connections[i]->dbh);
240            free(DTX->connections[i]);
241          }
242        }
243        free(DTX->connections);
244      }
245    }
246  }
247#endif
248
249  return 0;
250}
251
252int
253_hash_drv_lock_get (
254  DSPAM_CTX *CTX,
255  struct _hash_drv_storage *s,
256  const char *username)
257{
258  char filename[MAX_FILENAME_LENGTH];
259  int r;
260
261  _ds_userdir_path(filename, CTX->home, username, "lock");
262  _ds_prepare_path_for(filename);
263
264  s->lock = fopen(filename, "a");
265  if (s->lock == NULL) {
266    LOG(LOG_ERR, ERR_IO_FILE_WRITE, filename, strerror(errno));
267    return EFAILURE;
268  }
269  r = _ds_get_fcntl_lock(fileno(s->lock));
270  if (r) {
271    fclose(s->lock);
272    LOG(LOG_ERR, ERR_IO_LOCK, filename, r, strerror(errno));
273  }
274  return r;
275}
276
277int
278_hash_drv_lock_free (
279  struct _hash_drv_storage *s,
280  const char *username)
281{
282  int r;
283
284  if (username == NULL)
285    return 0;
286
287  r = _ds_free_fcntl_lock(fileno(s->lock));
288  if (!r) {
289    fclose(s->lock);
290  } else {
291    LOG(LOG_ERR, ERR_IO_LOCK_FREE, username, r, strerror(errno));
292  }
293
294  return r;
295}
296
297FILE*
298_hash_tools_lock_get (const char *cssfilename)
299{
300  char filename[MAX_FILENAME_LENGTH];
301  char *pPeriod;
302  int r;
303  FILE* lockfile = NULL;
304
305  if (cssfilename == NULL)
306    return NULL;
307  pPeriod = strrchr(cssfilename, '.');
308  if (pPeriod == NULL || strcmp(pPeriod + 1, "css") || (size_t)(pPeriod - cssfilename + 5) >= sizeof(filename))
309    return NULL;
310  strncpy(filename, cssfilename, pPeriod - cssfilename + 1);
311  strcpy(filename + (pPeriod - cssfilename + 1), "lock");
312  _ds_prepare_path_for(filename);
313
314  lockfile = fopen(filename, "a");
315  if (lockfile == NULL) {
316    LOG(LOG_ERR, ERR_IO_FILE_OPEN, filename, strerror(errno));
317    return NULL;
318  }
319  r = _ds_get_fcntl_lock(fileno(lockfile));
320  if (r) {
321    fclose(lockfile);
322        lockfile = NULL;
323    LOG(LOG_ERR, ERR_IO_LOCK, filename, r, strerror(errno));
324  }
325  return lockfile;
326}
327
328int
329_hash_tools_lock_free (
330  const char *cssfilename,
331  FILE* lockfile)
332{
333  int r;
334
335  if (cssfilename == NULL || lockfile == NULL)
336    return 0;
337
338  r = _ds_free_fcntl_lock(fileno(lockfile));
339  if (!r) {
340    fclose(lockfile);
341  } else {
342    LOG(LOG_ERR, ERR_IO_LOCK_FREE, cssfilename, r, strerror(errno));
343  }
344
345  return r;
346}
347
348int _hash_drv_open(
349  const char *filename,
350  hash_drv_map_t map,
351  unsigned long recmaxifnew,
352  unsigned long max_seek,
353  unsigned long max_extents,
354  unsigned long extent_size,
355  int pctincrease,
356  int flags)
357{
358  struct _hash_drv_header header;
359  int open_flags = O_RDWR;
360  int mmap_flags = PROT_READ + PROT_WRITE;
361  FILE *f;
362
363  map->fd = open(filename, open_flags);
364
365  /*
366   *  Create a new hash database if desired. The record count written in the
367   *  first segment will be recmaxifnew. Once the file is created, it's then
368   *  mmap()'d into memory as usual.
369   */
370
371  if (map->fd < 0 && recmaxifnew) {
372    struct _hash_drv_spam_record rec;
373    unsigned long i;
374
375    memset(&header, 0, sizeof(struct _hash_drv_header));
376    memset(&rec, 0, sizeof(struct _hash_drv_spam_record));
377
378    header.hash_rec_max = recmaxifnew;
379
380    f = fopen(filename, "w");
381    if (!f) {
382      LOG(LOG_ERR, ERR_IO_FILE_WRITE, filename, strerror(errno));
383      return EFILE;
384    }
385
386    if(fwrite(&header, sizeof(struct _hash_drv_header), 1, f)!=1)
387      goto WRITE_ERROR;
388    for(i=0;i<header.hash_rec_max;i++)
389      if(fwrite(&rec, sizeof(struct _hash_drv_spam_record), 1, f)!=1)
390        goto WRITE_ERROR;
391    fclose(f);
392    map->fd = open(filename, open_flags);
393  }
394
395  if (map->fd < 0) {
396    LOG(LOG_ERR, ERR_IO_FILE_WRITE, filename, strerror(errno));
397    return EFILE;
398  }
399
400  map->header = malloc(sizeof(struct _hash_drv_header));
401  if (map->header == NULL) {
402    LOG(LOG_CRIT, ERR_MEM_ALLOC);
403    close(map->fd);
404    map->addr = 0;
405    return EFAILURE;
406  }
407
408  if (read(map->fd, map->header, sizeof(struct _hash_drv_header))
409        != sizeof(struct _hash_drv_header)) {
410    free(map->header);
411    close(map->fd);
412    return EFAILURE;
413  }
414  map->file_len = lseek(map->fd, 0, SEEK_END);
415
416  map->addr = mmap(NULL, map->file_len, mmap_flags, MAP_SHARED, map->fd, 0);
417  if (map->addr == MAP_FAILED) {
418    free(map->header);
419    close(map->fd);
420    map->addr = 0;
421    return EFAILURE;
422  }
423
424  strlcpy(map->filename, filename, MAX_FILENAME_LENGTH);
425  map->max_seek    = max_seek;
426  map->max_extents = max_extents;
427  map->extent_size = extent_size;
428  map->pctincrease = pctincrease;
429  map->flags       = flags;
430
431  return 0;
432
433WRITE_ERROR:
434  fclose(f);
435  unlink(filename);
436  LOG(LOG_ERR, ERR_IO_FILE_WRITING, filename, strerror(errno));
437  return EFILE;
438}
439
440int
441_hash_drv_close(hash_drv_map_t map) {
442  struct _hash_drv_header header;
443  int r;
444
445  if (!map->addr)
446    return EINVAL;
447
448  memcpy(&header, map->header, sizeof(struct _hash_drv_header));
449
450  r = munmap(map->addr, map->file_len);
451  if (r) {
452    LOG(LOG_WARNING, "munmap failed on error %d: %s", r, strerror(errno));
453  }
454
455  lseek (map->fd, 0, SEEK_SET);
456  r = write (map->fd, &header, sizeof(struct _hash_drv_header));
457  if (r < 0) {
458    LOG(LOG_WARNING, "write failed on error %d: %s", r, strerror(errno));
459  }
460  close(map->fd);
461
462  map->addr = 0;
463  free(map->header);
464
465  return r;
466}
467
468
469int
470_ds_init_storage (DSPAM_CTX * CTX, void *dbh)
471{
472  struct _hash_drv_storage *s = NULL;
473  hash_drv_map_t map = NULL;
474
475  if (CTX == NULL)
476    return EINVAL;
477
478  if (!CTX->home) {
479    LOG(LOG_ERR, ERR_AGENT_DSPAM_HOME);
480    return EINVAL;
481  }
482
483  if (CTX->flags & DSF_MERGED) {
484    LOG(LOG_ERR, ERR_DRV_NO_MERGED);
485    return EINVAL;
486  }
487
488  if (CTX->storage)
489    return EINVAL;
490
491  /* Persistent driver storage */
492
493  s = calloc (1, sizeof (struct _hash_drv_storage));
494  if (s == NULL)
495  {
496    LOG(LOG_CRIT, ERR_MEM_ALLOC);
497    return EUNKNOWN;
498  }
499
500  /* If running in HashConcurrentUser mode, use existing hash mapping */
501
502  if (dbh) {
503    map = dbh;
504    s->dbh_attached = 1;
505  } else {
506    map = calloc(1, sizeof(struct _hash_drv_map));
507    if (!map) {
508      LOG(LOG_CRIT, ERR_MEM_ALLOC);
509      free(s);
510      return EUNKNOWN;
511    }
512    s->dbh_attached = 0;
513  }
514
515  s->map = map;
516
517  /* Mapping defaults */
518
519  s->hash_rec_max = HASH_REC_MAX;
520  s->max_seek     = HASH_SEEK_MAX;
521  s->max_extents  = 0;
522  s->extent_size  = HASH_EXTENT_MAX;
523  s->pctincrease  = 0;
524  s->flags        = HMAP_AUTOEXTEND;
525
526  if (READ_ATTRIB("HashRecMax"))
527    s->hash_rec_max = strtol(READ_ATTRIB("HashRecMax"), NULL, 0);
528
529  if (READ_ATTRIB("HashExtentSize"))
530    s->extent_size = strtol(READ_ATTRIB("HashExtentSize"), NULL, 0);
531
532  if (READ_ATTRIB("HashMaxExtents"))
533    s->max_extents = strtol(READ_ATTRIB("HashMaxExtents"), NULL, 0);
534
535  if (!MATCH_ATTRIB("HashAutoExtend", "on"))
536    s->flags = 0;
537
538  if (READ_ATTRIB("HashPctIncrease")) {
539    s->pctincrease = atoi(READ_ATTRIB("HashPctIncrease"));
540    if (s->pctincrease > 100) {
541        LOG(LOG_ERR, "HashPctIncrease out of range; ignoring");
542        s->pctincrease = 0;
543    }
544  }
545
546  if (READ_ATTRIB("HashMaxSeek"))
547    s->max_seek = strtol(READ_ATTRIB("HashMaxSeek"), NULL, 0);
548
549  if (!dbh && CTX->username != NULL)
550  {
551    char db[MAX_FILENAME_LENGTH];
552    int lock_result;
553    int ret;
554
555    if (CTX->group == NULL)
556      _ds_userdir_path(db, CTX->home, CTX->username, "css");
557    else
558      _ds_userdir_path(db, CTX->home, CTX->group, "css");
559
560    lock_result = _hash_drv_lock_get (CTX, s,
561      (CTX->group) ? CTX->group : CTX->username);
562    if (lock_result < 0)
563      goto BAIL;
564
565    ret = _hash_drv_open(db, s->map, s->hash_rec_max, s->max_seek,
566        s->max_extents, s->extent_size, s->pctincrease, s->flags);
567
568    if (ret) {
569      _hash_drv_close(s->map);
570      free(s);
571      return EFAILURE;
572    }
573  }
574
575  CTX->storage = s;
576  s->dir_handles = nt_create (NT_INDEX);
577
578  if (_hash_drv_get_spamtotals (CTX))
579  {
580    LOGDEBUG ("unable to load totals.  using zero values.");
581    memset (&CTX->totals, 0, sizeof (struct _ds_spam_totals));
582  }
583
584  return 0;
585
586BAIL:
587  free(s);
588  return EFAILURE;
589}
590
591int
592_ds_shutdown_storage (DSPAM_CTX * CTX)
593{
594  struct _hash_drv_storage *s;
595  struct nt_node *node_nt;
596  struct nt_c c_nt;
597
598  if (!CTX || !CTX->storage)
599    return EINVAL;
600
601  s  = (struct _hash_drv_storage *) CTX->storage;
602
603  /* Close open file handles to directories (iteration functions) */
604
605  node_nt = c_nt_first (s->dir_handles, &c_nt);
606  while (node_nt != NULL)
607  {
608    DIR *dir;
609    dir = (DIR *) node_nt->ptr;
610    closedir (dir);
611    node_nt = c_nt_next (s->dir_handles, &c_nt);
612  }
613  nt_destroy (s->dir_handles);
614
615  if (CTX->operating_mode != DSM_CLASSIFY)
616    _hash_drv_set_spamtotals (CTX);
617
618  /* Close connection to hash database only if we're not concurrent */
619
620  if (!s->dbh_attached) {
621    _hash_drv_close(s->map);
622    free(s->map);
623    int lock_result =
624      _hash_drv_lock_free (s, (CTX->group) ? CTX->group : CTX->username);
625    if (lock_result < 0)
626      return EUNKNOWN;
627  }
628
629  free (CTX->storage);
630  CTX->storage = NULL;
631
632  return 0;
633}
634
635int
636_hash_drv_get_spamtotals (DSPAM_CTX * CTX)
637{
638  struct _hash_drv_storage *s = (struct _hash_drv_storage *) CTX->storage;
639
640  if (s->map->addr == 0)
641    return EINVAL;
642  /* Totals are loaded straight from the hash header */
643  memcpy(&CTX->totals, &s->map->header->totals, sizeof(struct _ds_spam_totals));
644  return 0;
645}
646
647int
648_hash_drv_set_spamtotals (DSPAM_CTX * CTX)
649{
650  struct _hash_drv_storage *s = (struct _hash_drv_storage *) CTX->storage;
651
652  if (s->map->addr == NULL)
653    return EINVAL;
654  /* Totals are stored into the hash header */
655  memcpy(&s->map->header->totals, &CTX->totals, sizeof(struct _ds_spam_totals));
656  return 0;
657}
658
659int
660_ds_getall_spamrecords (DSPAM_CTX * CTX, ds_diction_t diction)
661{
662  ds_term_t ds_term;
663  ds_cursor_t ds_c;
664  struct _ds_spam_stat stat;
665  struct _ds_spam_stat *p_stat = &stat;
666  int ret = 0, x = 0;
667
668  if (diction == NULL || CTX == NULL)
669    return EINVAL;
670
671  ds_c = ds_diction_cursor(diction);
672  ds_term = ds_diction_next(ds_c);
673  while(ds_term)
674  {
675    ds_term->s.spam_hits = 0;
676    ds_term->s.innocent_hits = 0;
677    ds_term->s.offset = 0;
678    x = _ds_get_spamrecord (CTX, ds_term->key, p_stat);
679    if (!x)
680      ds_diction_setstat(diction, ds_term->key, p_stat);
681    else if (x != EFAILURE)
682      ret = x;
683
684    ds_term = ds_diction_next(ds_c);
685  }
686  ds_diction_close(ds_c);
687
688  if (ret) {
689    LOGDEBUG("_ds_getall_spamtotals returning %d", ret);
690  }
691
692  return ret;
693}
694
695int
696_ds_setall_spamrecords (DSPAM_CTX * CTX, ds_diction_t diction)
697{
698  ds_term_t ds_term;
699  ds_cursor_t ds_c;
700  int ret = EUNKNOWN;
701
702  if (diction == NULL || CTX == NULL)
703    return EINVAL;
704
705  if (CTX->operating_mode == DSM_CLASSIFY &&
706        (CTX->training_mode != DST_TOE ||
707          (diction->whitelist_token == 0 && (!(CTX->flags & DSF_NOISE)))))
708  {
709    return 0;
710  }
711                                                                               
712  ds_c = ds_diction_cursor(diction);
713  ds_term = ds_diction_next(ds_c);
714  while(ds_term)
715  {
716    if (!(ds_term->s.status & TST_DIRTY)) { 
717      ds_term = ds_diction_next(ds_c);
718      continue;
719    }
720
721    if (CTX->training_mode == DST_TOE           &&
722        CTX->classification == DSR_NONE         &&
723        CTX->operating_mode == DSM_CLASSIFY     &&
724        diction->whitelist_token != ds_term->key  &&
725        (!ds_term->name || strncmp(ds_term->name, "bnr.", 4)))
726    {
727      ds_term = ds_diction_next(ds_c);
728      continue;
729    }
730
731    if (ds_term->s.spam_hits > CTX->totals.spam_learned)
732      ds_term->s.spam_hits = CTX->totals.spam_learned;
733    if (ds_term->s.innocent_hits > CTX->totals.innocent_learned)
734      ds_term->s.innocent_hits = CTX->totals.innocent_learned;
735
736    if (!_ds_set_spamrecord (CTX, ds_term->key, &ds_term->s))
737      ret = 0;
738    ds_term = ds_diction_next(ds_c);
739  }
740  ds_diction_close(ds_c);
741
742  return ret;
743}
744
745int
746_ds_get_spamrecord (
747  DSPAM_CTX * CTX,
748  unsigned long long token,
749  struct _ds_spam_stat *stat)
750{
751  struct _hash_drv_spam_record rec;
752  struct _hash_drv_storage *s = (struct _hash_drv_storage *) CTX->storage;
753
754  rec.spam = rec.nonspam = 0;
755  rec.hashcode = token;
756
757  stat->offset = _hash_drv_get_spamrecord(s->map, &rec);
758  if (!stat->offset)
759    return EFAILURE;
760
761  stat->probability   = 0.00000;
762  stat->status        = 0;
763  stat->innocent_hits = rec.nonspam & 0x0fffffff;
764  stat->spam_hits     = rec.spam & 0x0fffffff;
765
766  return 0;
767}
768
769int
770_ds_set_spamrecord (
771  DSPAM_CTX * CTX,
772  unsigned long long token,
773  struct _ds_spam_stat *stat)
774{
775  struct _hash_drv_spam_record rec;
776  struct _hash_drv_storage *s = (struct _hash_drv_storage *) CTX->storage;
777
778  rec.hashcode = token;
779  rec.nonspam = (stat->innocent_hits > 0) ? stat->innocent_hits : 0;
780  rec.spam = (stat->spam_hits > 0) ? stat->spam_hits : 0;
781
782  if(rec.nonspam>0x0fffffff)rec.nonspam=0x0fffffff;
783  if(rec.spam>0x0fffffff)rec.spam=0x0fffffff;
784
785  return _hash_drv_set_spamrecord(s->map, &rec, stat->offset);
786}
787
788int
789_ds_set_signature (DSPAM_CTX * CTX, struct _ds_spam_signature *SIG,
790                   const char *signature)
791{
792  char filename[MAX_FILENAME_LENGTH];
793  char scratch[128];
794  FILE *file;
795
796  _ds_userdir_path(filename,
797                   CTX->home,
798                   (CTX->group) ? CTX->group : CTX->username,
799                   "sig");
800
801  snprintf(scratch, sizeof(scratch), "/%s.sig", signature);
802  strlcat(filename, scratch, sizeof(filename));
803  _ds_prepare_path_for(filename);
804
805  file = fopen(filename, "w");
806  if (!file) {
807    LOG(LOG_ERR, ERR_IO_FILE_WRITE, filename, strerror(errno));
808    return EFAILURE;
809  }
810  if(fwrite(SIG->data, SIG->length, 1, file)!=1) {
811    fclose(file);
812    unlink(filename);
813    LOG(LOG_ERR, ERR_IO_FILE_WRITING, filename, strerror(errno));
814    return(EFAILURE);
815  }
816  fclose(file);
817
818  return 0;
819}
820
821int
822_ds_get_signature (DSPAM_CTX * CTX, struct _ds_spam_signature *SIG,
823                   const char *signature)
824{
825  char filename[MAX_FILENAME_LENGTH];
826  char scratch[128];
827  FILE *file;
828  struct stat statbuf;
829
830  _ds_userdir_path(filename,
831                   CTX->home,
832                   (CTX->group) ? CTX->group : CTX->username, 
833                   "sig");
834
835  snprintf(scratch, sizeof(scratch), "/%s.sig", signature);
836  strlcat(filename, scratch, sizeof(filename));
837
838  if (stat (filename, &statbuf)) {
839    LOG(LOG_ERR, ERR_IO_FILE_OPEN, filename, strerror(errno));
840    return EFAILURE;
841  };
842
843  SIG->data = malloc(statbuf.st_size);
844  if (!SIG->data) {
845    LOG(LOG_CRIT, ERR_MEM_ALLOC);
846    return EUNKNOWN;
847  }
848
849  file = fopen(filename, "r");
850  if (!file) {
851    LOG(LOG_ERR, ERR_IO_FILE_OPEN, filename, strerror(errno));
852    return EFAILURE;
853  }
854
855  if (fread(SIG->data, statbuf.st_size, 1, file) != 1) {
856    LOG(LOG_ERR, ERR_IO_FILE_READ, filename, strerror(errno));
857    fclose(file);
858    return EFAILURE;
859  }
860
861  SIG->length = statbuf.st_size;
862  fclose(file);
863  return 0;
864}
865
866void *_ds_connect (DSPAM_CTX *CTX)
867{
868  CTX = CTX; /* Keep compiler happy */
869  return NULL;
870}
871
872int
873_ds_create_signature_id (DSPAM_CTX * CTX, char *buf, size_t len)
874{
875  char session[64];
876  char digit[6];
877  int pid, j;
878 
879  CTX = CTX; /* Keep compiler happy */
880  pid = getpid ();
881  snprintf (session, sizeof (session), "%8lx%d", (long) time (NULL), pid);
882 
883  for (j = 0; j < 2; j++)
884  { 
885    snprintf (digit, 6, "%d", rand ());
886    strlcat (session, digit, 64);
887  }
888
889  strlcpy (buf, session, len);
890  return 0;
891}
892
893int
894_ds_verify_signature (DSPAM_CTX * CTX, const char *signature)
895{
896  char filename[MAX_FILENAME_LENGTH];
897  char scratch[128];
898  struct stat statbuf;
899
900  _ds_userdir_path(filename,
901                   CTX->home,
902                   (CTX->group) ? CTX->group : CTX->username,
903                   "sig");
904
905  snprintf(scratch, sizeof(scratch), "/%s.sig", signature);
906  strlcat(filename, scratch, sizeof(filename));
907
908  if (stat (filename, &statbuf))
909    return 1;
910
911  return 0;
912}
913
914struct _ds_storage_record *
915_ds_get_nexttoken (DSPAM_CTX * CTX)
916{
917  struct _hash_drv_storage *s = (struct _hash_drv_storage *) CTX->storage;
918  struct _hash_drv_spam_record rec;
919  struct _ds_storage_record *sr;
920  struct _ds_spam_stat stat;
921
922  rec.hashcode = 0;
923
924  sr = calloc(1, sizeof(struct _ds_storage_record));
925  if (!sr) {
926    LOG(LOG_CRIT, ERR_MEM_ALLOC);
927    return NULL;
928  }
929
930  if (s->offset_nexttoken == 0) {
931    s->offset_header = s->map->addr;
932    s->offset_nexttoken = sizeof(struct _hash_drv_header);
933    memcpy(&rec,
934           (void *)((unsigned long) s->map->addr + s->offset_nexttoken),
935           sizeof(struct _hash_drv_spam_record));
936    if (rec.hashcode)
937      _ds_get_spamrecord (CTX, rec.hashcode, &stat);
938  }
939
940  while(rec.hashcode == 0 ||
941   ((unsigned long) s->map->addr + s->offset_nexttoken ==
942    (unsigned long) s->offset_header + sizeof(struct _hash_drv_header) +
943    (s->offset_header->hash_rec_max * sizeof(struct _hash_drv_spam_record))))
944  {
945    s->offset_nexttoken += sizeof(struct _hash_drv_spam_record);
946
947    if ((unsigned long) s->map->addr + s->offset_nexttoken >
948        (unsigned long) s->offset_header + sizeof(struct _hash_drv_header) +
949      (s->offset_header->hash_rec_max * sizeof(struct _hash_drv_spam_record)))
950    {
951      if (s->offset_nexttoken < s->map->file_len) {
952        s->offset_header = (void *)((unsigned long) s->map->addr +
953          (s->offset_nexttoken - sizeof(struct _hash_drv_spam_record)));
954
955        s->offset_nexttoken += sizeof(struct _hash_drv_header);
956        s->offset_nexttoken -= sizeof(struct _hash_drv_spam_record);
957      } else {
958        free(sr);
959        return NULL;
960      }
961    }
962
963    memcpy(&rec,
964           (void *)((unsigned long) s->map->addr + s->offset_nexttoken),
965           sizeof(struct _hash_drv_spam_record));
966    _ds_get_spamrecord (CTX, rec.hashcode, &stat);
967  }
968
969  sr->token = rec.hashcode;
970  sr->spam_hits = stat.spam_hits;
971  sr->innocent_hits = stat.innocent_hits;
972  sr->last_hit = time(NULL);
973  return sr;
974}
975
976int
977_ds_delete_signature (DSPAM_CTX * CTX, const char *signature)
978{
979  char filename[MAX_FILENAME_LENGTH];
980  char scratch[128];
981
982  _ds_userdir_path(filename,
983                   CTX->home,
984                   (CTX->group) ? CTX->group : CTX->username,
985                   "sig");
986
987  snprintf(scratch, sizeof(scratch), "/%s.sig", signature);
988  strlcat(filename, scratch, sizeof(filename)); 
989  return unlink(filename);
990}
991
992char *
993_ds_get_nextuser (DSPAM_CTX * CTX)
994{
995  static char user[MAX_FILENAME_LENGTH];
996  static char path[MAX_FILENAME_LENGTH];
997  struct _hash_drv_storage *s = (struct _hash_drv_storage *) CTX->storage;
998  struct nt_node *node_nt, *prev;
999  struct nt_c c_nt;
1000  char *x = NULL, *y;
1001  DIR *dir = NULL;
1002
1003  struct dirent *entry;
1004
1005  if (s->dir_handles->items == 0)
1006  {
1007    char filename[MAX_FILENAME_LENGTH];
1008    snprintf(filename, MAX_FILENAME_LENGTH, "%s/data", CTX->home);
1009    dir = opendir (filename);
1010    if (dir == NULL)
1011    {
1012      LOG (LOG_WARNING,
1013           "unable to open directory '%s' for reading: %s",
1014           CTX->home, strerror (errno));
1015      return NULL;
1016    }
1017
1018    nt_add (s->dir_handles, (void *) dir);
1019    strlcpy (path, filename, sizeof (path));
1020  }
1021  else
1022  {
1023    node_nt = c_nt_first (s->dir_handles, &c_nt);
1024    while (node_nt != NULL)
1025    {
1026      if (node_nt->next == NULL)
1027        dir = (DIR *) node_nt->ptr;
1028      node_nt = c_nt_next (s->dir_handles, &c_nt);
1029    }
1030  }
1031
1032  if (dir != NULL) {
1033    while ((entry = readdir (dir)) != NULL)
1034    {
1035      struct stat st;
1036      char filename[MAX_FILENAME_LENGTH];
1037      snprintf (filename, sizeof (filename), "%s/%s", path, entry->d_name);
1038
1039      if (!strcmp (entry->d_name, ".") || !strcmp (entry->d_name, ".."))
1040        continue;
1041
1042      if (stat (filename, &st)) {
1043        continue;
1044      }
1045
1046      /* push a new directory */
1047      if (st.st_mode & S_IFDIR)
1048      {
1049        DIR *ndir;
1050
1051        ndir = opendir (filename);
1052        if (ndir == NULL)
1053          continue;
1054        strlcat (path, "/", sizeof (path));
1055        strlcat (path, entry->d_name, sizeof (path));
1056        nt_add (s->dir_handles, (void *) ndir);
1057        return _ds_get_nextuser (CTX);
1058      }
1059      else if (strlen(entry->d_name)>4 &&
1060        !strncmp ((entry->d_name + strlen (entry->d_name)) - 4, ".css", 4))
1061      {
1062        strlcpy (user, entry->d_name, sizeof (user));
1063        user[strlen (user) - 4] = 0;
1064        return user;
1065      }
1066    }
1067  }
1068
1069  /* pop current directory */
1070  y = strchr (path, '/');
1071  while (y != NULL)
1072  {
1073    x = y;
1074    y = strchr (x + 1, '/');
1075  }
1076  if (x)
1077    x[0] = 0;
1078
1079  /* pop directory handle from list */
1080  node_nt = c_nt_first (s->dir_handles, &c_nt);
1081  prev = NULL;
1082  while (node_nt != NULL)
1083  {
1084    if (node_nt->next == NULL)
1085    {
1086      dir = (DIR *) node_nt->ptr;
1087      closedir (dir);
1088      if (prev != NULL) {
1089        prev->next = NULL;
1090        s->dir_handles->insert = NULL;
1091      }
1092      else
1093        s->dir_handles->first = NULL;
1094      free (node_nt);
1095      s->dir_handles->items--;
1096      break;
1097    }
1098    prev = node_nt;
1099    node_nt = c_nt_next (s->dir_handles, &c_nt);
1100  }
1101  if (s->dir_handles->items > 0)
1102    return _ds_get_nextuser (CTX);
1103
1104  user[0] = 0;
1105  return NULL;
1106}
1107
1108struct _ds_storage_signature *
1109_ds_get_nextsignature (DSPAM_CTX * CTX)
1110{
1111  CTX = CTX; /* Keep compiler happy */
1112  return NULL;
1113}
1114
1115int
1116_ds_delall_spamrecords (DSPAM_CTX * CTX, ds_diction_t diction)
1117{
1118  CTX = CTX; /* Keep compiler happy */
1119  diction = diction; /* Keep compiler happy */
1120  return 0;
1121}
1122
1123int _hash_drv_autoextend(
1124    hash_drv_map_t map,
1125    int extents,
1126    unsigned long last_extent_size)
1127{
1128  struct _hash_drv_header header;
1129  struct _hash_drv_spam_record rec;
1130  int lastsize;
1131  unsigned long i;
1132
1133  _hash_drv_close(map);
1134
1135  map->fd = open(map->filename, O_RDWR);
1136  if (map->fd < 0) {
1137    LOG(LOG_WARNING, "unable to resize hash. open failed: %s", strerror(errno));
1138    return EFAILURE;
1139  }
1140
1141  memset(&header, 0, sizeof(struct _hash_drv_header));
1142  memset(&rec, 0, sizeof(struct _hash_drv_spam_record));
1143 
1144  if (extents == 0 || !map->pctincrease)
1145    header.hash_rec_max = map->extent_size;
1146  else
1147    header.hash_rec_max = last_extent_size
1148                   + (last_extent_size * (map->pctincrease/100.0));
1149
1150  LOGDEBUG("adding extent last: %d(%ld) new: %d(%ld) pctincrease: %1.2f", extents, last_extent_size, extents+1, header.hash_rec_max, (map->pctincrease/100.0));
1151
1152  lastsize=lseek (map->fd, 0, SEEK_END);
1153  if(write (map->fd, &header, sizeof(struct _hash_drv_header))!=sizeof(struct _hash_drv_header)) {
1154    if (ftruncate(map->fd, lastsize) < 0) {
1155      LOG(LOG_WARNING, "unable to truncate hash file %s: %s",
1156          map->filename, strerror(errno));
1157    }
1158    close(map->fd);
1159    LOG(LOG_WARNING, "unable to resize hash. open failed: %s", strerror(errno));
1160    return EFAILURE;
1161  }
1162  for(i=0;i<header.hash_rec_max;i++)
1163    if(write (map->fd, &rec, sizeof(struct _hash_drv_spam_record))!=sizeof(struct _hash_drv_spam_record)) {
1164      if (ftruncate(map->fd, lastsize) < 0) {
1165        LOG(LOG_WARNING, "unable to truncate hash file %s: %s",
1166            map->filename, strerror(errno));
1167      }
1168      close(map->fd);
1169      LOG(LOG_WARNING, "unable to resize hash. open failed: %s", strerror(errno));
1170      return EFAILURE;
1171    }
1172  close(map->fd);
1173
1174  _hash_drv_open(map->filename, map, 0, map->max_seek,
1175      map->max_extents, map->extent_size, map->pctincrease, map->flags);
1176  return 0;
1177}
1178
1179unsigned long _hash_drv_seek(
1180  hash_drv_map_t map,
1181  unsigned long offset,
1182  unsigned long long hashcode,
1183  int flags)
1184{
1185  hash_drv_header_t header = (void *)((unsigned long) map->addr + offset);
1186  hash_drv_spam_record_t rec;
1187  unsigned long fpos;
1188  unsigned long iterations = 0;
1189
1190  if (offset >= map->file_len)
1191    return 0;
1192
1193  fpos = sizeof(struct _hash_drv_header) +
1194    ((hashcode % header->hash_rec_max) * sizeof(struct _hash_drv_spam_record));
1195
1196  rec = (void *)((unsigned long) map->addr + offset + fpos);
1197  while(rec->hashcode != hashcode  &&   /* Match token     */
1198        rec->hashcode != 0         &&   /* Insert on empty */
1199        iterations < map->max_seek)     /* Max Iterations  */
1200  {
1201    iterations++;
1202    fpos += sizeof(struct _hash_drv_spam_record);
1203
1204    if (fpos >= (header->hash_rec_max * sizeof(struct _hash_drv_spam_record)))
1205      fpos = sizeof(struct _hash_drv_header);
1206    rec = (void *)((unsigned long) map->addr + offset + fpos);
1207  }     
1208
1209  if (rec->hashcode == hashcode)
1210    return fpos;
1211
1212  if (rec->hashcode == 0 && (flags & HSEEK_INSERT))
1213    return fpos;
1214
1215  return 0;
1216}
1217
1218int
1219_hash_drv_set_spamrecord (
1220  hash_drv_map_t map,
1221  hash_drv_spam_record_t wrec,
1222  unsigned long map_offset)
1223{
1224  hash_drv_spam_record_t rec;
1225  unsigned long offset = 0, extents = 0, last_extent_size = 0, rec_offset = 0;
1226
1227  if (map->addr == NULL)
1228    return EINVAL;
1229
1230  if (map_offset) {
1231    rec = (void *)((unsigned long) map->addr + map_offset);
1232  } else {
1233    while(rec_offset <= 0 && offset < map->file_len)
1234    {
1235      rec_offset = _hash_drv_seek(map, offset, wrec->hashcode, HSEEK_INSERT);
1236      if (rec_offset <= 0) {
1237        hash_drv_header_t header = (void *)((unsigned long) map->addr + offset);
1238        offset += sizeof(struct _hash_drv_header) +
1239          (sizeof(struct _hash_drv_spam_record) * header->hash_rec_max);
1240        last_extent_size = header->hash_rec_max;
1241        extents++;
1242      }
1243    }
1244 
1245    if (rec_offset <= 0) {
1246      if (map->flags & HMAP_AUTOEXTEND) {
1247        if (extents > map->max_extents && map->max_extents)
1248          goto FULL;
1249 
1250        if (!_hash_drv_autoextend(map, extents-1, last_extent_size))
1251          return _hash_drv_set_spamrecord(map, wrec, map_offset);
1252        else
1253          return EFAILURE;
1254      } else {
1255        goto FULL;
1256      }
1257    }
1258 
1259    rec = (void *)((unsigned long) map->addr + offset + rec_offset);
1260  }
1261  rec->hashcode = wrec->hashcode;
1262  rec->nonspam  = wrec->nonspam;
1263  rec->spam     = wrec->spam;
1264
1265  return 0;
1266
1267FULL:
1268  LOG(LOG_WARNING, "hash table %s full", map->filename);
1269  return EFAILURE;
1270}
1271
1272unsigned long
1273_hash_drv_get_spamrecord (
1274  hash_drv_map_t map,
1275  hash_drv_spam_record_t wrec)
1276{
1277  hash_drv_spam_record_t rec;
1278  unsigned long offset = 0, extents = 0, rec_offset = 0;
1279
1280  if (map->addr == NULL)
1281    return 0;
1282
1283  while(rec_offset <= 0 && offset < map->file_len)
1284  {
1285    rec_offset = _hash_drv_seek(map, offset, wrec->hashcode, 0);
1286    if (rec_offset <= 0) {
1287      hash_drv_header_t header = (void *)((unsigned long) map->addr + offset);
1288      offset += sizeof(struct _hash_drv_header) +
1289        (sizeof(struct _hash_drv_spam_record) * header->hash_rec_max);
1290      extents++;
1291    }
1292  }
1293
1294  if (rec_offset <= 0)
1295    return 0;
1296
1297  offset += rec_offset;
1298  rec = (void *)((unsigned long) map->addr + offset);
1299 
1300  wrec->nonspam  = rec->nonspam;
1301  wrec->spam     = rec->spam;
1302  return offset;
1303}
1304
1305/* Preference Stubs for Flat-File */
1306
1307agent_pref_t _ds_pref_load(config_t config, const char *user,
1308  const char *home, void *dbh)
1309{
1310  return _ds_ff_pref_load(config, user, home, dbh);
1311}
1312
1313int _ds_pref_set(config_t config, const char *user, const char *home,
1314  const char *attrib, const char *value, void *dbh)
1315{
1316  return _ds_ff_pref_set(config, user, home, attrib, value, dbh);
1317}
1318
1319int _ds_pref_del(config_t config, const char *user, const char *home,
1320  const char *attrib, void *dbh)
1321{
1322  return _ds_ff_pref_del(config, user, home, attrib, dbh);
1323}
1324
Note: See TracBrowser for help on using the repository browser.