From 0cc804c87799fb5902e66d5bd537a762f786dc2a Mon Sep 17 00:00:00 2001 From: Jeremy Harris Date: Sun, 20 Mar 2022 14:20:13 +0000 Subject: [PATCH] Hints DB interface: convert from macros to inlinable functions. Testing status: tdb, dbm, gdbm & ndbm build and pass testsuite. --- src/Makefile | 1 + src/OS/Makefile-Base | 6 +- src/exim_monitor/em_hdr.h | 3 +- src/scripts/MakeLinks | 4 +- src/src/buildconfig.c | 5 +- src/src/dbfn.c | 85 ++-- src/src/dbfunctions.h | 9 +- src/src/dbstuff.h | 724 ----------------------------- src/src/exim.h | 3 +- src/src/exim_dbmbuild.c | 62 +-- src/src/exim_dbutil.c | 83 ++-- src/src/filter.c | 10 +- src/src/hintsdb.h | 805 +++++++++++++++++++++++++++++++++ src/src/hintsdb_structs.h | 189 ++++++++ src/src/lookups/dbmdb.c | 21 +- src/src/perl.c | 4 + src/src/transports/autoreply.c | 37 +- 17 files changed, 1128 insertions(+), 923 deletions(-) delete mode 100644 src/src/dbstuff.h create mode 100644 src/src/hintsdb.h create mode 100644 src/src/hintsdb_structs.h diff --git a/src/Makefile b/src/Makefile index 3aa3cc374..a48938023 100644 --- a/src/Makefile +++ b/src/Makefile @@ -102,6 +102,7 @@ distclean:; $(RM_COMMAND) -rf build-* cscope* cscope.files: FRC echo "-q" > $@ echo "-p3" >> $@ + -bd=build-$(buildname); [ -d $$bd ] && echo -e "$$bd/config.h\n$$bd/Makefile" >> $@ find src Local OS exim_monitor -name "*.[cshyl]" -print \ -o -name "os.[ch]*" -print \ -o -name "*akefile*" -print \ diff --git a/src/OS/Makefile-Base b/src/OS/Makefile-Base index 612db7a9c..cab56cc38 100644 --- a/src/OS/Makefile-Base +++ b/src/OS/Makefile-Base @@ -635,11 +635,12 @@ eximon.bin: $(EXIMON_EDITME) eximon $(OBJ_MONBIN) ../exim_monitor/em_version.c \ HDRS = blob.h \ config.h \ dbfunctions.h \ - dbstuff.h \ exim.h \ functions.h \ globals.h \ hash.h \ + hintsdb.h \ + hintsdb_structs.h \ local_scan.h \ macros.h \ mytypes.h \ @@ -648,10 +649,11 @@ HDRS = blob.h \ os.h PHDRS = ../config.h \ ../dbfunctions.h \ - ../dbstuff.h \ ../exim.h \ ../functions.h \ ../globals.h \ + ../hintsdb.h \ + ../hintsdb_structs.h \ ../local_scan.h \ ../macros.h \ ../mytypes.h \ diff --git a/src/exim_monitor/em_hdr.h b/src/exim_monitor/em_hdr.h index 87e4fc6df..ee05815df 100644 --- a/src/exim_monitor/em_hdr.h +++ b/src/exim_monitor/em_hdr.h @@ -102,7 +102,8 @@ typedef void hctx; #include "structs.h" #include "blob.h" #include "globals.h" -#include "dbstuff.h" +#include "hintsdb.h" +#include "hintsdb_structs.h" #include "functions.h" #include "osfunctions.h" diff --git a/src/scripts/MakeLinks b/src/scripts/MakeLinks index 4a714e147..35bb755d5 100755 --- a/src/scripts/MakeLinks +++ b/src/scripts/MakeLinks @@ -95,8 +95,8 @@ cd .. # but local_scan.c does not, because its location is taken from the build-time # configuration. Likewise for the os.c file, which gets build dynamically. -for f in blob.h dbfunctions.h dbstuff.h exim.h functions.h globals.h \ - hash.h local_scan.h \ +for f in blob.h dbfunctions.h exim.h functions.h globals.h \ + hash.h hintsdb.h hintsdb_structs.h local_scan.h \ macros.h mytypes.h osfunctions.h store.h structs.h lookupapi.h sha_ver.h \ \ acl.c buildconfig.c base64.c child.c crypt16.c daemon.c dbfn.c debug.c \ diff --git a/src/src/buildconfig.c b/src/src/buildconfig.c index a1b5485cd..a7f5812b2 100644 --- a/src/src/buildconfig.c +++ b/src/src/buildconfig.c @@ -221,8 +221,7 @@ else /* Now search the makefile for certain settings */ -base = fopen("Makefile", "rb"); -if (base == NULL) +if (!(base = fopen("Makefile", "rb"))) { printf("*** Buildconfig: failed to open Makefile\n"); (void)fclose(new); @@ -387,7 +386,6 @@ while (fgets(buffer, sizeof(buffer), base) != NULL) encountered. */ for (i = 1; i < sizeof(db_opts)/sizeof(char *); i++) - { if (strcmp(name, db_opts[i]) == 0) { if (use_which_db == i) @@ -397,7 +395,6 @@ while (fgets(buffer, sizeof(buffer), base) != NULL) fprintf(new, "/* %s not set */\n", name); break; } - } if (i < sizeof(db_opts)/sizeof(char *)) continue; /* EXIM_USER is a special case. We look in the environment for EXIM_USER or diff --git a/src/src/dbfn.c b/src/src/dbfn.c index a9bc892d4..600da18c4 100644 --- a/src/src/dbfn.c +++ b/src/src/dbfn.c @@ -38,31 +38,6 @@ arrange to hold the locks for the bare minimum of time. */ -/************************************************* -* Berkeley DB error callback * -*************************************************/ - -/* For Berkeley DB >= 2, we can define a function to be called in case of DB -errors. This should help with debugging strange DB problems, e.g. getting "File -exists" when you try to open a db file. The API for this function was changed -at DB release 4.3. */ - -#if defined(USE_DB) && defined(DB_VERSION_STRING) -void -# if DB_VERSION_MAJOR > 4 || (DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR >= 3) -dbfn_bdb_error_callback(const DB_ENV *dbenv, const char *pfx, const char *msg) -{ -dbenv = dbenv; -# else -dbfn_bdb_error_callback(const char *pfx, char *msg) -{ -# endif -pfx = pfx; -log_write(0, LOG_MAIN, "Berkeley DB error: %s", msg); -} -#endif - - /************************************************* * Open and lock a database file * *************************************************/ @@ -172,12 +147,12 @@ open call. */ snprintf(CS filename, sizeof(filename), "%s/%s", dirname, name); priv_drop_temp(exim_uid, exim_gid); -EXIM_DBOPEN(filename, dirname, flags, EXIMDB_MODE, &(dbblock->dbptr)); +dbblock->dbptr = exim_dbopen(filename, dirname, flags, EXIMDB_MODE); if (!dbblock->dbptr && errno == ENOENT && flags == O_RDWR) { DEBUG(D_hints_lookup) debug_printf_indent("%s appears not to exist: trying to create\n", filename); - EXIM_DBOPEN(filename, dirname, flags|O_CREAT, EXIMDB_MODE, &(dbblock->dbptr)); + dbblock->dbptr = exim_dbopen(filename, dirname, flags|O_CREAT, EXIMDB_MODE); } save_errno = errno; priv_restore(); @@ -232,7 +207,7 @@ Returns: nothing void dbfn_close(open_db *dbblock) { -EXIM_DBCLOSE(dbblock->dbptr); +exim_dbclose(dbblock->dbptr); (void)close(dbblock->lockfd); DEBUG(D_hints_lookup) { debug_printf_indent("closed hints database and lockfile\n"); acl_level--; } @@ -274,21 +249,21 @@ memcpy(key_copy, key, klen); DEBUG(D_hints_lookup) debug_printf_indent("dbfn_read: key=%s\n", key); -EXIM_DATUM_INIT(key_datum); /* Some DBM libraries require the datum */ -EXIM_DATUM_INIT(result_datum); /* to be cleared before use. */ -EXIM_DATUM_DATA(key_datum) = (void *) key_copy; -EXIM_DATUM_SIZE(key_datum) = klen; +exim_datum_init(&key_datum); /* Some DBM libraries require the datum */ +exim_datum_init(&result_datum); /* to be cleared before use. */ +exim_datum_data_set(&key_datum, key_copy); +exim_datum_size_set(&key_datum, klen); -if (!EXIM_DBGET(dbblock->dbptr, key_datum, result_datum)) return NULL; +if (!exim_dbget(dbblock->dbptr, &key_datum, &result_datum)) return NULL; /* Assume the data store could have been tainted. Properly, we should store the taint status with the data. */ -yield = store_get(EXIM_DATUM_SIZE(result_datum), GET_TAINTED); -memcpy(yield, EXIM_DATUM_DATA(result_datum), EXIM_DATUM_SIZE(result_datum)); -if (length) *length = EXIM_DATUM_SIZE(result_datum); +yield = store_get(exim_datum_size_get(&result_datum), GET_TAINTED); +memcpy(yield, exim_datum_data_get(&result_datum), exim_datum_size_get(&result_datum)); +if (length) *length = exim_datum_size_get(&result_datum); -EXIM_DATUM_FREE(result_datum); /* Some DBM libs require freeing */ +exim_datum_free(&result_datum); /* Some DBM libs require freeing */ return yield; } @@ -350,13 +325,13 @@ gptr->time_stamp = time(NULL); DEBUG(D_hints_lookup) debug_printf_indent("dbfn_write: key=%s\n", key); -EXIM_DATUM_INIT(key_datum); /* Some DBM libraries require the datum */ -EXIM_DATUM_INIT(value_datum); /* to be cleared before use. */ -EXIM_DATUM_DATA(key_datum) = (void *) key_copy; -EXIM_DATUM_SIZE(key_datum) = klen; -EXIM_DATUM_DATA(value_datum) = (void *) ptr; -EXIM_DATUM_SIZE(value_datum) = length; -return EXIM_DBPUT(dbblock->dbptr, key_datum, value_datum); +exim_datum_init(&key_datum); /* Some DBM libraries require the datum */ +exim_datum_init(&value_datum); /* to be cleared before use. */ +exim_datum_data_set(&key_datum, key_copy); +exim_datum_size_set(&key_datum, klen); +exim_datum_data_set(&value_datum, ptr); +exim_datum_size_set(&value_datum, length); +return exim_dbput(dbblock->dbptr, &key_datum, &value_datum); } @@ -378,15 +353,15 @@ dbfn_delete(open_db *dbblock, const uschar *key) { int klen = Ustrlen(key) + 1; uschar * key_copy = store_get(klen, key); +EXIM_DATUM key_datum; DEBUG(D_hints_lookup) debug_printf_indent("dbfn_delete: key=%s\n", key); memcpy(key_copy, key, klen); -EXIM_DATUM key_datum; -EXIM_DATUM_INIT(key_datum); /* Some DBM libraries require clearing */ -EXIM_DATUM_DATA(key_datum) = (void *) key_copy; -EXIM_DATUM_SIZE(key_datum) = klen; -return EXIM_DBDEL(dbblock->dbptr, key_datum); +exim_datum_init(&key_datum); /* Some DBM libraries require clearing */ +exim_datum_data_set(&key_datum, key_copy); +exim_datum_size_set(&key_datum, klen); +return exim_dbdel(dbblock->dbptr, &key_datum); } @@ -417,17 +392,17 @@ DEBUG(D_hints_lookup) debug_printf_indent("dbfn_scan\n"); /* Some dbm require an initialization */ -if (start) EXIM_DBCREATE_CURSOR(dbblock->dbptr, cursor); +if (start) *cursor = exim_dbcreate_cursor(dbblock->dbptr); -EXIM_DATUM_INIT(key_datum); /* Some DBM libraries require the datum */ -EXIM_DATUM_INIT(value_datum); /* to be cleared before use. */ +exim_datum_init(&key_datum); /* Some DBM libraries require the datum */ +exim_datum_init(&value_datum); /* to be cleared before use. */ -yield = (EXIM_DBSCAN(dbblock->dbptr, key_datum, value_datum, start, *cursor))? - US EXIM_DATUM_DATA(key_datum) : NULL; +yield = exim_dbscan(dbblock->dbptr, &key_datum, &value_datum, start, *cursor) + ? US exim_datum_data_get(&key_datum) : NULL; /* Some dbm require a termination */ -if (!yield) EXIM_DBDELETE_CURSOR(*cursor); +if (!yield) exim_dbdelete_cursor(*cursor); return yield; } diff --git a/src/src/dbfunctions.h b/src/src/dbfunctions.h index f3b04ad2f..384613981 100644 --- a/src/src/dbfunctions.h +++ b/src/src/dbfunctions.h @@ -5,6 +5,8 @@ /* Copyright (c) University of Cambridge 1995 - 2021 */ /* See the file NOTICE for conditions of use and distribution. */ +#ifndef DBFUNCTIONS_H +#define DBFUNCTIONS_H /* Functions for reading/writing exim database files */ @@ -24,11 +26,12 @@ int dbfn_write(open_db *, const uschar *, void *, int); changed at release 4.3. */ #if defined(USE_DB) && defined(DB_VERSION_STRING) -#if DB_VERSION_MAJOR > 4 || (DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR >= 3) +# if DB_VERSION_MAJOR > 4 || (DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR >= 3) void dbfn_bdb_error_callback(const DB_ENV *, const char *, const char *); -#else +# else void dbfn_bdb_error_callback(const char *, char *); -#endif +# endif #endif +#endif /* End of dbfunctions.h */ diff --git a/src/src/dbstuff.h b/src/src/dbstuff.h deleted file mode 100644 index 8ebf6f6e4..000000000 --- a/src/src/dbstuff.h +++ /dev/null @@ -1,724 +0,0 @@ -/************************************************* -* Exim - an Internet mail transport agent * -*************************************************/ - -/* Copyright (c) University of Cambridge 1995 - 2018 */ -/* Copyright (c) The Exim Maintainers 2020 - 2022 */ -/* See the file NOTICE for conditions of use and distribution. */ - -/* This header file contains macro definitions so that a variety of DBM -libraries can be used by Exim. Nigel Metheringham provided the original set for -Berkeley DB 1.x in native mode and ndbm. Subsequently, versions for Berkeley DB -2.x and 3.x were added. Later still, support for tdb was added, courtesy of -James Antill. Most recently, support for native mode gdbm was added, with code -from Pierre A. Humblet, so Exim could be made to work with Cygwin. - -For convenience, the definitions of the structures used in the various hints -databases are also kept in this file, which is used by the maintenance -utilities as well as the main Exim binary. */ - - -#ifdef USE_TDB - -/* ************************* tdb interface ************************ */ - -# include - -/* Basic DB type */ -# define EXIM_DB TDB_CONTEXT - -/* Cursor type: tdb uses the previous "key" in _nextkey() (really it wants -tdb_traverse to be called) */ -# define EXIM_CURSOR TDB_DATA - -/* The datum type used for queries */ -# define EXIM_DATUM TDB_DATA - -/* Some text for messages */ -# define EXIM_DBTYPE "tdb" - -/* Access functions */ - -/* EXIM_DBOPEN - sets *dbpp to point to an EXIM_DB, NULL if failed */ -# define EXIM_DBOPEN__(name, dirname, flags, mode, dbpp) \ - *(dbpp) = tdb_open(CS name, 0, TDB_DEFAULT, flags, mode) - -/* EXIM_DBGET - returns TRUE if successful, FALSE otherwise */ -# define EXIM_DBGET(db, key, data) \ - (data = tdb_fetch(db, key), data.dptr != NULL) - -/* EXIM_DBPUT - returns nothing useful, assumes replace mode */ -# define EXIM_DBPUT(db, key, data) \ - tdb_store(db, key, data, TDB_REPLACE) - -/* EXIM_DBPUTB - non-overwriting for use by dbmbuild */ -# define EXIM_DBPUTB(db, key, data) \ - tdb_store(db, key, data, TDB_INSERT) - -/* Returns from EXIM_DBPUTB */ - -# define EXIM_DBPUTB_OK 0 -# define EXIM_DBPUTB_DUP (-1) - -/* EXIM_DBDEL */ -# define EXIM_DBDEL(db, key) tdb_delete(db, key) - -/* EXIM_DBCREATE_CURSOR - initialize for scanning operation */ -# define EXIM_DBCREATE_CURSOR(db, cursor) { \ - *(cursor) = store_malloc(sizeof(TDB_DATA)); (*(cursor))->dptr = NULL; } - -/* EXIM_DBSCAN - This is complicated because we have to free the last datum -free() must not die when passed NULL */ -# define EXIM_DBSCAN(db, key, data, first, cursor) \ - (key = (first ? tdb_firstkey(db) : tdb_nextkey(db, *(cursor))), \ - free((cursor)->dptr), *(cursor) = key, \ - key.dptr != NULL) - -/* EXIM_DBDELETE_CURSOR - terminate scanning operation. */ -# define EXIM_DBDELETE_CURSOR(cursor) store_free(cursor) - -/* EXIM_DBCLOSE */ -# define EXIM_DBCLOSE__(db) tdb_close(db) - -/* Datum access types - these are intended to be assignable */ - -# define EXIM_DATUM_SIZE(datum) (datum).dsize -# define EXIM_DATUM_DATA(datum) (datum).dptr - -/* Free the stuff inside the datum. */ - -# define EXIM_DATUM_FREE(datum) (free((datum).dptr), (datum).dptr = NULL) - -/* No initialization is needed. */ - -# define EXIM_DATUM_INIT(datum) - -/* size limit */ - -# define EXIM_DB_RLIMIT 150 - - - - - - -/********************* Berkeley db native definitions **********************/ - -#elif defined USE_DB - -# include - - -/* We can distinguish between versions 1.x and 2.x/3.x by looking for a -definition of DB_VERSION_STRING, which is present in versions 2.x onwards. */ - -# ifdef DB_VERSION_STRING - -# if DB_VERSION_MAJOR >= 6 -# error Version 6 and later BDB API is not supported -# endif - -/* The API changed (again!) between the 2.x and 3.x versions */ - -# if DB_VERSION_MAJOR >= 3 - -/***************** Berkeley db 3.x/4.x native definitions ******************/ - -/* Basic DB type */ -# if DB_VERSION_MAJOR > 4 || (DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR >= 1) -# define EXIM_DB DB_ENV -/* Cursor type, for scanning */ -# define EXIM_CURSOR DBC - -/* The datum type used for queries */ -# define EXIM_DATUM DBT - -/* Some text for messages */ -# define EXIM_DBTYPE "db (v4.1+)" - -/* Only more-recent versions. 5+ ? */ -# ifndef DB_FORCESYNC -# define DB_FORCESYNC 0 -# endif - - -/* Access functions */ - -/* EXIM_DBOPEN - sets *dbpp to point to an EXIM_DB, NULL if failed. The -API changed for DB 4.1. - and we also starting using the "env" with a -specified working dir, to avoid the DBCONFIG file trap. */ - -# define ENV_TO_DB(env) ((DB *)((env)->app_private)) - -# define EXIM_DBOPEN__(name, dirname, flags, mode, dbpp) \ - if ( db_env_create(dbpp, 0) != 0 \ - || ((*dbpp)->set_errcall(*dbpp, dbfn_bdb_error_callback), 0) \ - || (*dbpp)->open(*dbpp, CS dirname, DB_CREATE|DB_INIT_MPOOL|DB_PRIVATE, 0) != 0\ - ) \ - *dbpp = NULL; \ - else if (db_create((DB **) &((*dbpp)->app_private), *dbpp, 0) != 0) \ - { \ - ((DB_ENV *)(*dbpp))->close((DB_ENV *)(*dbpp), 0); \ - *dbpp = NULL; \ - } \ - else if (ENV_TO_DB(*dbpp)->open(ENV_TO_DB(*dbpp), NULL, CS name, NULL, \ - (flags) == O_RDONLY ? DB_UNKNOWN : DB_HASH, \ - (flags) == O_RDONLY ? DB_RDONLY : DB_CREATE, \ - mode) != 0 \ - ) \ - { \ - ENV_TO_DB(*dbpp)->close(ENV_TO_DB(*dbpp), 0); \ - ((DB_ENV *)(*dbpp))->close((DB_ENV *)(*dbpp), 0); \ - *dbpp = NULL; \ - } - -/* EXIM_DBGET - returns TRUE if successful, FALSE otherwise */ -# define EXIM_DBGET(db, key, data) \ - (ENV_TO_DB(db)->get(ENV_TO_DB(db), NULL, &key, &data, 0) == 0) - -/* EXIM_DBPUT - returns nothing useful, assumes replace mode */ -# define EXIM_DBPUT(db, key, data) \ - ENV_TO_DB(db)->put(ENV_TO_DB(db), NULL, &key, &data, 0) - -/* EXIM_DBPUTB - non-overwriting for use by dbmbuild */ -# define EXIM_DBPUTB(db, key, data) \ - ENV_TO_DB(db)->put(ENV_TO_DB(db), NULL, &key, &data, DB_NOOVERWRITE) - -/* Return values from EXIM_DBPUTB */ - -# define EXIM_DBPUTB_OK 0 -# define EXIM_DBPUTB_DUP DB_KEYEXIST - -/* EXIM_DBDEL */ -# define EXIM_DBDEL(db, key) ENV_TO_DB(db)->del(ENV_TO_DB(db), NULL, &key, 0) - -/* EXIM_DBCREATE_CURSOR - initialize for scanning operation */ - -# define EXIM_DBCREATE_CURSOR(db, cursor) \ - ENV_TO_DB(db)->cursor(ENV_TO_DB(db), NULL, cursor, 0) - -/* EXIM_DBSCAN - returns TRUE if data is returned, FALSE at end */ -# define EXIM_DBSCAN(db, key, data, first, cursor) \ - ((cursor)->c_get(cursor, &key, &data, \ - (first? DB_FIRST : DB_NEXT)) == 0) - -/* EXIM_DBDELETE_CURSOR - terminate scanning operation */ -# define EXIM_DBDELETE_CURSOR(cursor) \ - (cursor)->c_close(cursor) - -/* EXIM_DBCLOSE */ -# define EXIM_DBCLOSE__(db) \ - (ENV_TO_DB(db)->close(ENV_TO_DB(db), 0) , ((DB_ENV *)(db))->close((DB_ENV *)(db), DB_FORCESYNC)) - -/* Datum access types - these are intended to be assignable. */ - -# define EXIM_DATUM_SIZE(datum) (datum).size -# define EXIM_DATUM_DATA(datum) (datum).data - -/* The whole datum structure contains other fields that must be cleared -before use, but we don't have to free anything after reading data. */ - -# define EXIM_DATUM_INIT(datum) memset(&datum, 0, sizeof(datum)) -# define EXIM_DATUM_FREE(datum) - -# else /* pre- 4.1 */ - -# define EXIM_DB DB - -/* Cursor type, for scanning */ -# define EXIM_CURSOR DBC - -/* The datum type used for queries */ -# define EXIM_DATUM DBT - -/* Some text for messages */ -# define EXIM_DBTYPE "db (v3/4)" - -/* Access functions */ - -/* EXIM_DBOPEN - sets *dbpp to point to an EXIM_DB, NULL if failed. */ - -# define EXIM_DBOPEN__(name, dirname, flags, mode, dbpp) \ - if (db_create(dbpp, NULL, 0) != 0 || \ - ((*dbpp)->set_errcall(*dbpp, dbfn_bdb_error_callback), \ - ((*dbpp)->open)(*dbpp, CS name, NULL, \ - ((flags) == O_RDONLY)? DB_UNKNOWN : DB_HASH, \ - ((flags) == O_RDONLY)? DB_RDONLY : DB_CREATE, \ - mode)) != 0) *(dbpp) = NULL - -/* EXIM_DBGET - returns TRUE if successful, FALSE otherwise */ -# define EXIM_DBGET(db, key, data) \ - ((db)->get(db, NULL, &key, &data, 0) == 0) - -/* EXIM_DBPUT - returns nothing useful, assumes replace mode */ -# define EXIM_DBPUT(db, key, data) \ - (db)->put(db, NULL, &key, &data, 0) - -/* EXIM_DBPUTB - non-overwriting for use by dbmbuild */ -# define EXIM_DBPUTB(db, key, data) \ - (db)->put(db, NULL, &key, &data, DB_NOOVERWRITE) - -/* Return values from EXIM_DBPUTB */ - -# define EXIM_DBPUTB_OK 0 -# define EXIM_DBPUTB_DUP DB_KEYEXIST - -/* EXIM_DBDEL */ -# define EXIM_DBDEL(db, key) (db)->del(db, NULL, &key, 0) - -/* EXIM_DBCREATE_CURSOR - initialize for scanning operation */ - -# define EXIM_DBCREATE_CURSOR(db, cursor) \ - (db)->cursor(db, NULL, cursor, 0) - -/* EXIM_DBSCAN - returns TRUE if data is returned, FALSE at end */ -# define EXIM_DBSCAN(db, key, data, first, cursor) \ - ((cursor)->c_get(cursor, &key, &data, \ - (first? DB_FIRST : DB_NEXT)) == 0) - -/* EXIM_DBDELETE_CURSOR - terminate scanning operation */ -# define EXIM_DBDELETE_CURSOR(cursor) \ - (cursor)->c_close(cursor) - -/* EXIM_DBCLOSE */ -# define EXIM_DBCLOSE__(db) (db)->close(db, 0) - -/* Datum access types - these are intended to be assignable. */ - -# define EXIM_DATUM_SIZE(datum) (datum).size -# define EXIM_DATUM_DATA(datum) (datum).data - -/* The whole datum structure contains other fields that must be cleared -before use, but we don't have to free anything after reading data. */ - -# define EXIM_DATUM_INIT(datum) memset(&datum, 0, sizeof(datum)) -# define EXIM_DATUM_FREE(datum) - -# endif - - -# else /* DB_VERSION_MAJOR >= 3 */ -# error Berkeley DB versions earlier than 3 are not supported */ -# endif /* DB_VERSION_MAJOR */ -# else -# error Berkeley DB version 1 is no longer supported -# endif /* DB_VERSION_STRING */ - - -/* all BDB versions */ -/* size limit */ - -# define EXIM_DB_RLIMIT 150 - - - - - - -/********************* gdbm interface definitions **********************/ - -#elif defined USE_GDBM - -# include - -/* Basic DB type */ -typedef struct { - GDBM_FILE gdbm; /* Database */ - datum lkey; /* Last key, for scans */ -} gdbm_db; - -#define EXIM_DB gdbm_db - -/* Cursor type, not used with gdbm: just set up a dummy */ -# define EXIM_CURSOR int - -/* The datum type used for queries */ -# define EXIM_DATUM datum - -/* Some text for messages */ - -# define EXIM_DBTYPE "gdbm" - -/* Access functions */ - -/* EXIM_DBOPEN - returns a EXIM_DB *, NULL if failed */ -# define EXIM_DBOPEN__(name, dirname, flags, mode, dbpp) \ - { EXIM_DB * dbp = malloc(sizeof(EXIM_DB));\ - if (dbp) { \ - dbp->lkey.dptr = NULL;\ - dbp->gdbm = gdbm_open(CS name, 0, (((flags) & O_CREAT))?GDBM_WRCREAT:(((flags) & (O_RDWR|O_WRONLY))?GDBM_WRITER:GDBM_READER), (mode), 0);\ - if (!dbp->gdbm) {\ - free(dbp);\ - dbp = NULL;\ - }\ - }\ - *(dbpp) = dbp;\ - } - -/* EXIM_DBGET - returns TRUE if successful, FALSE otherwise */ -# define EXIM_DBGET(db, key, data) \ - (data = gdbm_fetch(db->gdbm, key), data.dptr != NULL) - -/* EXIM_DBPUT - returns nothing useful, assumes replace mode */ -# define EXIM_DBPUT(db, key, data) \ - gdbm_store(db->gdbm, key, data, GDBM_REPLACE) - -/* EXIM_DBPUTB - non-overwriting for use by dbmbuild */ -# define EXIM_DBPUTB(db, key, data) \ - gdbm_store(db->gdbm, key, data, GDBM_INSERT) - -/* Returns from EXIM_DBPUTB */ - -# define EXIM_DBPUTB_OK 0 -# define EXIM_DBPUTB_DUP 1 - -/* EXIM_DBDEL */ -# define EXIM_DBDEL(db, key) gdbm_delete(db->gdbm, key) - -/* EXIM_DBCREATE_CURSOR - initialize for scanning operation (null) */ -# define EXIM_DBCREATE_CURSOR(db, cursor) {} - -/* EXIM_DBSCAN */ -# define EXIM_DBSCAN(db, key, data, first, cursor) \ - ( key = ((first)? gdbm_firstkey(db->gdbm) : gdbm_nextkey(db->gdbm, db->lkey)), \ - (((db)->lkey.dptr != NULL)? (free((db)->lkey.dptr),1) : 1),\ - db->lkey = key, key.dptr != NULL) - -/* EXIM_DBDELETE_CURSOR - terminate scanning operation (null). */ -# define EXIM_DBDELETE_CURSOR(cursor) { } - -/* EXIM_DBCLOSE */ -# define EXIM_DBCLOSE__(db) \ -{ gdbm_close((db)->gdbm);\ - if ((db)->lkey.dptr != NULL) free((db)->lkey.dptr);\ - free(db); } - -/* Datum access types - these are intended to be assignable */ - -# define EXIM_DATUM_SIZE(datum) (datum).dsize -# define EXIM_DATUM_DATA(datum) (datum).dptr - -/* There's no clearing required before use, but we have to free the dptr -after reading data. */ - -# define EXIM_DATUM_INIT(datum) -# define EXIM_DATUM_FREE(datum) free(datum.dptr) - -/* size limit */ - -# define EXIM_DB_RLIMIT 150 - -#else /* USE_GDBM */ - - - - - - -/* If none of USE_DB, USG_GDBM, or USE_TDB are set, the default is the NDBM -interface */ - - -/********************* ndbm interface definitions **********************/ - -# include - -/* Basic DB type */ -# define EXIM_DB DBM - -/* Cursor type, not used with ndbm: just set up a dummy */ -# define EXIM_CURSOR int - -/* The datum type used for queries */ -# define EXIM_DATUM datum - -/* Some text for messages */ - -# define EXIM_DBTYPE "ndbm" - -/* Access functions */ - -/* EXIM_DBOPEN - returns a EXIM_DB *, NULL if failed */ -/* Check that the name given is not present. This catches -a directory name; otherwise we would create the name.pag and -name.dir files in the directory's parent. */ - -# define EXIM_DBOPEN__(name, dirname, flags, mode, dbpp) \ - { \ - struct stat st; \ - *(dbpp) = !(flags & O_CREAT) \ - || lstat(CCS (name), &st) != 0 && errno == ENOENT \ - ? dbm_open(CS (name), (flags), (mode)) \ - : (errno = (st.st_mode & S_IFMT) == S_IFDIR ? EISDIR : EEXIST, \ - NULL); \ - } - -/* EXIM_DBGET - returns TRUE if successful, FALSE otherwise */ -# define EXIM_DBGET(db, key, data) \ - (data = dbm_fetch(db, key), data.dptr != NULL) - -/* EXIM_DBPUT - returns nothing useful, assumes replace mode */ -# define EXIM_DBPUT(db, key, data) \ - dbm_store(db, key, data, DBM_REPLACE) - -/* EXIM_DBPUTB - non-overwriting for use by dbmbuild */ -# define EXIM_DBPUTB(db, key, data) \ - dbm_store(db, key, data, DBM_INSERT) - -/* Returns from EXIM_DBPUTB */ - -# define EXIM_DBPUTB_OK 0 -# define EXIM_DBPUTB_DUP 1 - -/* EXIM_DBDEL */ -# define EXIM_DBDEL(db, key) dbm_delete(db, key) - -/* EXIM_DBCREATE_CURSOR - initialize for scanning operation (null) */ -# define EXIM_DBCREATE_CURSOR(db, cursor) {} - -/* EXIM_DBSCAN */ -# define EXIM_DBSCAN(db, key, data, first, cursor) \ - (key = (first? dbm_firstkey(db) : dbm_nextkey(db)), key.dptr != NULL) - -/* EXIM_DBDELETE_CURSOR - terminate scanning operation (null). */ -# define EXIM_DBDELETE_CURSOR(cursor) { } - -/* EXIM_DBCLOSE */ -# define EXIM_DBCLOSE__(db) dbm_close(db) - -/* Datum access types - these are intended to be assignable */ - -# define EXIM_DATUM_SIZE(datum) (datum).dsize -# define EXIM_DATUM_DATA(datum) (datum).dptr - -/* There's no clearing required before use, and we don't have to free anything -after reading data. */ - -# define EXIM_DATUM_INIT(datum) -# define EXIM_DATUM_FREE(datum) - -/* size limit */ - -# define EXIM_DB_RLIMIT 150 - -#endif /* USE_GDBM */ - - - - - -#ifdef COMPILE_UTILITY - -# define EXIM_DBOPEN(name, dirname, flags, mode, dbpp) \ - EXIM_DBOPEN__(name, dirname, flags, mode, dbpp) -# define EXIM_DBCLOSE(db) EXIM_DBCLOSE__(db) - -#else - -# define EXIM_DBOPEN(name, dirname, flags, mode, dbpp) \ - do { \ - DEBUG(D_hints_lookup) \ - debug_printf_indent("EXIM_DBOPEN: file <%s> dir <%s> flags=%s\n", \ - (name), (dirname), \ - (flags) == O_RDONLY ? "O_RDONLY" \ - : (flags) == O_RDWR ? "O_RDWR" \ - : (flags) == (O_RDWR|O_CREAT) ? "O_RDWR|O_CREAT" \ - : "??"); \ - if (is_tainted(name) || is_tainted(dirname)) \ - { \ - log_write(0, LOG_MAIN|LOG_PANIC, "Tainted name for DB file not permitted"); \ - *dbpp = NULL; \ - } \ - else \ - { EXIM_DBOPEN__(name, dirname, flags, mode, dbpp); } \ - DEBUG(D_hints_lookup) debug_printf_indent("returned from EXIM_DBOPEN: %p\n", *dbpp); \ - } while(0) -# define EXIM_DBCLOSE(db) \ - do { \ - DEBUG(D_hints_lookup) debug_printf_indent("EXIM_DBCLOSE(%p)\n", db); \ - EXIM_DBCLOSE__(db); \ - } while(0) - -# endif - -#ifndef EXIM_DB -# define EXIM_DB void /* dummy */ -#endif -#ifndef EXIM_CURSOR -# define EXIM_CURSOR void /* dummy */ -#endif -/********************* End of dbm library definitions **********************/ - - -/* Structure for carrying around an open DBM file, and an open locking file -that relates to it. */ - -typedef struct { - EXIM_DB *dbptr; - int lockfd; -} open_db; - - -/* Structures for records stored in exim database dbm files. They all -start with the same fields, described in the generic type. */ - - -typedef struct { - time_t time_stamp; /* Timestamp of writing */ -} dbdata_generic; - - -/* This structure keeps track of retry information for a host or a local -address. */ - -typedef struct { - time_t time_stamp; - /*************/ - time_t first_failed; /* Time of first failure */ - time_t last_try; /* Time of last try */ - time_t next_try; /* Time of next try */ - BOOL expired; /* Retry time has expired */ - int basic_errno; /* Errno of last failure */ - int more_errno; /* Additional information */ - uschar text[1]; /* Text message for last failure */ -} dbdata_retry; - -/* These structures keep track of addresses that have had callout verification -performed on them. There are two groups of records: - -1. keyed by localpart@domain - - Full address was tested and record holds result - -2. keyed by domain - - Domain response upto MAIL FROM:<>, postmaster, random local part; - -If a record exists, the result field is either ccache_accept or ccache_reject, -or, for a domain record only, ccache_reject_mfnull when MAIL FROM:<> was -rejected. The other fields, however, (which are only relevant to domain -records) may also contain ccache_unknown if that particular test has not been -done. - -Originally, there was only one structure, used for both types. However, it got -expanded for domain records, so it got split. To make it possible for Exim to -handle the old type of record, we retain the old definition. The different -kinds of record can be distinguished by their different lengths. */ - -typedef struct { - time_t time_stamp; - /*************/ - int result; - int postmaster_result; /* Postmaster is accepted */ - int random_result; /* Random local part was accepted */ -} dbdata_callout_cache_obs; - -typedef struct { - time_t time_stamp; /* Timestamp of last address check */ - /*************/ - int result; /* accept or reject */ -} dbdata_callout_cache_address; - -/* For this new layout, we put the additional fields (the timestamps) -last so that if somebody reverts to an older Exim, the new records will -still make sense because they match the old layout. */ - -typedef struct { - time_t time_stamp; /* Time stamp of last connection */ - /*************/ - int result; /* Domain reject or accept */ - int postmaster_result; /* Postmaster result */ - int random_result; /* Random result */ - time_t postmaster_stamp; /* Timestamp of postmaster check */ - time_t random_stamp; /* Timestamp of random check */ -} dbdata_callout_cache; - -/* This structure keeps track of messages that are waiting for a particular -host for a particular transport. */ - -typedef struct { - time_t time_stamp; - /*************/ - int count; /* Count of message ids */ - int sequence; /* Sequence for continued records */ - uschar text[1]; /* One long character string */ -} dbdata_wait; - - -/* The contents of the "misc" database are a mixture of different kinds of -record, as defined below. The keys used for a specific type all start with a -given string such as "etrn-" or "host-serialize-". */ - - -/* This structure records a connection to a particular host, for the -purpose of serializing access to certain hosts. For possible future extension, -a field is defined for holding the count of connections, but it is not -at present in use. The same structure is used for recording a running ETRN -process. */ - -typedef struct { - time_t time_stamp; - /*************/ - int count; /* Reserved for possible connection count */ -} dbdata_serialize; - - -/* This structure records the information required for the ratelimit -ACL condition. */ - -typedef struct { - time_t time_stamp; - /*************/ - int time_usec; /* Fractional part of time, from gettimeofday() */ - double rate; /* Smoothed sending rate at that time */ -} dbdata_ratelimit; - -/* Same as above, plus a Bloom filter for uniquifying events. */ - -typedef struct { - dbdata_ratelimit dbd; - time_t bloom_epoch; /* When the Bloom filter was last reset */ - unsigned bloom_size; /* Number of bytes in the Bloom filter */ - uschar bloom[40]; /* Bloom filter which may be larger than this */ -} dbdata_ratelimit_unique; - - -/* For "seen" ACL condition */ -typedef struct { - time_t time_stamp; -} dbdata_seen; - -#ifndef DISABLE_PIPE_CONNECT -/* This structure records the EHLO responses, cleartext and crypted, -for an IP, as bitmasks (cf. OPTION_TLS). For LIMITS, also values -advertised for MAILMAX, RCPTMAX and RCPTDOMAINMAX; zero meaning no -value advertised. */ - -typedef struct { - unsigned short cleartext_features; - unsigned short crypted_features; - unsigned short cleartext_auths; - unsigned short crypted_auths; - -# ifdef EXPERIMENTAL_ESMTP_LIMITS - unsigned int limit_mail; - unsigned int limit_rcpt; - unsigned int limit_rcptdom; -# endif -} ehlo_resp_precis; - -typedef struct { - time_t time_stamp; - /*************/ - ehlo_resp_precis data; -} dbdata_ehlo_resp; -#endif - -typedef struct { - time_t time_stamp; - /*************/ - uschar verify_override:1; - uschar ocsp:3; - uschar session[1]; -} dbdata_tls_session; - - -/* End of dbstuff.h */ diff --git a/src/src/exim.h b/src/src/exim.h index cf8f19eb6..2541baa3d 100644 --- a/src/src/exim.h +++ b/src/src/exim.h @@ -531,7 +531,8 @@ config.h, mytypes.h, and store.h, so we don't need to mention them explicitly. #include "local_scan.h" #include "macros.h" -#include "dbstuff.h" +#include "hintsdb.h" +#include "hintsdb_structs.h" #include "structs.h" #include "blob.h" #include "globals.h" diff --git a/src/src/exim_dbmbuild.c b/src/src/exim_dbmbuild.c index 73ca10e90..b33864702 100644 --- a/src/src/exim_dbmbuild.c +++ b/src/src/exim_dbmbuild.c @@ -31,7 +31,7 @@ characters. */ #include "exim.h" -uschar * spool_directory = NULL; /* dummy for dbstuff.h */ +uschar * spool_directory = NULL; /* dummy for hintsdb.h */ /******************************************************************************/ /* dummies needed by Solaris build */ @@ -106,26 +106,6 @@ return sys_errlist[n]; #endif /* STRERROR_FROM_ERRLIST */ -/* For Berkeley DB >= 2, we can define a function to be called in case of DB -errors. This should help with debugging strange DB problems, e.g. getting "File -exists" when you try to open a db file. The API changed at release 4.3. */ - -#if defined(USE_DB) && defined(DB_VERSION_STRING) -void -# if DB_VERSION_MAJOR > 4 || (DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR >= 3) -dbfn_bdb_error_callback(const DB_ENV *dbenv, const char *pfx, const char *msg) -{ -dbenv = dbenv; -# else -dbfn_bdb_error_callback(const char *pfx, char *msg) -{ -# endif -pfx = pfx; -printf("Berkeley DB error: %s\n", msg); -} -#endif - - /************************************************* * Interpret escape sequence * @@ -266,9 +246,7 @@ else /* It is apparently necessary to open with O_RDWR for this to work with gdbm-1.7.3, though no reading is actually going to be done. */ -EXIM_DBOPEN(temp_dbmname, dirname, O_RDWR|O_CREAT|O_EXCL, 0644, &d); - -if (d == NULL) +if (!(d = exim_dbopen(temp_dbmname, dirname, O_RDWR|O_CREAT|O_EXCL, 0644))) { printf("exim_dbmbuild: unable to create %s: %s\n", temp_dbmname, strerror(errno)); @@ -347,11 +325,11 @@ while (Ufgets(line, max_insize, f) != NULL) if (started) { - EXIM_DATUM_INIT(content); - EXIM_DATUM_DATA(content) = (void *) buffer; - EXIM_DATUM_SIZE(content) = bptr - buffer + add_zero; + exim_datum_init(&content); + exim_datum_data_set(&content, buffer); + exim_datum_size_set(&content, bptr - buffer + add_zero); - switch(rc = EXIM_DBPUTB(d, key, content)) + switch(rc = exim_dbputb(d, &key, &content)) { case EXIM_DBPUTB_OK: count++; @@ -361,7 +339,7 @@ while (Ufgets(line, max_insize, f) != NULL) if (warn) fprintf(stderr, "** Duplicate key \"%s\"\n", keybuffer); dupcount++; if(duperr) yield = 1; - if (lastdup) EXIM_DBPUT(d, key, content); + if (lastdup) exim_dbput(d, &key, &content); break; default: @@ -374,8 +352,8 @@ while (Ufgets(line, max_insize, f) != NULL) bptr = buffer; } - EXIM_DATUM_INIT(key); - EXIM_DATUM_DATA(key) = (void *) keybuffer; + exim_datum_init(&key); + exim_datum_data_set(&key, keybuffer); /* Deal with quoted keys. Escape sequences always make one character out of several, so we can re-build in place. */ @@ -392,16 +370,16 @@ while (Ufgets(line, max_insize, f) != NULL) s++; } if (*s != 0) s++; /* Past terminating " */ - EXIM_DATUM_SIZE(key) = t - keystart + add_zero; + exim_datum_size_set(&key, t - keystart + add_zero); } else { keystart = s; while (*s != 0 && *s != ':' && !isspace(*s)) s++; - EXIM_DATUM_SIZE(key) = s - keystart + add_zero; + exim_datum_size_set(&key, s - keystart + add_zero); } - if (EXIM_DATUM_SIZE(key) > 256) + if (exim_datum_size_get(&key) > 256) { printf("Keys longer than 255 characters cannot be handled\n"); started = 0; @@ -410,10 +388,10 @@ while (Ufgets(line, max_insize, f) != NULL) } if (lowercase) - for (i = 0; i < EXIM_DATUM_SIZE(key) - add_zero; i++) + for (i = 0; i < exim_datum_size_get(&key) - add_zero; i++) keybuffer[i] = tolower(keystart[i]); else - for (i = 0; i < EXIM_DATUM_SIZE(key) - add_zero; i++) + for (i = 0; i < exim_datum_size_get(&key) - add_zero; i++) keybuffer[i] = keystart[i]; keybuffer[i] = 0; @@ -437,11 +415,11 @@ while (Ufgets(line, max_insize, f) != NULL) if (started) { int rc; - EXIM_DATUM_INIT(content); - EXIM_DATUM_DATA(content) = (void *) buffer; - EXIM_DATUM_SIZE(content) = bptr - buffer + add_zero; + exim_datum_init(&content); + exim_datum_data_set(&content, buffer); + exim_datum_size_set(&content, bptr - buffer + add_zero); - switch(rc = EXIM_DBPUTB(d, key, content)) + switch(rc = exim_dbputb(d, &key, &content)) { case EXIM_DBPUTB_OK: count++; @@ -451,7 +429,7 @@ if (started) if (warn) fprintf(stderr, "** Duplicate key \"%s\"\n", keybuffer); dupcount++; if (duperr) yield = 1; - if (lastdup) EXIM_DBPUT(d, key, content); + if (lastdup) exim_dbput(d, &key, &content); break; default: @@ -466,7 +444,7 @@ if (started) TIDYUP: -EXIM_DBCLOSE(d); +exim_dbclose(d); (void)fclose(f); /* If successful, output the number of entries and rename the temporary diff --git a/src/src/exim_dbutil.c b/src/src/exim_dbutil.c index c536a2037..1d87cec17 100644 --- a/src/src/exim_dbutil.c +++ b/src/src/exim_dbutil.c @@ -84,31 +84,6 @@ BOOL allow_insecure_tainted_data; /******************************************************************************/ -/************************************************* -* Berkeley DB error callback * -*************************************************/ - -/* For Berkeley DB >= 2, we can define a function to be called in case of DB -errors. This should help with debugging strange DB problems, e.g. getting "File -exists" when you try to open a db file. The API changed at release 4.3. */ - -#if defined(USE_DB) && defined(DB_VERSION_STRING) -void -#if DB_VERSION_MAJOR > 4 || (DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR >= 3) -dbfn_bdb_error_callback(const DB_ENV *dbenv, const char *pfx, const char *msg) -{ -dbenv = dbenv; -#else -dbfn_bdb_error_callback(const char *pfx, char *msg) -{ -#endif -pfx = pfx; -printf("Berkeley DB error: %s\n", msg); -} -#endif - - - /************************************************* * SIGALRM handler * *************************************************/ @@ -364,7 +339,7 @@ if (asprintf(CSS &filename, "%s/%s", dirname, name) < 0) return NULL; #else filename = string_sprintf("%s/%s", dirname, name); #endif -EXIM_DBOPEN(filename, dirname, flags, 0, &dbblock->dbptr); +dbblock->dbptr = exim_dbopen(filename, dirname, flags, 0); if (!dbblock->dbptr) { @@ -400,7 +375,7 @@ Returns: nothing void dbfn_close(open_db *dbblock) { -EXIM_DBCLOSE(dbblock->dbptr); +exim_dbclose(dbblock->dbptr); (void)close(dbblock->lockfd); } @@ -434,21 +409,21 @@ uschar * key_copy = store_get(klen, key); memcpy(key_copy, key, klen); -EXIM_DATUM_INIT(key_datum); /* Some DBM libraries require the datum */ -EXIM_DATUM_INIT(result_datum); /* to be cleared before use. */ -EXIM_DATUM_DATA(key_datum) = (void *) key_copy; -EXIM_DATUM_SIZE(key_datum) = klen; +exim_datum_init(&key_datum); /* Some DBM libraries require the datum */ +exim_datum_init(&result_datum); /* to be cleared before use. */ +exim_datum_data_set(&key_datum, key_copy); +exim_datum_size_set(&key_datum, klen); -if (!EXIM_DBGET(dbblock->dbptr, key_datum, result_datum)) return NULL; +if (!exim_dbget(dbblock->dbptr, &key_datum, &result_datum)) return NULL; /* Assume for now that anything stored could have been tainted. Properly we should store the taint status along with the data. */ -yield = store_get(EXIM_DATUM_SIZE(result_datum), GET_TAINTED); -memcpy(yield, EXIM_DATUM_DATA(result_datum), EXIM_DATUM_SIZE(result_datum)); -if (length) *length = EXIM_DATUM_SIZE(result_datum); +yield = store_get(exim_datum_size_get(&result_datum), GET_TAINTED); +memcpy(yield, exim_datum_data_get(&result_datum), exim_datum_size_get(&result_datum)); +if (length) *length = exim_datum_size_get(&result_datum); -EXIM_DATUM_FREE(result_datum); /* Some DBM libs require freeing */ +exim_datum_free(&result_datum); /* Some DBM libs require freeing */ return yield; } @@ -482,13 +457,13 @@ uschar * key_copy = store_get(klen, key); memcpy(key_copy, key, klen); gptr->time_stamp = time(NULL); -EXIM_DATUM_INIT(key_datum); /* Some DBM libraries require the datum */ -EXIM_DATUM_INIT(value_datum); /* to be cleared before use. */ -EXIM_DATUM_DATA(key_datum) = (void *) key_copy; -EXIM_DATUM_SIZE(key_datum) = klen; -EXIM_DATUM_DATA(value_datum) = (void *) ptr; -EXIM_DATUM_SIZE(value_datum) = length; -return EXIM_DBPUT(dbblock->dbptr, key_datum, value_datum); +exim_datum_init(&key_datum); /* Some DBM libraries require the datum */ +exim_datum_init(&value_datum); /* to be cleared before use. */ +exim_datum_data_set(&key_datum, key_copy); +exim_datum_size_set(&key_datum, klen); +exim_datum_data_set(&value_datum, ptr); +exim_datum_size_set(&value_datum, length); +return exim_dbput(dbblock->dbptr, &key_datum, &value_datum); } @@ -510,13 +485,13 @@ dbfn_delete(open_db *dbblock, const uschar *key) { int klen = Ustrlen(key) + 1; uschar * key_copy = store_get(klen, key); +EXIM_DATUM key_datum; memcpy(key_copy, key, klen); -EXIM_DATUM key_datum; -EXIM_DATUM_INIT(key_datum); /* Some DBM libraries require clearing */ -EXIM_DATUM_DATA(key_datum) = (void *) key_copy; -EXIM_DATUM_SIZE(key_datum) = klen; -return EXIM_DBDEL(dbblock->dbptr, key_datum); +exim_datum_init(&key_datum); /* Some DBM libraries require clearing */ +exim_datum_data_set(&key_datum, key_copy); +exim_datum_size_set(&key_datum, klen); +return exim_dbdel(dbblock->dbptr, &key_datum); } #endif /* EXIM_TIDYDB || EXIM_FIXDB */ @@ -548,17 +523,17 @@ uschar *yield; /* Some dbm require an initialization */ -if (start) EXIM_DBCREATE_CURSOR(dbblock->dbptr, cursor); +if (start) *cursor = exim_dbcreate_cursor(dbblock->dbptr); -EXIM_DATUM_INIT(key_datum); /* Some DBM libraries require the datum */ -EXIM_DATUM_INIT(value_datum); /* to be cleared before use. */ +exim_datum_init(&key_datum); /* Some DBM libraries require the datum */ +exim_datum_init(&value_datum); /* to be cleared before use. */ -yield = (EXIM_DBSCAN(dbblock->dbptr, key_datum, value_datum, start, *cursor))? - US EXIM_DATUM_DATA(key_datum) : NULL; +yield = exim_dbscan(dbblock->dbptr, &key_datum, &value_datum, start, *cursor) + ? US exim_datum_data_get(&key_datum) : NULL; /* Some dbm require a termination */ -if (!yield) EXIM_DBDELETE_CURSOR(*cursor); +if (!yield) exim_dbdelete_cursor(*cursor); return yield; } #endif /* EXIM_DUMPDB || EXIM_TIDYDB */ diff --git a/src/src/filter.c b/src/src/filter.c index 629bbf536..210f7b0e2 100644 --- a/src/src/filter.c +++ b/src/src/filter.c @@ -1290,18 +1290,18 @@ switch (command) if (command == vacation_command) { - if (new->args[mailarg_index_file].u == NULL) + if (!new->args[mailarg_index_file].u) { new->args[mailarg_index_file].u = string_copy(US".vacation.msg"); new->args[mailarg_index_expand].u = US""; /* not NULL => TRUE */ } - if (new->args[mailarg_index_log].u == NULL) + if (!new->args[mailarg_index_log].u) new->args[mailarg_index_log].u = string_copy(US".vacation.log"); - if (new->args[mailarg_index_once].u == NULL) + if (!new->args[mailarg_index_once].u) new->args[mailarg_index_once].u = string_copy(US".vacation"); - if (new->args[mailarg_index_once_repeat].u == NULL) + if (!new->args[mailarg_index_once_repeat].u) new->args[mailarg_index_once_repeat].u = string_copy(US"7d"); - if (new->args[mailarg_index_subject].u == NULL) + if (!new->args[mailarg_index_subject].u) new->args[mailarg_index_subject].u = string_copy(US"On vacation"); } diff --git a/src/src/hintsdb.h b/src/src/hintsdb.h new file mode 100644 index 000000000..de3e6e4aa --- /dev/null +++ b/src/src/hintsdb.h @@ -0,0 +1,805 @@ +/************************************************* +* Exim - an Internet mail transport agent * +*************************************************/ + +/* Copyright (c) University of Cambridge 1995 - 2018 */ +/* Copyright (c) The Exim Maintainers 2020 - 2022 */ +/* See the file NOTICE for conditions of use and distribution. */ + +/* This header file contains macro definitions so that a variety of DBM +libraries can be used by Exim. Nigel Metheringham provided the original set for +Berkeley DB 1.x in native mode and ndbm. Subsequently, versions for Berkeley DB +2.x and 3.x were added. Later still, support for tdb was added, courtesy of +James Antill. Most recently, support for native mode gdbm was added, with code +from Pierre A. Humblet, so Exim could be made to work with Cygwin. + +For convenience, the definitions of the structures used in the various hints +databases are also kept in this file, which is used by the maintenance +utilities as well as the main Exim binary. */ + +#ifndef HINTSDB_H +#define HINTSDB_H + + +#if defined(USE_TDB) + +/* ************************* tdb interface ************************ */ +/*XXX https://manpages.org/tdb/3 mentions concurrent writes. +Could we lose the file lock? */ + +# include + +/* Basic DB type */ +# define EXIM_DB TDB_CONTEXT + +/* Cursor type: tdb uses the previous "key" in _nextkey() (really it wants +tdb_traverse to be called) */ +# define EXIM_CURSOR TDB_DATA + +/* The datum type used for queries */ +# define EXIM_DATUM TDB_DATA + +/* Some text for messages */ +# define EXIM_DBTYPE "tdb" + +/* Access functions */ + +/* EXIM_DBOPEN - return pointer to an EXIM_DB, NULL if failed */ +static EXIM_DB * +exim_dbopen__(const uschar * name, const uschar * dirname, int flags, + unsigned mode) +{ +return tdb_open(CS name, 0, TDB_DEFAULT, flags, mode); +} + +/* EXIM_DBGET - returns TRUE if successful, FALSE otherwise */ +static BOOL +exim_dbget(EXIM_DB * dbp, EXIM_DATUM * key, EXIM_DATUM * res) +{ +*res = tdb_fetch(dbp, *key); /* A struct arg and return!! */ +return res->dptr != NULL; +} + +/* EXIM_DBPUT - returns nothing useful, assumes replace mode */ +static int +exim_dbput(EXIM_DB * dbp, EXIM_DATUM * key, EXIM_DATUM * data) +{ return tdb_store(dbp, *key, *data, TDB_REPLACE); } + +/* EXIM_DBPUTB - non-overwriting for use by dbmbuild */ +static int +exim_dbputb(EXIM_DB * dbp, EXIM_DATUM * key, EXIM_DATUM * data) +{ return tdb_store(dbp, *key, *data, TDB_INSERT); } + +/* Returns from EXIM_DBPUTB */ + +# define EXIM_DBPUTB_OK 0 +# define EXIM_DBPUTB_DUP (-1) + +/* EXIM_DBDEL */ +static int +exim_dbdel(EXIM_DB * dbp, EXIM_DATUM * key) +{ return tdb_delete(dbp, *key); } + +/* EXIM_DBCREATE_CURSOR - initialize for scanning operation */ +static EXIM_CURSOR * +exim_dbcreate_cursor(EXIM_DB * dbp) +{ +EXIM_CURSOR * c = store_malloc(sizeof(TDB_DATA)); +c->dptr = NULL; +return c; +} + +/* EXIM_DBSCAN - This is complicated because we have to free the last datum +free() must not die when passed NULL */ + +static BOOL +exim_dbscan(EXIM_DB * dbp, EXIM_DATUM * key, EXIM_DATUM * res, BOOL first, + EXIM_CURSOR * cursor) +{ +*key = first ? tdb_firstkey(dbp) : tdb_nextkey(dbp, *cursor); +free(cursor->dptr); +*cursor = *key; +return key->dptr != NULL; +} + +/* EXIM_DBDELETE_CURSOR - terminate scanning operation. */ +static void +exim_dbdelete_cursor(EXIM_CURSOR * cursor) +{ store_free(cursor); } + +/* EXIM_DBCLOSE */ +static void +exim_dbclose__(EXIM_DB * db) +{ tdb_close(db); } + +/* Datum access */ + +static uschar * +exim_datum_data_get(EXIM_DATUM * dp) +{ return US dp->dptr; } +static void +exim_datum_data_set(EXIM_DATUM * dp, void * s) +{ dp->dptr = s; } + +static unsigned +exim_datum_size_get(EXIM_DATUM * dp) +{ return dp->dsize; } +static void +exim_datum_size_set(EXIM_DATUM * dp, unsigned n) +{ dp->dsize = n; } + +/* No initialization is needed. */ + +static void +exim_datum_init(EXIM_DATUM * d) +{ } + +/* Free the stuff inside the datum. */ + +static void +exim_datum_free(EXIM_DATUM * d) +{ +free(d->dptr); +d->dptr = NULL; +} + +/* size limit */ + +# define EXIM_DB_RLIMIT 150 + + + + + + +/********************* Berkeley db native definitions **********************/ + +#elif defined USE_DB + +# include + +/* 1.x did no locking + 2.x had facilities, but exim does it's own + 3.x+ unknown +*/ + +/* We can distinguish between versions 1.x and 2.x/3.x by looking for a +definition of DB_VERSION_STRING, which is present in versions 2.x onwards. */ + +# ifdef DB_VERSION_STRING + +# if DB_VERSION_MAJOR >= 6 +# error Version 6 and later BDB API is not supported +# endif + +/* The API changed (again!) between the 2.x and 3.x versions */ + +# if DB_VERSION_MAJOR >= 3 + +/***************** Berkeley db 3.x/4.x native definitions ******************/ + +/* Basic DB type */ +# if DB_VERSION_MAJOR > 4 || (DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR >= 1) +# define EXIM_DB DB_ENV +/* Cursor type, for scanning */ +# define EXIM_CURSOR DBC + +/* The datum type used for queries */ +# define EXIM_DATUM DBT + +/* Some text for messages */ +# define EXIM_DBTYPE "db (v4.1+)" + +/* Only more-recent versions. 5+ ? */ +# ifndef DB_FORCESYNC +# define DB_FORCESYNC 0 +# endif + +/* Error callback */ +/* For Berkeley DB >= 2, we can define a function to be called in case of DB +errors. This should help with debugging strange DB problems, e.g. getting "File +exists" when you try to open a db file. The API for this function was changed +at DB release 4.3. */ + +static void +dbfn_bdb_error_callback(const DB_ENV * dbenv, const char * pfx, const char * msg) +{ log_write(0, LOG_MAIN, "Berkeley DB error: %s", msg); } + + + +/* Access functions */ + +/* EXIM_DBOPEN - return pointer to an EXIM_DB, NULL if failed */ +/* The API changed for DB 4.1. - and we also starting using the "env" with a +specified working dir, to avoid the DBCONFIG file trap. */ + +# define ENV_TO_DB(env) ((DB *)(((EXIM_DB *)env)->app_private)) + +static EXIM_DB * +exim_dbopen__(const uschar * name, const uschar * dirname, int flags, + unsigned mode) +{ +EXIM_DB * dbp; +DB * b; +if ( db_env_create(&dbp, 0) != 0 + || (dbp->set_errcall(dbp, dbfn_bdb_error_callback), 0) + || dbp->open(dbp, CS dirname, DB_CREATE|DB_INIT_MPOOL|DB_PRIVATE, 0) != 0 + ) + return NULL; +if (db_create(&b, dbp, 0) == 0) + { + dbp->app_private = b; + if (b->open(b, NULL, CS name, NULL, + flags == O_RDONLY ? DB_UNKNOWN : DB_HASH, + flags == O_RDONLY ? DB_RDONLY : DB_CREATE, + mode) == 0 + ) + return dbp; + + b->close(b, 0); + } +dbp->close(dbp, 0); +return NULL; +} + +/* EXIM_DBGET - returns TRUE if successful, FALSE otherwise */ +static BOOL +exim_dbget(EXIM_DB * dbp, EXIM_DATUM * key, EXIM_DATUM * res) +{ +DB * b = ENV_TO_DB(dbp); +return b->get(b, NULL, key, res, 0) == 0; +} + +/* EXIM_DBPUT - returns nothing useful, assumes replace mode */ +static int +exim_dbput(EXIM_DB * dbp, EXIM_DATUM * key, EXIM_DATUM * data) +{ +DB * b = ENV_TO_DB(dbp); +return b->put(b, NULL, key, data, 0); +} + +/* EXIM_DBPUTB - non-overwriting for use by dbmbuild */ +static int +exim_dbputb(EXIM_DB * dbp, EXIM_DATUM * key, EXIM_DATUM * data) +{ +DB * b = ENV_TO_DB(dbp); +return b->put(b, NULL, key, data, DB_NOOVERWRITE); +} + +/* Return values from EXIM_DBPUTB */ + +# define EXIM_DBPUTB_OK 0 +# define EXIM_DBPUTB_DUP DB_KEYEXIST + +/* EXIM_DBDEL */ +static int +exim_dbdel(EXIM_DB * dbp, EXIM_DATUM * key) +{ +DB * b = ENV_TO_DB(dbp); +return b->del(b, NULL, key, 0); +} + +/* EXIM_DBCREATE_CURSOR - initialize for scanning operation */ + +static EXIM_CURSOR * +exim_dbcreate_cursor(EXIM_DB * dbp) +{ +DB * b = ENV_TO_DB(dbp); +EXIM_CURSOR * c; +b->cursor(b, NULL, &c, 0); +return c; +} + +/* EXIM_DBSCAN - returns TRUE if data is returned, FALSE at end */ +static BOOL +exim_dbscan(EXIM_DB * dbp, EXIM_DATUM * key, EXIM_DATUM * data, BOOL first, + EXIM_CURSOR * cursor) +{ +return cursor->c_get(cursor, key, data, first ? DB_FIRST : DB_NEXT) == 0; +} + +/* EXIM_DBDELETE_CURSOR - terminate scanning operation */ +static void +exim_dbdelete_cursor(EXIM_CURSOR * cursor) +{ cursor->c_close(cursor); } + +/* EXIM_DBCLOSE */ +static void +exim_dbclose__(EXIM_DB * dbp_o) +{ +DB_ENV * dbp = dbp_o; +DB * b = ENV_TO_DB(dbp); +b->close(b, 0); +dbp->close(dbp, DB_FORCESYNC); +} + +/* Datum access */ + +static uschar * +exim_datum_data_get(EXIM_DATUM * dp) +{ return dp->data; } +static void +exim_datum_data_set(EXIM_DATUM * dp, void * s) +{ dp->data = s; } + +static unsigned +exim_datum_size_get(EXIM_DATUM * dp) +{ return dp->size; } +static void +exim_datum_size_set(EXIM_DATUM * dp, unsigned n) +{ dp->size = n; } + +/* The whole datum structure contains other fields that must be cleared +before use, but we don't have to free anything after reading data. */ + +static void +exim_datum_init(EXIM_DATUM * d) +{ memset(d, 0, sizeof(*d)); } + +static void +exim_datum_free(EXIM_DATUM * d) +{ } + +# else /* pre- 4.1 */ + +# define EXIM_DB DB + +/* Cursor type, for scanning */ +# define EXIM_CURSOR DBC + +/* The datum type used for queries */ +# define EXIM_DATUM DBT + +/* Some text for messages */ +# define EXIM_DBTYPE "db (v3/4)" + +/* Access functions */ + +/* EXIM_DBOPEN - return pointer to an EXIM_DB, NULL if failed */ +static EXIM_DB * +exim_dbopen__(const uschar * name, const uschar * dirname, int flags, + unsigned mode) +{ +EXIM_DB * dbp; +return db_create(&dbp, NULL, 0) == 0 + && ( dbp->set_errcall(dbp, dbfn_bdb_error_callback), + dbp->open(dbp, CS name, NULL, + flags == O_RDONLY ? DB_UNKNOWN : DB_HASH, + flags == O_RDONLY ? DB_RDONLY : DB_CREATE, + mode) + ) == 0 + ? dbp : NULL; +} + +/* EXIM_DBGET - returns TRUE if successful, FALSE otherwise */ +static BOOL +exim_dbget(EXIM_DB * dbp, EXIM_DATUM * key, EXIM_DATUM * res) +{ return dbp->get(dbp, NULL, key, res, 0) == 0; } + +/* EXIM_DBPUT - returns nothing useful, assumes replace mode */ +static int +exim_dbput(EXIM_DB * dbp, EXIM_DATUM * key, EXIM_DATUM * data) +{ return dbp->put(dbp, NULL, key, data, 0); } + +/* EXIM_DBPUTB - non-overwriting for use by dbmbuild */ +static int +exim_dbputb(EXIM_DB * dbp, EXIM_DATUM * key, EXIM_DATUM * data) +{ return dbp->put(dbp, NULL, key, data, DB_NOOVERWRITE); } + +/* Return values from EXIM_DBPUTB */ + +# define EXIM_DBPUTB_OK 0 +# define EXIM_DBPUTB_DUP DB_KEYEXIST + +/* EXIM_DBDEL */ +static int +exim_dbdel(EXIM_DB * dbp, EXIM_DATUM * key) +{ return dbp->del(dbp, NULL, key, 0); } + +/* EXIM_DBCREATE_CURSOR - initialize for scanning operation */ + +static EXIM_CURSOR * +exim_dbcreate_cursor(EXIM_DB * dbp) +{ +EXIM_CURSOR * c; +dbp->cursor(dbp, NULL, &c, 0); +return c; +} + +/* EXIM_DBSCAN - returns TRUE if data is returned, FALSE at end */ +static BOOL +exim_dbscan(EXIM_DB * dbp, EXIM_DATUM * key, EXIM_DATUM * data, BOOL first, + EXIM_CURSOR * cursor) +{ +return cursor->c_get(cursor, key, data, first ? DB_FIRST : DB_NEXT) == 0; +} + +/* EXIM_DBDELETE_CURSOR - terminate scanning operation */ +static void +exim_dbdelete_cursor(EXIM_CURSOR * cursor) +{ cursor->c_close(cursor); } + +/* EXIM_DBCLOSE */ +static void +exim_dbclose__(EXIM_DB * dbp) +{ dbp->close(dbp, 0); } + +/* Datum access */ + +static uschar * +exim_datum_data_get(EXIM_DATUM * dp) +{ return US dp->dptr; } +static void +exim_datum_data_set(EXIM_DATUM * dp, void * s) +{ dp->dptr = s; } + +static uschar * +exim_datum_size_get(EXIM_DATUM * dp) +{ return US dp->size; } +static void +exim_datum_size_set(EXIM_DATUM * dp, uschar * s) +{ dp->size = CS s; } + +/* The whole datum structure contains other fields that must be cleared +before use, but we don't have to free anything after reading data. */ + +static void +exim_datum_init(EXIM_DATUM * d) +{ memset(d, 0, sizeof(*d)); } + +static void +exim_datum_free(EXIM_DATUM * d) +{ } + +# endif + + +# else /* DB_VERSION_MAJOR >= 3 */ +# error Berkeley DB versions earlier than 3 are not supported */ +# endif /* DB_VERSION_MAJOR */ +# else +# error Berkeley DB version 1 is no longer supported +# endif /* DB_VERSION_STRING */ + + +/* all BDB versions */ +/* size limit */ + +# define EXIM_DB_RLIMIT 150 + + + + + + +/********************* gdbm interface definitions **********************/ + +#elif defined USE_GDBM +/*XXX TODO: exim's locfile not needed */ + +# include + +/* Basic DB type */ +typedef struct { + GDBM_FILE gdbm; /* Database */ + datum lkey; /* Last key, for scans */ +} EXIM_DB; + +/* Cursor type, not used with gdbm: just set up a dummy */ +# define EXIM_CURSOR int + +/* The datum type used for queries */ +# define EXIM_DATUM datum + +/* Some text for messages */ + +# define EXIM_DBTYPE "gdbm" + +/* Access functions */ + +/* EXIM_DBOPEN - return pointer to an EXIM_DB, NULL if failed */ +static EXIM_DB * +exim_dbopen__(const uschar * name, const uschar * dirname, int flags, + unsigned mode) +{ +EXIM_DB * dbp = malloc(sizeof(EXIM_DB)); /*XXX why not exim mem-mgmt? */ +if (dbp) + { + dbp->lkey.dptr = NULL; + dbp->gdbm = gdbm_open(CS name, 0, + flags & O_CREAT ? GDBM_WRCREAT + : flags & (O_RDWR|O_WRONLY) ? GDBM_WRITER + : GDBM_READER, + mode, 0); + if (dbp->gdbm) return dbp; + free(dbp); + } +return NULL; +} + +/* EXIM_DBGET - returns TRUE if successful, FALSE otherwise */ +static BOOL +exim_dbget(EXIM_DB * dbp, EXIM_DATUM * key, EXIM_DATUM * res) +{ +*res = gdbm_fetch(dbp->gdbm, *key); /* A struct arg & return! */ +return res->dptr != NULL; +} + +/* EXIM_DBPUT - returns nothing useful, assumes replace mode */ +static int +exim_dbput(EXIM_DB * dbp, EXIM_DATUM * key, EXIM_DATUM * data) +{ return gdbm_store(dbp->gdbm, *key, *data, GDBM_REPLACE); } + +/* EXIM_DBPUTB - non-overwriting for use by dbmbuild */ +static int +exim_dbputb(EXIM_DB * dbp, EXIM_DATUM * key, EXIM_DATUM * data) +{ return gdbm_store(dbp->gdbm, *key, *data, GDBM_INSERT); } + +/* Returns from EXIM_DBPUTB */ + +# define EXIM_DBPUTB_OK 0 +# define EXIM_DBPUTB_DUP 1 + +/* EXIM_DBDEL */ +static int +exim_dbdel(EXIM_DB * dbp, EXIM_DATUM * key) +{ return gdbm_delete(dbp->gdbm, *key); } + +/* EXIM_DBCREATE_CURSOR - initialize for scanning operation (null) */ +static EXIM_CURSOR * +exim_dbcreate_cursor(EXIM_DB * dbp) +{ return NULL; } + +/* EXIM_DBSCAN */ +static BOOL +exim_dbscan(EXIM_DB * dbp, EXIM_DATUM * key, EXIM_DATUM * data, BOOL first, + EXIM_CURSOR * cursor) +{ +char * s; +*key = first ? gdbm_firstkey(dbp->gdbm) : gdbm_nextkey(dbp->gdbm, dbp->lkey); +if ((s = dbp->lkey.dptr)) free(s); +dbp->lkey = *key; +return key->dptr != NULL; +} + +/* EXIM_DBDELETE_CURSOR - terminate scanning operation (null). */ +static void +exim_dbdelete_cursor(EXIM_CURSOR * cursor) +{ } + +/* EXIM_DBCLOSE */ +static void +exim_dbclose__(EXIM_DB * dbp) +{ +char * s; +gdbm_close(dbp->gdbm); +if ((s = dbp->lkey.dptr)) free(s); +free(dbp); +} + +/* Datum access types */ + +static uschar * +exim_datum_data_get(EXIM_DATUM * dp) +{ return US dp->dptr; } +static void +exim_datum_data_set(EXIM_DATUM * dp, void * s) +{ dp->dptr = s; } + +static unsigned +exim_datum_size_get(EXIM_DATUM * dp) +{ return dp->dsize; } +static void +exim_datum_size_set(EXIM_DATUM * dp, unsigned n) +{ dp->dsize = n; } + +/* There's no clearing required before use, but we have to free the dptr +after reading data. */ + +static void +exim_datum_init(EXIM_DATUM * d) +{ } + +static void +exim_datum_free(EXIM_DATUM * d) +{ free(d->dptr); } + +/* size limit */ + +# define EXIM_DB_RLIMIT 150 + +#else /* USE_GDBM */ + + + + + + +/* If none of USE_DB, USG_GDBM, or USE_TDB are set, the default is the NDBM +interface */ + + +/********************* ndbm interface definitions **********************/ + +# include + +/* Basic DB type */ +# define EXIM_DB DBM + +/* Cursor type, not used with ndbm: just set up a dummy */ +# define EXIM_CURSOR int + +/* The datum type used for queries */ +# define EXIM_DATUM datum + +/* Some text for messages */ + +# define EXIM_DBTYPE "ndbm" + +/* Access functions */ + +/* EXIM_DBOPEN - returns a EXIM_DB *, NULL if failed */ +/* Check that the name given is not present. This catches +a directory name; otherwise we would create the name.pag and +name.dir files in the directory's parent. */ + +static EXIM_DB * +exim_dbopen__(const uschar * name, const uschar * dirname, int flags, + unsigned mode) +{ +struct stat st; +if (!(flags & O_CREAT) || lstat(CCS name, &st) != 0 && errno == ENOENT) + return dbm_open(CS name, flags, mode); +#ifndef COMPILE_UTILITY +debug_printf("%s %d errno %s\n", __FUNCTION__, __LINE__, strerror(errno)); +#endif +errno = (st.st_mode & S_IFMT) == S_IFDIR ? EISDIR : EEXIST; +return NULL; +} + +/* EXIM_DBGET - returns TRUE if successful, FALSE otherwise */ +static BOOL +exim_dbget(EXIM_DB * dbp, EXIM_DATUM * key, EXIM_DATUM * res) +{ +*res = dbm_fetch(dbp, *key); /* A struct arg & return! */ +return res->dptr != NULL; +} + +/* EXIM_DBPUT - returns nothing useful, assumes replace mode */ +static int +exim_dbput(EXIM_DB * dbp, EXIM_DATUM * key, EXIM_DATUM * data) +{ return dbm_store(dbp, *key, *data, DBM_REPLACE); } + +/* EXIM_DBPUTB - non-overwriting for use by dbmbuild */ +static int +exim_dbputb(EXIM_DB * dbp, EXIM_DATUM * key, EXIM_DATUM * data) +{ return dbm_store(dbp, *key, *data, DBM_INSERT); } + +/* Returns from EXIM_DBPUTB */ + +# define EXIM_DBPUTB_OK 0 +# define EXIM_DBPUTB_DUP 1 + +/* EXIM_DBDEL */ +static int +exim_dbdel(EXIM_DB * dbp, EXIM_DATUM * key) +{ return dbm_delete(dbp, *key); } + +/* EXIM_DBCREATE_CURSOR - initialize for scanning operation (null) */ +static EXIM_CURSOR * +exim_dbcreate_cursor(EXIM_DB * dbp) +{ return NULL; } + +/* EXIM_DBSCAN */ +static BOOL +exim_dbscan(EXIM_DB * dbp, EXIM_DATUM * key, EXIM_DATUM * data, BOOL first, + EXIM_CURSOR * cursor) +{ +*key = first ? dbm_firstkey(dbp) : dbm_nextkey(dbp); +return key->dptr != NULL; +} + +/* EXIM_DBDELETE_CURSOR - terminate scanning operation (null). */ +static void +exim_dbdelete_cursor(EXIM_CURSOR * cursor) +{ } + +/* EXIM_DBCLOSE */ +static void +exim_dbclose__(EXIM_DB * dbp) +{ dbm_close(dbp); } + +/* Datum access types */ + +static uschar * +exim_datum_data_get(EXIM_DATUM * dp) +{ return US dp->dptr; } +static void +exim_datum_data_set(EXIM_DATUM * dp, void * s) +{ dp->dptr = s; } + +static unsigned +exim_datum_size_get(EXIM_DATUM * dp) +{ return dp->dsize; } +static void +exim_datum_size_set(EXIM_DATUM * dp, unsigned n) +{ dp->dsize = n; } + +/* There's no clearing required before use, and we don't have to free anything +after reading data. */ + +static void +exim_datum_init(EXIM_DATUM * d) +{ } + +static void +exim_datum_free(EXIM_DATUM * d) +{ } + +/* size limit */ + +# define EXIM_DB_RLIMIT 150 + +#endif /* USE_GDBM */ + + + + + +#if defined(COMPILE_UTILITY) || defined(MACRO_PREDEF) + +static EXIM_DB * +exim_dbopen(const uschar * name, const uschar * dirname, int flags, + unsigned mode) +{ +return exim_dbopen__(name, dirname, flags, mode); +} + +static void +exim_dbclose(EXIM_DB * dbp) +{ exim_dbclose__(dbp); } + +#else +/* Wrappers for open/close with debug tracing */ + +extern void debug_printf_indent(const char *, ...); +static BOOL is_tainted(const void *); + +static EXIM_DB * +exim_dbopen(const uschar * name, const uschar * dirname, int flags, + unsigned mode) +{ +void * dbp; +DEBUG(D_hints_lookup) + debug_printf_indent("EXIM_DBOPEN: file <%s> dir <%s> flags=%s\n", + name, dirname, + flags == O_RDONLY ? "O_RDONLY" + : flags == O_RDWR ? "O_RDWR" + : flags == (O_RDWR|O_CREAT) ? "O_RDWR|O_CREAT" + : "??"); +if (is_tainted(name) || is_tainted(dirname)) + { + log_write(0, LOG_MAIN|LOG_PANIC, "Tainted name for DB file not permitted"); + dbp = NULL; + } +else + dbp = exim_dbopen__(name, dirname, flags, mode); + +DEBUG(D_hints_lookup) debug_printf_indent("returned from EXIM_DBOPEN: %p\n", dbp); +return dbp; +} + +static void +exim_dbclose(EXIM_DB * dbp) +{ +DEBUG(D_hints_lookup) debug_printf_indent("EXIM_DBCLOSE(%p)\n", dbp); +exim_dbclose__(dbp); +} + +# endif + +/********************* End of dbm library definitions **********************/ + + +#endif /* whole file */ +/* End of hintsdb.h */ diff --git a/src/src/hintsdb_structs.h b/src/src/hintsdb_structs.h new file mode 100644 index 000000000..e0670a10e --- /dev/null +++ b/src/src/hintsdb_structs.h @@ -0,0 +1,189 @@ +/************************************************* +* Exim - an Internet mail transport agent * +*************************************************/ + +/* Copyright (c) University of Cambridge 1995 - 2018 */ +/* Copyright (c) The Exim Maintainers 2020 - 2021 */ +/* See the file NOTICE for conditions of use and distribution. */ + +/* This header file contains the definitions of the structures used in the +various hints databases are also kept in this file, which is used by the +maintenance utilities as well as the main Exim binary. */ + +#ifndef HINTSDB_STRUCTS_H +#define HINTSDB_STRUCTS_H + + +/* Structure for carrying around an open DBM file, and an open locking file +that relates to it. */ + +typedef struct { + void * dbptr; + int lockfd; +} open_db; + + +/* Structures for records stored in exim database dbm files. They all +start with the same fields, described in the generic type. */ + + +typedef struct { + time_t time_stamp; /* Timestamp of writing */ +} dbdata_generic; + + +/* This structure keeps track of retry information for a host or a local +address. */ + +typedef struct { + time_t time_stamp; + /*************/ + time_t first_failed; /* Time of first failure */ + time_t last_try; /* Time of last try */ + time_t next_try; /* Time of next try */ + BOOL expired; /* Retry time has expired */ + int basic_errno; /* Errno of last failure */ + int more_errno; /* Additional information */ + uschar text[1]; /* Text message for last failure */ +} dbdata_retry; + +/* These structures keep track of addresses that have had callout verification +performed on them. There are two groups of records: + +1. keyed by localpart@domain - + Full address was tested and record holds result + +2. keyed by domain - + Domain response upto MAIL FROM:<>, postmaster, random local part; + +If a record exists, the result field is either ccache_accept or ccache_reject, +or, for a domain record only, ccache_reject_mfnull when MAIL FROM:<> was +rejected. The other fields, however, (which are only relevant to domain +records) may also contain ccache_unknown if that particular test has not been +done. + +Originally, there was only one structure, used for both types. However, it got +expanded for domain records, so it got split. To make it possible for Exim to +handle the old type of record, we retain the old definition. The different +kinds of record can be distinguished by their different lengths. */ + +typedef struct { + time_t time_stamp; + /*************/ + int result; + int postmaster_result; /* Postmaster is accepted */ + int random_result; /* Random local part was accepted */ +} dbdata_callout_cache_obs; + +typedef struct { + time_t time_stamp; /* Timestamp of last address check */ + /*************/ + int result; /* accept or reject */ +} dbdata_callout_cache_address; + +/* For this new layout, we put the additional fields (the timestamps) +last so that if somebody reverts to an older Exim, the new records will +still make sense because they match the old layout. */ + +typedef struct { + time_t time_stamp; /* Time stamp of last connection */ + /*************/ + int result; /* Domain reject or accept */ + int postmaster_result; /* Postmaster result */ + int random_result; /* Random result */ + time_t postmaster_stamp; /* Timestamp of postmaster check */ + time_t random_stamp; /* Timestamp of random check */ +} dbdata_callout_cache; + +/* This structure keeps track of messages that are waiting for a particular +host for a particular transport. */ + +typedef struct { + time_t time_stamp; + /*************/ + int count; /* Count of message ids */ + int sequence; /* Sequence for continued records */ + uschar text[1]; /* One long character string */ +} dbdata_wait; + + +/* The contents of the "misc" database are a mixture of different kinds of +record, as defined below. The keys used for a specific type all start with a +given string such as "etrn-" or "host-serialize-". */ + + +/* This structure records a connection to a particular host, for the +purpose of serializing access to certain hosts. For possible future extension, +a field is defined for holding the count of connections, but it is not +at present in use. The same structure is used for recording a running ETRN +process. */ + +typedef struct { + time_t time_stamp; + /*************/ + int count; /* Reserved for possible connection count */ +} dbdata_serialize; + + +/* This structure records the information required for the ratelimit +ACL condition. */ + +typedef struct { + time_t time_stamp; + /*************/ + int time_usec; /* Fractional part of time, from gettimeofday() */ + double rate; /* Smoothed sending rate at that time */ +} dbdata_ratelimit; + +/* Same as above, plus a Bloom filter for uniquifying events. */ + +typedef struct { + dbdata_ratelimit dbd; + time_t bloom_epoch; /* When the Bloom filter was last reset */ + unsigned bloom_size; /* Number of bytes in the Bloom filter */ + uschar bloom[40]; /* Bloom filter which may be larger than this */ +} dbdata_ratelimit_unique; + + +/* For "seen" ACL condition */ +typedef struct { + time_t time_stamp; +} dbdata_seen; + +#ifndef DISABLE_PIPE_CONNECT +/* This structure records the EHLO responses, cleartext and crypted, +for an IP, as bitmasks (cf. OPTION_TLS). For LIMITS, also values +advertised for MAILMAX, RCPTMAX and RCPTDOMAINMAX; zero meaning no +value advertised. */ + +typedef struct { + unsigned short cleartext_features; + unsigned short crypted_features; + unsigned short cleartext_auths; + unsigned short crypted_auths; + +# ifdef EXPERIMENTAL_ESMTP_LIMITS + unsigned int limit_mail; + unsigned int limit_rcpt; + unsigned int limit_rcptdom; +# endif +} ehlo_resp_precis; + +typedef struct { + time_t time_stamp; + /*************/ + ehlo_resp_precis data; +} dbdata_ehlo_resp; +#endif + +typedef struct { + time_t time_stamp; + /*************/ + uschar verify_override:1; + uschar ocsp:3; + uschar session[1]; +} dbdata_tls_session; + + +#endif /* whole file */ +/* End of hintsdb_structs.h */ diff --git a/src/src/lookups/dbmdb.c b/src/src/lookups/dbmdb.c index 6db6dedb8..95f31bc16 100644 --- a/src/src/lookups/dbmdb.c +++ b/src/src/lookups/dbmdb.c @@ -21,11 +21,10 @@ dbmdb_open(const uschar * filename, uschar ** errmsg) { uschar * dirname = string_copy(filename); uschar * s; -EXIM_DB *yield = NULL; +EXIM_DB * yield = NULL; if ((s = Ustrrchr(dirname, '/'))) *s = '\0'; -EXIM_DBOPEN(filename, dirname, O_RDONLY, 0, &yield); -if (!yield) +if (!(yield = exim_dbopen(filename, dirname, O_RDONLY, 0))) *errmsg = string_open_failed("%s as a %s file", filename, EXIM_DBTYPE); return yield; } @@ -93,15 +92,15 @@ dbmdb_find(void * handle, const uschar * filename, const uschar * keystring, EXIM_DB *d = (EXIM_DB *)handle; EXIM_DATUM key, data; -EXIM_DATUM_INIT(key); /* Some DBM libraries require datums to */ -EXIM_DATUM_INIT(data); /* be cleared before use. */ -EXIM_DATUM_DATA(key) = (void *) keystring; -EXIM_DATUM_SIZE(key) = length + 1; +exim_datum_init(&key); /* Some DBM libraries require datums to */ +exim_datum_init(&data); /* be cleared before use. */ +exim_datum_data_set(&key, string_copyn(keystring, length)); +exim_datum_size_set(&key, length + 1); -if (EXIM_DBGET(d, key, data)) +if (exim_dbget(d, &key, &data)) { - *result = string_copyn(US EXIM_DATUM_DATA(data), EXIM_DATUM_SIZE(data)); - EXIM_DATUM_FREE(data); /* Some DBM libraries need a free() call */ + *result = string_copyn(exim_datum_data_get(&data), exim_datum_size_get(&data)); + exim_datum_free(&data); /* Some DBM libraries need a free() call */ return OK; } return FAIL; @@ -214,7 +213,7 @@ return dbmdb_find(handle, filename, key_buffer, key_item_len - 1, void static dbmdb_close(void *handle) { -EXIM_DBCLOSE((EXIM_DB *)handle); +exim_dbclose((EXIM_DB *)handle); } diff --git a/src/src/perl.c b/src/src/perl.c index 58643f0fa..c6d329102 100644 --- a/src/src/perl.c +++ b/src/src/perl.c @@ -15,6 +15,10 @@ /* See the file NOTICE for conditions of use and distribution. */ #include + +#define HINTSDB_H +#define DBFUNCTIONS_H + #include "exim.h" #define EXIM_TRUE TRUE diff --git a/src/src/transports/autoreply.c b/src/src/transports/autoreply.c index 77e769450..a2441d209 100644 --- a/src/src/transports/autoreply.c +++ b/src/src/transports/autoreply.c @@ -264,7 +264,7 @@ int fd, pid, rc; int cache_fd = -1; int cache_size = 0; int add_size = 0; -EXIM_DB *dbm_file = NULL; +EXIM_DB * dbm_file = NULL; BOOL file_expand, return_message; uschar *from, *reply_to, *to, *cc, *bcc, *subject, *headers, *text, *file; uschar *logfile, *oncelog; @@ -476,8 +476,7 @@ if (oncelog && *oncelog && to) dirname = (s = Ustrrchr(oncelog, '/')) ? string_copyn(oncelog, s - oncelog) : NULL; - EXIM_DBOPEN(oncelog, dirname, O_RDWR|O_CREAT, ob->mode, &dbm_file); - if (!dbm_file) + if (!(dbm_file = exim_dbopen(oncelog, dirname, O_RDWR|O_CREAT, ob->mode))) { addr->transport_return = DEFER; addr->basic_errno = errno; @@ -487,12 +486,12 @@ if (oncelog && *oncelog && to) goto END_OFF; } - EXIM_DATUM_INIT(key_datum); /* Some DBM libraries need datums */ - EXIM_DATUM_INIT(result_datum); /* to be cleared */ - EXIM_DATUM_DATA(key_datum) = (void *) to; - EXIM_DATUM_SIZE(key_datum) = Ustrlen(to) + 1; + exim_datum_init(&key_datum); /* Some DBM libraries need datums */ + exim_datum_init(&result_datum); /* to be cleared */ + exim_datum_data_set(&key_datum, (void *) to); + exim_datum_size_set(&key_datum, Ustrlen(to) + 1); - if (EXIM_DBGET(dbm_file, key_datum, result_datum)) + if (exim_dbget(dbm_file, &key_datum, &result_datum)) { /* If the datum size is that of a binary time, we are in the new world where messages are sent periodically. Otherwise the file is an old one, @@ -501,8 +500,8 @@ if (oncelog && *oncelog && to) introduced at Exim 3.00. In a couple of years' time the test on the size can be abolished. */ - if (EXIM_DATUM_SIZE(result_datum) == sizeof(time_t)) - memcpy(&then, EXIM_DATUM_DATA(result_datum), sizeof(time_t)); + if (exim_datum_size_get(&result_datum) == sizeof(time_t)) + memcpy(&then, exim_datum_data_get(&result_datum), sizeof(time_t)); else then = now; } @@ -575,7 +574,7 @@ if ((pid = child_open_exim(&fd, US"autoreply")) < 0) addr->message = string_sprintf("Failed to create child process to send " "message from %s transport: %s", tblock->name, strerror(errno)); DEBUG(D_transport) debug_printf("%s\n", addr->message); - if (dbm_file) EXIM_DBCLOSE(dbm_file); + if (dbm_file) exim_dbclose(dbm_file); return FALSE; } @@ -738,18 +737,18 @@ if (cache_fd >= 0) else if (dbm_file) { EXIM_DATUM key_datum, value_datum; - EXIM_DATUM_INIT(key_datum); /* Some DBM libraries need to have */ - EXIM_DATUM_INIT(value_datum); /* cleared datums. */ - EXIM_DATUM_DATA(key_datum) = (void *) to; - EXIM_DATUM_SIZE(key_datum) = Ustrlen(to) + 1; + exim_datum_init(&key_datum); /* Some DBM libraries need to have */ + exim_datum_init(&value_datum); /* cleared datums. */ + exim_datum_data_set(&key_datum, to); + exim_datum_size_set(&key_datum, Ustrlen(to) + 1); /* Many OS define the datum value, sensibly, as a void *. However, there are some which still have char *. By casting this address to a char * we can avoid warning messages from the char * systems. */ - EXIM_DATUM_DATA(value_datum) = (void *) &now; - EXIM_DATUM_SIZE(value_datum) = (int)sizeof(time_t); - EXIM_DBPUT(dbm_file, key_datum, value_datum); + exim_datum_data_set(&value_datum, &now); + exim_datum_size_set(&value_datum, sizeof(time_t)); + exim_dbput(dbm_file, &key_datum, &value_datum); } /* If sending failed, defer to try again - but if once is set the next @@ -812,7 +811,7 @@ if (logfile) } END_OFF: -if (dbm_file) EXIM_DBCLOSE(dbm_file); +if (dbm_file) exim_dbclose(dbm_file); if (cache_fd > 0) (void)close(cache_fd); DEBUG(D_transport) debug_printf("%s transport succeeded\n", tblock->name); -- 2.30.2