/* ** Copyright (c) 2007 Sendmail, Inc. and its suppliers. ** All rights reserved. ** ** $Id: stats.c,v 1.10 2007/06/27 19:49:23 msk Exp $ */ #ifdef _FFR_STATS #ifndef lint static char stats_c_id[] = "@(#)$Id: stats.c,v 1.10 2007/06/27 19:49:23 msk Exp $"; #endif /* !lint */ /* system includes */ #include #include #include #include #include #include #include #include #include /* libdb includes */ #include /* libsm includes */ #include #include #include /* libdkim includes */ #include /* dkim-filter includes */ #include "stats.h" #include "dkim-filter.h" /* definitions */ #ifndef DB_NOTFOUND # define DB_NOTFOUND 1 #endif /* ! DB_NOTFOUND */ #define DB_MODE (S_IRUSR|S_IWUSR) #ifndef DB_VERSION_MAJOR # define DB_VERSION_MAJOR 1 #endif /* ! DB_VERSION_MAJOR */ #define DB_VERSION_CHECK(x,y,z) ((DB_VERSION_MAJOR == (x) && \ DB_VERSION_MINOR >= (y)) || \ DB_VERSION_MAJOR > (x)) /* globals */ static pthread_mutex_t stats_lock; /* ** DKIMF_STATS_INIT -- initialize statistics ** ** Parameters: ** None. ** ** Return value: ** None. */ void dkimf_stats_init(void) { pthread_mutex_init(&stats_lock, NULL); } /* ** DKIMF_STATS_RECORD -- record a DKIM result ** ** Parameters: ** path -- patth to the DB to update ** sigdomain -- signing domain ** hdrcanon -- header canonicalization used ** bodycanon -- body canonicalization used ** signalg -- signing algorithm used ** passfail -- result (TRUE == pass, FALSE == not pass) ** testing -- testing? ** lengths -- l= tag present? ** ** Return value: ** None (for now). */ void dkimf_stats_record(const char *path, const char *sigdomain, dkim_canon_t hdrcanon, dkim_canon_t bodycanon, dkim_alg_t signalg, bool passfail, bool testing, bool lengths) { int status = 0; DB *db; DBT key; DBT data; struct dkim_stats_key reckey; struct dkim_stats_data recdata; assert(path != NULL); assert(sigdomain != NULL); pthread_mutex_lock(&stats_lock); /* open the DB */ #if DB_VERSION_CHECK(3,0,0) status = db_create(&db, NULL, 0); if (status == 0) { # if DB_VERSION_CHECK(4,0,0) status = db->open(db, NULL, path, NULL, DB_HASH, (DB_CREATE|DB_THREAD), DB_MODE); # else /* DB_VERSION_CHECK(4,0,0) */ status = db->open(db, path, NULL, DB_HASH, (DB_CREATE|DB_THREAD), DB_MODE); # endif /* DB_VERSION_CHECK(4,0,0) */ } #elif DB_VERSION_CHECK(2,0,0) status = db_open(path, DB_HASH, (DB_CREATE|DB_THREAD), DB_MODE, NULL, NULL, &db); #else /* DB_VERSION_CHECK(2,0,0) */ db = dbopen(path, (O_CREAT|O_RDWR), DB_MODE, DB_HASH, NULL); if (db == NULL) status = errno; #endif /* DB_VERSION_CHECK */ if (status != 0) { if (dolog) { char *err; #if DB_VERSION_CHECK(3,0,0) err = db_strerror(status); #else /* DB_VERSION_CHECK(3,0,0) */ err = strerror(errno); #endif /* DB_VERSION_CHECK(3,0,0) */ syslog(LOG_ERR, "%s: db->open(): %s", path, err); } pthread_mutex_unlock(&stats_lock); return; } /* populate the records */ memset(&reckey, '\0', sizeof reckey); memset(&recdata, '\0', sizeof recdata); reckey.sk_hdrcanon = hdrcanon; reckey.sk_bodycanon = bodycanon; sm_strlcpy(reckey.sk_sigdomain, sigdomain, sizeof reckey.sk_sigdomain); /* see if this key already exists */ memset(&key, '\0', sizeof key); memset(&data, '\0', sizeof data); key.data = (void *) &reckey; key.size = sizeof reckey; #if DB_VERSION_CHECK(3,0,0) key.ulen = sizeof reckey; key.flags = DB_DBT_USERMEM; #endif /* DB_VERSION_CHECK(3,0,0) */ data.data = (void *) &recdata; data.size = sizeof recdata; #if DB_VERSION_CHECK(3,0,0) data.ulen = sizeof recdata; data.flags = DB_DBT_USERMEM; #endif /* DB_VERSION_CHECK(3,0,0) */ #if DB_VERSION_CHECK(2,0,0) status = db->get(db, NULL, &key, &data, 0); #else /* DB_VERSION_CHECK(2,0,0) */ status = db->get(db, &key, &data, 0); #endif /* DB_VERSION_CHECK(2,0,0) */ if (status != DB_NOTFOUND && status != 0) { if (dolog) { char *err; #if DB_VERSION_CHECK(3,0,0) err = db_strerror(status); #else /* DB_VERSION_CHECK(3,0,0) */ err = strerror(errno); #endif /* DB_VERSION_CHECK(3,0,0) */ syslog(LOG_ERR, "%s: db->get(): %s", path, err); } #if DB_VERSION_CHECK(2,0,0) (void) db->close(db, 0); #else /* DB_VERSION_CHECK(2,0,0) */ (void) db->close(db); #endif /* DB_VERSION_CHECK(2,0,0) */ pthread_mutex_unlock(&stats_lock); return; } #if !DB_VERSION_CHECK(2,0,0) memcpy((void *) &recdata, data.data, MIN(sizeof recdata, data.size)); #endif /* ! DB_VERSION_CHECK(2,0,0) */ /* update totals */ recdata.sd_lengths = lengths; (void) time(&recdata.sd_lastseen); recdata.sd_lastalg = signalg; if (passfail) recdata.sd_pass++; else recdata.sd_fail++; /* write/update the record */ memset(&key, '\0', sizeof key); memset(&data, '\0', sizeof data); key.data = (void *) &reckey; key.size = sizeof reckey; #if DB_VERSION_CHECK(3,0,0) key.ulen = sizeof reckey; key.flags = DB_DBT_USERMEM; #endif /* DB_VERSION_CHECK(3,0,0) */ data.data = (void *) &recdata; data.size = sizeof recdata; #if DB_VERSION_CHECK(3,0,0) data.ulen = sizeof recdata; data.flags = DB_DBT_USERMEM; #endif /* DB_VERSION_CHECK(3,0,0) */ #if DB_VERSION_CHECK(2,0,0) status = db->put(db, NULL, &key, &data, 0); #else /* DB_VERSION_CHECK(2,0,0) */ status = db->put(db, &key, &data, 0); #endif /* DB_VERSION_CHECK(2,0,0) */ if (status != 0 && dolog) { char *err; #if DB_VERSION_CHECK(3,0,0) err = db_strerror(status); #else /* DB_VERSION_CHECK(3,0,0) */ err = strerror(errno); #endif /* DB_VERSION_CHECK(3,0,0) */ syslog(LOG_ERR, "%s: db->put(): %s", path, err); } /* close the DB */ #if DB_VERSION_CHECK(2,0,0) (void) db->close(db, 0); #else /* DB_VERSION_CHECK(2,0,0) */ (void) db->close(db); #endif /* DB_VERSION_CHECK(2,0,0) */ pthread_mutex_unlock(&stats_lock); } #endif /* _FFR_STATS */