This reuses the existing proxy object created by the dict_debug() function. --- proto/DATABASE_README.html | 13 +++++++ src/postconf/postconf.c | 9 +++++ src/proxymap/Makefile.in | 1 + src/proxymap/proxymap.c | 2 ++ src/util/Makefile.in | 12 +++++-- src/util/dict_debug.c | 66 ++++++++++++++++++++++++++++++++---- src/util/dict_debug.h | 34 +++++++++++++++++++ src/util/dict_debug_test.ref | 51 ++++++++++++++++++++++++++++ src/util/dict_debug_test.sh | 25 ++++++++++++++ src/util/dict_open.c | 2 ++ 10 files changed, 207 insertions(+), 8 deletions(-) create mode 100644 src/util/dict_debug.h create mode 100644 src/util/dict_debug_test.ref create mode 100755 src/util/dict_debug_test.sh
diff --git a/proto/DATABASE_README.html b/proto/DATABASE_README.html index 42a54ac7c..4182bb8c8 100644 --- a/proto/DATABASE_README.html +++ b/proto/DATABASE_README.html @@ -293,6 +293,19 @@ databases are maintained by Postfix daemons. The lookup table name as used in "dbm:table" is the database file name without the ".dir" or ".pag" suffix. </dd> +<dt> <b>debug</b> </dt> + +<dd> +<p> An adapter for another table that causes all accesses to be +logged. Example usage: "debug:hash:/etc/postfix/example". The +formats of the log messages are unspecified and subject to change. +Warning: If a query or the underlying table contains sensitive +information (such as a password), that information might be +logged. </p> + +<p> This feature is available with Postfix 3.11 and later. </p> +</dd> + <dt> <b>environ</b> </dt> <dd> The UNIX process environment array. The lookup key is the diff --git a/src/postconf/postconf.c b/src/postconf/postconf.c index 74f13b2cd..a4d4c8490 100644 --- a/src/postconf/postconf.c +++ b/src/postconf/postconf.c @@ -252,6 +252,15 @@ /* .IP \fBdbm\fR /* An indexed file type based on hashing. Available on systems /* with support for DBM databases. +/* .IP \fBdebug\fR +/* An adapter for another table that causes all accesses to be +/* logged. Example usage: \fBdebug:hash:/etc/postfix/example\fR. +/* The formats of the log messages are unspecified and subject to +/* change. Warning: If a query or the underlying table contains +/* sensitive information (such as a password), that information +/* might be logged. +/* +/* This feature is available with Postfix 3.11 and later. /* .IP \fBenviron\fR /* The UNIX process environment array. The lookup key is the /* environment variable name; the table name is ignored. Originally diff --git a/src/proxymap/Makefile.in b/src/proxymap/Makefile.in index efe6e0c87..29dec6a74 100644 --- a/src/proxymap/Makefile.in +++ b/src/proxymap/Makefile.in @@ -52,6 +52,7 @@ proxymap.o: ../../include/argv.h proxymap.o: ../../include/attr.h proxymap.o: ../../include/check_arg.h proxymap.o: ../../include/dict.h +proxymap.o: ../../include/dict_debug.h proxymap.o: ../../include/dict_pipe.h proxymap.o: ../../include/dict_proxy.h proxymap.o: ../../include/dict_union.h diff --git a/src/proxymap/proxymap.c b/src/proxymap/proxymap.c index c0af411f6..9fc1159e8 100644 --- a/src/proxymap/proxymap.c +++ b/src/proxymap/proxymap.c @@ -232,6 +232,7 @@ #include <htable.h> #include <stringops.h> #include <dict.h> +#include <dict_debug.h> #include <dict_pipe.h> #include <dict_union.h> @@ -307,6 +308,7 @@ static char *get_nested_dict_name(char *type_name) } *prefix, prefixes[] = { DICT_TYPE_UNION ":", (sizeof(DICT_TYPE_UNION ":") - 1), DICT_TYPE_PIPE ":", (sizeof(DICT_TYPE_PIPE ":") - 1), + DICT_TYPE_DEBUG ":", (sizeof(DICT_TYPE_DEBUG ":") - 1), }; #define COUNT_OF(x) (sizeof(x)/sizeof((x)[0])) diff --git a/src/util/Makefile.in b/src/util/Makefile.in index 7c99ae23a..b9d70dbd2 100644 --- a/src/util/Makefile.in +++ b/src/util/Makefile.in @@ -104,7 +104,8 @@ MAP_OBJ = dict_pcre.o dict_cdb.o dict_lmdb.o dict_sdbm.o slmdb.o \ mkmap_cdb.o mkmap_lmdb.o mkmap_sdbm.o HDRS = argv.h attr.h attr_clnt.h auto_clnt.h base64_code.h binhash.h \ chroot_uid.h cidr_match.h clean_env.h connect.h ctable.h dict.h \ - dict_cdb.h dict_cidr.h dict_db.h dict_dbm.h dict_env.h dict_ht.h \ + dict_cdb.h dict_cidr.h dict_db.h dict_dbm.h dict_debug.h dict_env.h \ + dict_ht.h \ dict_lmdb.h dict_ni.h dict_nis.h dict_nisplus.h dict_pcre.h dict_regexp.h \ dict_sdbm.h dict_static.h dict_tcp.h dict_unix.h dir_forest.h \ events.h exec_command.h find_inet.h fsspace.h fullname.h \ @@ -658,7 +659,7 @@ dict_tests: all dict_test \ dict_pipe_test dict_regexp_file_test dict_cidr_file_test \ dict_static_file_test dict_random_test dict_random_file_test \ dict_inline_file_test dict_stream_test dict_inline_regexp_test \ - dict_inline_cidr_test + dict_inline_cidr_test dict_debug_test dict_pcre_tests: dict_pcre_test miss_endif_pcre_test dict_pcre_file_test \ dict_inline_pcre_test @@ -1113,6 +1114,11 @@ dict_inline_cidr_test: dict_open dict_inline_cidr.ref diff dict_inline_cidr.ref dict_inline_cidr.tmp rm -f dict_inline_cidr.tmp +dict_debug_test: dict_open dict_debug_test.sh dict_debug_test.ref + $(SHLIB_ENV) ./dict_debug_test.sh >dict_debug_test.tmp 2>&1 + diff dict_debug_test.ref dict_debug_test.tmp + rm -f dict_debug_test.tmp + find_inet_test: find_inet find_inet.ref $(SHLIB_ENV) ${VALGRIND} ./find_inet >find_inet.tmp 2>&1 diff find_inet.ref find_inet.tmp @@ -1545,6 +1551,7 @@ dict_debug.o: argv.h dict_debug.o: check_arg.h dict_debug.o: dict.h dict_debug.o: dict_debug.c +dict_debug.o: dict_debug.h dict_debug.o: msg.h dict_debug.o: myflock.h dict_debug.o: mymalloc.h @@ -1672,6 +1679,7 @@ dict_open.o: dict_cdb.h dict_open.o: dict_cidr.h dict_open.o: dict_db.h dict_open.o: dict_dbm.h +dict_open.o: dict_debug.h dict_open.o: dict_env.h dict_open.o: dict_fail.h dict_open.o: dict_ht.h diff --git a/src/util/dict_debug.c b/src/util/dict_debug.c index 46634d40c..1ea67240e 100644 --- a/src/util/dict_debug.c +++ b/src/util/dict_debug.c @@ -11,6 +11,13 @@ /* /* DICT *DICT_DEBUG(dict_handle) /* DICT *dict_handle; +/* +/* #include <dict_debug.h> +/* +/* DICT *dict_debug_open(name, open_flags, dict_flags); +/* const char *name; +/* int open_flags; +/* int dict_flags; /* DESCRIPTION /* dict_debug() encapsulates the given dictionary object and returns /* a proxy object that logs all access to the encapsulated object. @@ -21,6 +28,12 @@ /* the object's debugging flag is not set, and that otherwise encapsulates /* the object with dict_debug(). This macro simplifies usage by avoiding /* clumsy expressions. The macro evaluates its argument multiple times. +/* +/* dict_debug_open() is similar to dict_debug() except the underlying +/* table's handle is registered as \fIname\fR, which has the form +/* "\fItype\fB:\fIname\fR". If no such registration exists yet, the +/* underlying table is first opened as if by a call to +/* dict_open(\fIname\fR, \fIopen_flags\fR, \fIdict_flags\fR). /* DIAGNOSTICS /* Fatal errors: out of memory. /* LICENSE @@ -32,6 +45,8 @@ /* IBM T.J. Watson Research /* P.O. Box 704 /* Yorktown Heights, NY 10598, USA +/* +/* Richard Hansen <rhan...@rhansen.org> (dict_debug_open(), © 2025) /*--*/ /* System libraries. */ @@ -43,12 +58,14 @@ #include <msg.h> #include <mymalloc.h> #include <dict.h> +#include "dict_debug.h" /* Application-specific. */ typedef struct { DICT dict; /* the proxy service */ DICT *real_dict; /* encapsulated object */ + char *reg; /* real_dict registration, or null */ } DICT_DEBUG; /* dict_debug_lookup - log lookup operation */ @@ -62,7 +79,11 @@ static const char *dict_debug_lookup(DICT *dict, const char *key) real_dict->flags = dict->flags; result = dict_get(real_dict, key); dict->flags = real_dict->flags; - msg_info("%s:%s lookup: \"%s\" = \"%s\"", dict->type, dict->name, key, + msg_info("%s%s%s lookup: \"%s\" = \"%s\"", + dict_debug->reg ? dict_debug->reg : dict->type, + dict_debug->reg ? "" : ":", + dict_debug->reg ? "" : dict->name, + key, result ? result : real_dict->error ? "error" : "not_found"); DICT_ERR_VAL_RETURN(dict, real_dict->error, result); } @@ -78,7 +99,10 @@ static int dict_debug_update(DICT *dict, const char *key, const char *value) real_dict->flags = dict->flags; result = dict_put(real_dict, key, value); dict->flags = real_dict->flags; - msg_info("%s:%s update: \"%s\" = \"%s\": %s", dict->type, dict->name, + msg_info("%s%s%s update: \"%s\" = \"%s\": %s", + dict_debug->reg ? dict_debug->reg : dict->type, + dict_debug->reg ? "" : ":", + dict_debug->reg ? "" : dict->name, key, value, result == 0 ? "success" : real_dict->error ? "error" : "failed"); DICT_ERR_VAL_RETURN(dict, real_dict->error, result); @@ -95,7 +119,11 @@ static int dict_debug_delete(DICT *dict, const char *key) real_dict->flags = dict->flags; result = dict_del(real_dict, key); dict->flags = real_dict->flags; - msg_info("%s:%s delete: \"%s\": %s", dict->type, dict->name, key, + msg_info("%s%s%s delete: \"%s\": %s", + dict_debug->reg ? dict_debug->reg : dict->type, + dict_debug->reg ? "" : ":", + dict_debug->reg ? "" : dict->name, + key, result == 0 ? "success" : real_dict->error ? "error" : "failed"); DICT_ERR_VAL_RETURN(dict, real_dict->error, result); @@ -114,10 +142,16 @@ static int dict_debug_sequence(DICT *dict, int function, result = dict_seq(real_dict, function, key, value); dict->flags = real_dict->flags; if (result == 0) - msg_info("%s:%s sequence: \"%s\" = \"%s\"", dict->type, dict->name, + msg_info("%s%s%s sequence: \"%s\" = \"%s\"", + dict_debug->reg ? dict_debug->reg : dict->type, + dict_debug->reg ? "" : ":", + dict_debug->reg ? "" : dict->name, *key, *value); else - msg_info("%s:%s sequence: found EOF", dict->type, dict->name); + msg_info("%s%s%s sequence: found EOF", + dict_debug->reg ? dict_debug->reg : dict->type, + dict_debug->reg ? "" : ":", + dict_debug->reg ? "" : dict->name); DICT_ERR_VAL_RETURN(dict, real_dict->error, result); } @@ -127,7 +161,11 @@ static void dict_debug_close(DICT *dict) { DICT_DEBUG *dict_debug = (DICT_DEBUG *) dict; - dict_close(dict_debug->real_dict); + if (dict_debug->reg != 0) { + dict_unregister(dict_debug->reg); + myfree(dict_debug->reg); + } else + dict_close(dict_debug->real_dict); dict_free(dict); } @@ -146,5 +184,21 @@ DICT *dict_debug(DICT *real_dict) dict_debug->dict.sequence = dict_debug_sequence; dict_debug->dict.close = dict_debug_close; dict_debug->real_dict = real_dict; + dict_debug->reg = 0; return (&dict_debug->dict); } + +DICT *dict_debug_open(const char *name, int open_flags, int dict_flags) +{ + static const char myname[] = "dict_debug_open"; + if (msg_verbose) + msg_info("%s: %s", myname, name); + DICT *real_dict = dict_handle(name); + if (real_dict == 0) + real_dict = dict_open(name, open_flags, dict_flags); + dict_register(name, real_dict); + DICT_DEBUG *dd = (DICT_DEBUG *) dict_debug(real_dict); + dd->reg = mystrdup(name); + dd->dict.owner = real_dict->owner; + return &dd->dict; +} diff --git a/src/util/dict_debug.h b/src/util/dict_debug.h new file mode 100644 index 000000000..bbf56c6c6 --- /dev/null +++ b/src/util/dict_debug.h @@ -0,0 +1,34 @@ +#ifndef _DICT_DEBUG_H_INCLUDED_ +#define _DICT_DEBUG_H_INCLUDED_ + +/*++ +/* NAME +/* dict_debug 3h +/* SUMMARY +/* dictionary manager interface for "debug" tables +/* SYNOPSIS +/* #include <dict_debug.h> +/* DESCRIPTION +/* .nf + + /* + * Utility library. + */ +#include <dict.h> + + /* + * External interface. + */ +#define DICT_TYPE_DEBUG "debug" + +extern DICT *dict_debug_open(const char *, int, int); + +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Richard Hansen <rhan...@rhansen.org> (© 2025) +/*--*/ + +#endif diff --git a/src/util/dict_debug_test.ref b/src/util/dict_debug_test.ref new file mode 100644 index 000000000..0c31d1ab1 --- /dev/null +++ b/src/util/dict_debug_test.ref @@ -0,0 +1,51 @@ ++ ./dict_open debug: read +./dict_open: fatal: open dictionary: expecting "type:name" form instead of "debug:" ++ ./dict_open debug:missing_colon_and_name read +./dict_open: fatal: open dictionary: expecting "type:name" form instead of "missing_colon_and_name" ++ ./dict_open debug:static:{space in name} read +owner=trusted (uid=2147483647) +> get k +./dict_open: static:{space in name} lookup: "k" = "space in name" +k=space in name ++ ./dict_open debug:debug:static:value read +owner=trusted (uid=2147483647) +> get k +./dict_open: static:value lookup: "k" = "value" +./dict_open: debug:static:value lookup: "k" = "value" +k=value ++ ./dict_open debug:internal:name write +owner=trusted (uid=2147483647) +> get k +./dict_open: internal:name lookup: "k" = "not_found" +k: not found +> put k=v +./dict_open: internal:name update: "k" = "v": success +> get k +./dict_open: internal:name lookup: "k" = "v" +k=v +> first +./dict_open: internal:name sequence: "k" = "v" +k=v +> next +./dict_open: internal:name sequence: found EOF +not found +> del k +./dict_open: internal:name delete: "k": success +k: deleted +> get k +./dict_open: internal:name lookup: "k" = "not_found" +k: not found +> del k +./dict_open: internal:name delete: "k": failed +k: not found +> first +./dict_open: internal:name sequence: found EOF +not found ++ ./dict_open debug:fail:{oh no} read +owner=trusted (uid=2147483647) +> get k +./dict_open: fail:{oh no} lookup: "k" = "error" +k: error +> first +./dict_open: fail:{oh no} sequence: found EOF +error diff --git a/src/util/dict_debug_test.sh b/src/util/dict_debug_test.sh new file mode 100755 index 000000000..a8c051d80 --- /dev/null +++ b/src/util/dict_debug_test.sh @@ -0,0 +1,25 @@ +#!/bin/sh +set -ex +! ${VALGRIND} ./dict_open 'debug:' read </dev/null || exit 1 +! ${VALGRIND} ./dict_open 'debug:missing_colon_and_name' read </dev/null || exit 1 +${VALGRIND} ./dict_open 'debug:static:{space in name}' read <<EOF +get k +EOF +${VALGRIND} ./dict_open 'debug:debug:static:value' read <<EOF +get k +EOF +${VALGRIND} ./dict_open 'debug:internal:name' write <<EOF +get k +put k=v +get k +first +next +del k +get k +del k +first +EOF +${VALGRIND} ./dict_open 'debug:fail:{oh no}' read <<EOF +get k +first +EOF diff --git a/src/util/dict_open.c b/src/util/dict_open.c index c3b90d497..bc42c06fc 100644 --- a/src/util/dict_open.c +++ b/src/util/dict_open.c @@ -337,6 +337,7 @@ #include <msg.h> #include <dict.h> #include <dict_cdb.h> +#include <dict_debug.h> #include <dict_env.h> #include <dict_unix.h> #include <dict_tcp.h> @@ -415,6 +416,7 @@ static const DICT_OPEN_INFO dict_open_info[] = { DICT_TYPE_LMDB, dict_lmdb_open, mkmap_lmdb_open, #endif #endif /* !USE_DYNAMIC_MAPS */ + DICT_TYPE_DEBUG, dict_debug_open, 0, 0, }; -- 2.49.0 _______________________________________________ Postfix-devel mailing list -- postfix-devel@postfix.org To unsubscribe send an email to postfix-devel-le...@postfix.org