From 4fe7066dddda5e2db9b839fbfea6e97b02bc61bd Mon Sep 17 00:00:00 2001 From: Wietse Venema Date: Sun, 29 Jul 2001 00:00:00 -0500 Subject: [PATCH] snapshot-20010729 --- postfix/.indent.pro | 2 + postfix/HISTORY | 34 +++- postfix/MYSQL_README | 5 +- postfix/conf/sample-ldap.cf | 14 ++ postfix/html/faq.html | 41 ++++ postfix/src/cleanup/cleanup.c | 2 +- postfix/src/global/mail_params.c | 6 +- postfix/src/global/mail_params.h | 18 +- postfix/src/global/mail_version.h | 2 +- postfix/src/master/Makefile.in | 3 + postfix/src/master/mail_flow.c | 16 ++ postfix/src/master/mail_flow.h | 1 + postfix/src/master/mail_server.h | 2 +- postfix/src/master/master_flow.c | 3 + postfix/src/master/multi_server.c | 23 ++- postfix/src/master/single_server.c | 14 +- postfix/src/master/trigger_server.c | 10 + postfix/src/nqmgr/qmgr.c | 18 +- postfix/src/qmgr/qmgr.c | 4 +- postfix/src/smtpd/smtpd.h | 1 + postfix/src/smtpd/smtpd_check.c | 289 +++++++++++++++++----------- postfix/src/smtpd/smtpd_state.c | 2 + postfix/src/smtpd/smtpd_token.c | 2 +- postfix/src/util/Makefile.in | 27 ++- postfix/src/util/cache | Bin 0 -> 82681 bytes postfix/src/util/cache.in | 26 +++ postfix/src/util/ctable.c | 273 ++++++++++++++++++++++++++ postfix/src/util/ctable.h | 39 ++++ postfix/src/util/ctable.in | 39 ++++ postfix/src/util/ctable.ref | 99 ++++++++++ postfix/src/util/dict_ldap.c | 77 ++++++-- postfix/src/util/ring.h | 11 ++ 32 files changed, 929 insertions(+), 174 deletions(-) create mode 100755 postfix/src/util/cache create mode 100644 postfix/src/util/cache.in create mode 100644 postfix/src/util/ctable.c create mode 100644 postfix/src/util/ctable.h create mode 100644 postfix/src/util/ctable.in create mode 100644 postfix/src/util/ctable.ref diff --git a/postfix/.indent.pro b/postfix/.indent.pro index 23f1f46ce..a0d1c27f7 100644 --- a/postfix/.indent.pro +++ b/postfix/.indent.pro @@ -20,6 +20,8 @@ -TCONFIG_STR_TABLE -TCONFIG_TIME_FN_TABLE -TCONFIG_TIME_TABLE +-TCTABLE +-TCTABLE_ENTRY -TDELIVER_ATTR -TDELIVER_REQUEST -TDICT diff --git a/postfix/HISTORY b/postfix/HISTORY index a000faf3e..3d7064771 100644 --- a/postfix/HISTORY +++ b/postfix/HISTORY @@ -5350,13 +5350,27 @@ Apologies for any names omitted. 20010719 - Feature: stiffer coupling between mail arrival rates and - mail delivery rates, using a trivial token-based scheme - where qmgr provides a token when it retrieves mail from - the incoming queue, and where the cleanup daemon wants a - token after enqueueing mail. If no token is available the - cleanup server pauses briefly and proceeds anyway. This - allows input rates to gradually exceed output rates. After - sustained exposure to high input loads Postfix reverts to - the old situation where it sacrifices output rates in favor - of receiving mail. + Feature: stiffer coupling between mail receiving rates and + mail delivery rates, using a trivial token-based scheme, + implemented by reading and writing an in-memory pipe. The + queue manager produces one token when it retrieves mail + from the incoming queue. The cleanup daemon consumes one + token when it adds mail to the incoming queue. If no token + is available the cleanup server pauses for $in_flow_delay + seconds and proceeds anyway. The delay allows mail sending + process to catch up and access the disk. Valid delays are + 0..10 seconds. + +20010727 + + Bugfix: updated LDAP client module from LaMont Jones, HP. + +20010729 + + Bugfix: recursive restrictions could clobber non-reentrant + address resolving results. Problem found by Victor Duchovni, + morganstanley.com. + + Bugfix: the not yet published DUNNO table lookup result + did not prevent further partial key lookups in the same + table. Problem found by Victor Duchovni, morganstanley.com. diff --git a/postfix/MYSQL_README b/postfix/MYSQL_README index faecf80a2..2f11bd956 100644 --- a/postfix/MYSQL_README +++ b/postfix/MYSQL_README @@ -12,9 +12,10 @@ the mysqlclient library (and libm) to AUXLIBS, for example: make -f Makefile.init makefiles \ 'CCARGS=-DHAS_MYSQL -I/usr/local/mysql/include' \ - 'AUXLIBS=-L/usr/local/mysql/lib -lmysqlclient -lm' + 'AUXLIBS=-L/usr/local/mysql/lib -lmysqlclient -lz -lm' -then, just run 'make'. +then, just run 'make'. This requires libz, the compression library. +Older mysql implementations build without libz. Postfix installations which may benefit from using mysql map types include sites that have a need for instantaneous updates of diff --git a/postfix/conf/sample-ldap.cf b/postfix/conf/sample-ldap.cf index c503df0bc..8badb4026 100644 --- a/postfix/conf/sample-ldap.cf +++ b/postfix/conf/sample-ldap.cf @@ -23,6 +23,13 @@ #ldap_server_port = 389 # The ldap_query_filter parameter specifies the filter used for queries. +# The replacement for "%s" is the address input into the map; e.g. +# for alias maps, the "user" part (the RFC 2822 local-part) of +# "user@domain.com" for To: addresses destined for local delivery +# (those matching $mydestination or a virtual domain), and all of +# "user@domain.com" (the RFC 2822 addr-spec) for other addresses. +# "%u" provides just the user portion of the input, and "%d" provides +# just the hostname. # #ldap_query_filter = (mailacceptinggeneralid=%s) @@ -31,6 +38,13 @@ # #ldap_result_attribute = maildrop +# The ldap_special_result_attribute lists the attribute(s) of an +# entry which contain links, either ldap url's or distinguished names. +# The entries referenced by these links are (recursively) treated as if +# they were contained in the referencing entity. +# +# ldap_special_result_attribute = + # The ldap_scope parameter specifies the LDAP search scope: sub, base, or one. # #ldap_scope = sub diff --git a/postfix/html/faq.html b/postfix/html/faq.html index fc787ee47..64ea296ba 100644 --- a/postfix/html/faq.html +++ b/postfix/html/faq.html @@ -296,6 +296,9 @@ domains with "relay access denied"
  • Receiving a virtual domain in a mailbox +
  • Postfix logs delivery to virtual +address with the wrong name +

    Address rewriting

    @@ -2716,6 +2719,44 @@ your system supports, use the command postconf -m.
    +

    Postfix logs delivery to virtual +address with the wrong name

    + +When Postfix delivers mail for a virtual address vuser@vdomain.name +that is aliased to a local user, then Postfix logs the local username +instead of the virtual one. + +

    + +Changing this requires non-trivial changes, because Postfix only +remembers the address that it delivers to, not the address that +was original specified in, for example, the SMTP MAIL FROM command. + +

    + +A workaround exists. It uses regular expressions. This +can be expensive if you have many virtual domains. + +

    +

    +
    +/etc/postfix/main.cf:
    +    virtual_maps = regexp:/etc/postfix/virtual_regexp
    +    recipient_delimiter = +
    +
    +/etc/postfix/virtual_regexp:
    +    /^vdomain\.name$/            whatever
    +    /(.*)@vdomain\.name$/        localuser+$1=vdomain.name
    +
    +
    + +

    + +This delivers the mail as +localuser+vuser=vdomain.name@your.domain. + +


    +

    Address masquerading with exceptions

    For people outside your organization it can be desirable to only diff --git a/postfix/src/cleanup/cleanup.c b/postfix/src/cleanup/cleanup.c index 9891a03cc..1d457753d 100644 --- a/postfix/src/cleanup/cleanup.c +++ b/postfix/src/cleanup/cleanup.c @@ -271,6 +271,6 @@ int main(int argc, char **argv) MAIL_SERVER_PRE_INIT, cleanup_pre_jail, MAIL_SERVER_POST_INIT, cleanup_post_jail, MAIL_SERVER_PRE_ACCEPT, pre_accept, - MAIL_SERVER_FLOW_CTL, + MAIL_SERVER_IN_FLOW_DELAY, var_in_flow_delay, 0); } diff --git a/postfix/src/global/mail_params.c b/postfix/src/global/mail_params.c index ec3d78064..98da1b2d7 100644 --- a/postfix/src/global/mail_params.c +++ b/postfix/src/global/mail_params.c @@ -69,7 +69,7 @@ /* char *var_export_environ; /* char *var_debug_peer_list; /* int var_debug_peer_level; -/* int var_glob_flow_ctl; +/* int var_in_flow_delay; /* /* void mail_params_init() /* DESCRIPTION @@ -186,7 +186,7 @@ char *var_def_transport; char *var_mynetworks_style; char *var_verp_delims; char *var_verp_filter; -int var_glob_flow_ctl; +int var_in_flow_delay; char *var_import_environ; char *var_export_environ; @@ -327,7 +327,6 @@ void mail_params_init() VAR_FORK_TRIES, DEF_FORK_TRIES, &var_fork_tries, 1, 0, VAR_FLOCK_TRIES, DEF_FLOCK_TRIES, &var_flock_tries, 1, 0, VAR_DEBUG_PEER_LEVEL, DEF_DEBUG_PEER_LEVEL, &var_debug_peer_level, 1, 0, - VAR_GLOB_FLOW_CTL, DEF_GLOB_FLOW_CTL, &var_glob_flow_ctl, 0, 0, 0, }; static CONFIG_TIME_TABLE time_defaults[] = { @@ -339,6 +338,7 @@ void mail_params_init() VAR_FLOCK_DELAY, DEF_FLOCK_DELAY, &var_flock_delay, 1, 0, VAR_FLOCK_STALE, DEF_FLOCK_STALE, &var_flock_stale, 1, 0, VAR_DAEMON_TIMEOUT, DEF_DAEMON_TIMEOUT, &var_daemon_timeout, 1, 0, + VAR_IN_FLOW_DELAY, DEF_IN_FLOW_DELAY, &var_in_flow_delay, 0, 10, 0, }; static CONFIG_BOOL_TABLE bool_defaults[] = { diff --git a/postfix/src/global/mail_params.h b/postfix/src/global/mail_params.h index 7c6744d2d..45eaecaa4 100644 --- a/postfix/src/global/mail_params.h +++ b/postfix/src/global/mail_params.h @@ -1284,15 +1284,17 @@ extern char *var_verp_delims; extern char *var_verp_filter; /* - * Global flow control. This allows for a stiffer coupling between receiving - * programs and the queue manager, so that receiving processes cannot easily - * overwhelm the file system. The coupling is not so tight that Postfix - * stops receiving mail althogether. It just slows down a bit so that - * sending processes get a chance to read from the disk. + * Inbound mail flow control. This allows for a stiffer coupling between + * receiving mail and sending mail. A sending process produces one token for + * each message that it takes from the incoming queue; a receiving process + * consumes one token for each message that it adds to the incoming queue. + * When no token is available (Postfix receives more mail than it is able to + * deliver) a receiving process pauses for $in_flow_delay seconds so that + * the sending processes get a chance to access the disk. */ -#define VAR_GLOB_FLOW_CTL "global_mail_flow_control" -#define DEF_GLOB_FLOW_CTL 0 -extern int var_glob_flow_ctl; +#define VAR_IN_FLOW_DELAY "in_flow_delay" +#define DEF_IN_FLOW_DELAY "1s" +extern int var_in_flow_delay; /* LICENSE /* .ad diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h index 527fb8295..5b94e2288 100644 --- a/postfix/src/global/mail_version.h +++ b/postfix/src/global/mail_version.h @@ -15,7 +15,7 @@ * Version of this program. */ #define VAR_MAIL_VERSION "mail_version" -#define DEF_MAIL_VERSION "Snapshot-20010722" +#define DEF_MAIL_VERSION "Snapshot-20010729" extern char *var_mail_version; /* LICENSE diff --git a/postfix/src/master/Makefile.in b/postfix/src/master/Makefile.in index 59f1e3cc8..db445d72a 100644 --- a/postfix/src/master/Makefile.in +++ b/postfix/src/master/Makefile.in @@ -232,6 +232,7 @@ multi_server.o: ../../include/mail_params.h multi_server.o: ../../include/mail_conf.h multi_server.o: ../../include/timed_ipc.h multi_server.o: ../../include/resolve_local.h +multi_server.o: mail_flow.h multi_server.o: master_proto.h multi_server.o: mail_server.h single_server.o: single_server.c @@ -259,6 +260,7 @@ single_server.o: ../../include/debug_process.h single_server.o: ../../include/mail_conf.h single_server.o: ../../include/timed_ipc.h single_server.o: ../../include/resolve_local.h +single_server.o: mail_flow.h single_server.o: master_proto.h single_server.o: mail_server.h trigger_server.o: trigger_server.c @@ -285,5 +287,6 @@ trigger_server.o: ../../include/mail_task.h trigger_server.o: ../../include/debug_process.h trigger_server.o: ../../include/mail_conf.h trigger_server.o: ../../include/resolve_local.h +trigger_server.o: mail_flow.h trigger_server.o: master_proto.h trigger_server.o: mail_server.h diff --git a/postfix/src/master/mail_flow.c b/postfix/src/master/mail_flow.c index 603a1f373..3a42df080 100644 --- a/postfix/src/master/mail_flow.c +++ b/postfix/src/master/mail_flow.c @@ -11,6 +11,8 @@ /* /* void mail_flow_put(count) /* int count; +/* +/* int mail_flow_count() /* DESCRIPTION /* This module implements a simple flow control mechanism that /* is based on tokens that are consumed by mail receiving processes @@ -23,6 +25,8 @@ /* mail_flow_put() produces the specified number of tokens. The /* token producing process is expected to produce new tokens /* whenever it falls idle and no more tokens are available. +/* +/* mail_flow_count() returns the number of available tokens. /* BUGS /* The producer needs to wake up periodically to ensure that /* tokens are not lost due to leakage. @@ -110,3 +114,15 @@ int mail_flow_put(int len) msg_info("%s: %d %d", myname, len, len - count); return (len - count); } + +/* mail_flow_count - return number of available tokens */ + +int mail_flow_count(void) +{ + char *myname = "mail_flow_count"; + int count; + + if ((count = peekfd(MASTER_FLOW_READ)) < 0) + msg_warn("%s: %m", myname); + return (count); +} diff --git a/postfix/src/master/mail_flow.h b/postfix/src/master/mail_flow.h index c146d83be..9e058598f 100644 --- a/postfix/src/master/mail_flow.h +++ b/postfix/src/master/mail_flow.h @@ -16,6 +16,7 @@ */ extern int mail_flow_get(int); extern int mail_flow_put(int); +extern int mail_flow_count(void); /* LICENSE /* .ad diff --git a/postfix/src/master/mail_server.h b/postfix/src/master/mail_server.h index 6c55603c1..47b8cfeee 100644 --- a/postfix/src/master/mail_server.h +++ b/postfix/src/master/mail_server.h @@ -28,7 +28,7 @@ #define MAIL_SERVER_EXIT 13 #define MAIL_SERVER_PRE_ACCEPT 14 -#define MAIL_SERVER_FLOW_CTL 20 +#define MAIL_SERVER_IN_FLOW_DELAY 20 typedef void (*MAIL_SERVER_INIT_FN) (char *, char **); typedef int (*MAIL_SERVER_LOOP_FN) (char *, char **); diff --git a/postfix/src/master/master_flow.c b/postfix/src/master/master_flow.c index cc13f8cc8..ed9b492d2 100644 --- a/postfix/src/master/master_flow.c +++ b/postfix/src/master/master_flow.c @@ -27,4 +27,7 @@ void master_flow_init(void) non_blocking(master_flow_pipe[0], NON_BLOCKING); non_blocking(master_flow_pipe[1], NON_BLOCKING); + + close_on_exec(master_flow_pipe[0], CLOSE_ON_EXEC); + close_on_exec(master_flow_pipe[1], CLOSE_ON_EXEC); } diff --git a/postfix/src/master/multi_server.c b/postfix/src/master/multi_server.c index e845ca5ff..746e7c033 100644 --- a/postfix/src/master/multi_server.c +++ b/postfix/src/master/multi_server.c @@ -95,6 +95,9 @@ /* Function to be executed prior to accepting a new connection. /* .sp /* Only the last instance of this parameter type is remembered. +/* .IP "MAIL_SERVER_IN_FLOW_DELAY (int)" +/* The amount of seconds to pause when no "mail flow control token" +/* is available. A token is consumed for each connection request. /* .PP /* multi_server_disconnect() should be called by the application /* when a client disconnects. @@ -168,6 +171,7 @@ #include #include #include +#include /* Process manager. */ @@ -190,6 +194,7 @@ static void (*multi_server_accept) (int, char *); static void (*multi_server_onexit) (char *, char **); static void (*multi_server_pre_accept) (char *, char **); static VSTREAM *multi_server_lock; +static int multi_server_in_flow_delay; /* multi_server_exit - normal termination */ @@ -253,6 +258,15 @@ static void multi_server_execute(int unused_event, char *context) event_request_timer(multi_server_timeout, (char *) 0, var_idle_limit); } +/* multi_server_enable_read - enable read events */ + +static void multi_server_enable_read(int unused_event, char *context) +{ + VSTREAM *stream = (VSTREAM *) context; + + event_enable_read(vstream_fileno(stream), multi_server_execute, (char *) stream); +} + /* multi_server_wakeup - wake up application */ static void multi_server_wakeup(int fd) @@ -266,7 +280,11 @@ static void multi_server_wakeup(int fd) client_count++; stream = vstream_fdopen(fd, O_RDWR); timed_ipc_setup(stream); - event_enable_read(fd, multi_server_execute, (char *) stream); + if (multi_server_in_flow_delay > 0 && mail_flow_get(1) < 0) + event_request_timer(multi_server_enable_read, (char *) stream, + multi_server_in_flow_delay); + else + multi_server_enable_read(0, (char *) stream); } /* multi_server_accept_local - accept client connection request */ @@ -498,6 +516,9 @@ NORETURN multi_server_main(int argc, char **argv, MULTI_SERVER_FN service,...) case MAIL_SERVER_PRE_ACCEPT: multi_server_pre_accept = va_arg(ap, MAIL_SERVER_ACCEPT_FN); break; + case MAIL_SERVER_IN_FLOW_DELAY: + multi_server_in_flow_delay = va_arg(ap, int); + break; default: msg_panic("%s: unknown argument type: %d", myname, key); } diff --git a/postfix/src/master/single_server.c b/postfix/src/master/single_server.c index 58cc6f7ce..5860fc798 100644 --- a/postfix/src/master/single_server.c +++ b/postfix/src/master/single_server.c @@ -92,6 +92,9 @@ /* Function to be executed prior to accepting a new connection. /* .sp /* Only the last instance of this parameter type is remembered. +/* .IP "MAIL_SERVER_IN_FLOW_DELAY (int)" +/* The amount of seconds to pause when no "mail flow control token" +/* is available. A token is consumed for each connection request. /* .PP /* The var_use_limit variable limits the number of clients that /* a server can service before it commits suicide. @@ -184,7 +187,7 @@ static void (*single_server_accept) (int, char *); static void (*single_server_onexit) (char *, char **); static void (*single_server_pre_accept) (char *, char **); static VSTREAM *single_server_lock; -static int single_server_flow_ctl; +static int single_server_in_flow_delay; /* single_server_exit - normal termination */ @@ -233,6 +236,8 @@ static void single_server_wakeup(int fd) timed_ipc_setup(stream); if (master_notify(var_pid, MASTER_STAT_TAKEN) < 0) single_server_abort(EVENT_NULL_TYPE, EVENT_NULL_CONTEXT); + if (single_server_in_flow_delay > 0 && mail_flow_get(1) < 0) + doze(single_server_in_flow_delay * 1000000); single_server_service(stream, single_server_name, single_server_argv); (void) vstream_fclose(stream); if (master_notify(var_pid, MASTER_STAT_AVAIL) < 0) @@ -240,9 +245,6 @@ static void single_server_wakeup(int fd) if (msg_verbose) msg_info("connection closed"); use_count++; - if (single_server_flow_ctl) - if (mail_flow_get(1) < 0) - rand_sleep(single_server_flow_ctl, 0); if (var_idle_limit > 0) event_request_timer(single_server_timeout, (char *) 0, var_idle_limit); } @@ -474,8 +476,8 @@ NORETURN single_server_main(int argc, char **argv, SINGLE_SERVER_FN service,...) case MAIL_SERVER_PRE_ACCEPT: single_server_pre_accept = va_arg(ap, MAIL_SERVER_ACCEPT_FN); break; - case MAIL_SERVER_FLOW_CTL: - single_server_flow_ctl = var_glob_flow_ctl; + case MAIL_SERVER_IN_FLOW_DELAY: + single_server_in_flow_delay = va_arg(ap, int); break; default: msg_panic("%s: unknown argument type: %d", myname, key); diff --git a/postfix/src/master/trigger_server.c b/postfix/src/master/trigger_server.c index 61307e2a4..62ce674aa 100644 --- a/postfix/src/master/trigger_server.c +++ b/postfix/src/master/trigger_server.c @@ -99,6 +99,9 @@ /* Function to be executed prior to accepting a new request. /* .sp /* Only the last instance of this parameter type is remembered. +/* .IP "MAIL_SERVER_IN_FLOW_DELAY (int)" +/* The amount of seconds to pause when no "mail flow control token" +/* is available. A token is consumed for each connection request. /* .PP /* The var_use_limit variable limits the number of clients that /* a server can service before it commits suicide. @@ -169,6 +172,7 @@ #include #include #include +#include /* Process manager. */ @@ -190,6 +194,7 @@ static void (*trigger_server_accept) (int, char *); static void (*trigger_server_onexit) (char *, char **); static void (*trigger_server_pre_accept) (char *, char **); static VSTREAM *trigger_server_lock; +static int trigger_server_in_flow_delay; /* trigger_server_exit - normal termination */ @@ -230,6 +235,8 @@ static void trigger_server_wakeup(int fd) */ if (master_notify(var_pid, MASTER_STAT_TAKEN) < 0) trigger_server_abort(EVENT_NULL_TYPE, EVENT_NULL_CONTEXT); + if (trigger_server_in_flow_delay > 0 && mail_flow_get(1) < 0) + doze(trigger_server_in_flow_delay * 1000000); if ((len = read(fd, buf, sizeof(buf))) >= 0) trigger_server_service(buf, len, trigger_server_name, trigger_server_argv); @@ -469,6 +476,9 @@ NORETURN trigger_server_main(int argc, char **argv, TRIGGER_SERVER_FN service,.. case MAIL_SERVER_PRE_ACCEPT: trigger_server_pre_accept = va_arg(ap, MAIL_SERVER_ACCEPT_FN); break; + case MAIL_SERVER_IN_FLOW_DELAY: + trigger_server_in_flow_delay = va_arg(ap, int); + break; default: msg_panic("%s: unknown argument type: %d", myname, key); } diff --git a/postfix/src/nqmgr/qmgr.c b/postfix/src/nqmgr/qmgr.c index c9f461877..6185a0be9 100644 --- a/postfix/src/nqmgr/qmgr.c +++ b/postfix/src/nqmgr/qmgr.c @@ -421,7 +421,7 @@ static int qmgr_loop(char *unused_name, char **unused_argv) { char *in_path = 0; char *df_path = 0; -int token_count; + int token_count; /* * This routine runs as part of the event handling loop, after the event @@ -456,13 +456,13 @@ int token_count; * Global flow control. If enabled, slow down receiving processes that * get ahead of the queue manager, but don't block them completely. */ - if (var_glob_flow_ctl) { - if (in_path != 0) - mail_flow_put(1); - else if ((token_count = peekfd(MASTER_FLOW_READ)) < var_proc_limit) - mail_flow_put(var_proc_limit - token_count); - else if (token_count > var_proc_limit) - mail_flow_get(token_count - var_proc_limit); + if (var_in_flow_delay > 0) { + if (in_path != 0) + mail_flow_put(1); + else if ((token_count = mail_flow_count()) < var_proc_limit) + mail_flow_put(var_proc_limit - token_count); + else if (token_count > var_proc_limit) + mail_flow_get(token_count - var_proc_limit); } if (in_path || df_path) return (DONT_WAIT); @@ -514,7 +514,7 @@ static void qmgr_post_init(char *unused_name, char **unused_argv) qmgr_incoming = qmgr_scan_create(MAIL_QUEUE_INCOMING); qmgr_deferred = qmgr_scan_create(MAIL_QUEUE_DEFERRED); qmgr_scan_request(qmgr_incoming, QMGR_SCAN_START); - qmgr_deferred_run_event(0, (char *) 0); + qmgr_deferred_run_event(0, (char *)0); } /* main - the main program */ diff --git a/postfix/src/qmgr/qmgr.c b/postfix/src/qmgr/qmgr.c index 1c48b118b..a705aadc4 100644 --- a/postfix/src/qmgr/qmgr.c +++ b/postfix/src/qmgr/qmgr.c @@ -418,10 +418,10 @@ static int qmgr_loop(char *unused_name, char **unused_argv) * Global flow control. If enabled, slow down receiving processes that * get ahead of the queue manager, but don't block them completely. */ - if (var_glob_flow_ctl) { + if (var_in_flow_delay > 0) { if (in_path != 0) mail_flow_put(1); - else if ((token_count = peekfd(MASTER_FLOW_READ)) < var_proc_limit) + else if ((token_count = mail_flow_count()) < var_proc_limit) mail_flow_put(var_proc_limit - token_count); else if (token_count > var_proc_limit) mail_flow_get(token_count - var_proc_limit); diff --git a/postfix/src/smtpd/smtpd.h b/postfix/src/smtpd/smtpd.h index 29fb3d2d7..feedc8020 100644 --- a/postfix/src/smtpd/smtpd.h +++ b/postfix/src/smtpd/smtpd.h @@ -67,6 +67,7 @@ typedef struct SMTPD_STATE { int recursion; off_t msg_size; int junk_cmds; + VSTRING *error_text; #ifdef USE_SASL_AUTH char *sasl_mechanism_list; char *sasl_method; diff --git a/postfix/src/smtpd/smtpd_check.c b/postfix/src/smtpd/smtpd_check.c index 75e7694f4..2fc9a6ba9 100644 --- a/postfix/src/smtpd/smtpd_check.c +++ b/postfix/src/smtpd/smtpd_check.c @@ -269,6 +269,7 @@ #include #include #include +#include /* DNS library. */ @@ -310,9 +311,8 @@ static jmp_buf smtpd_check_buf; * Intermediate results. These are static to avoid unnecessary stress on the * memory manager routines. */ -static RESOLVE_REPLY reply; -static VSTRING *query; static VSTRING *error_text; +static CTABLE *smtpd_resolve_cache; /* * Pre-opened SMTP recipient maps so we can reject mail for unknown users. @@ -346,7 +346,7 @@ static HTABLE *smtpd_rest_classes; /* * The routine that recursively applies restrictions. */ -static int generic_checks(SMTPD_STATE *, ARGV *, char *, char *, char *); +static int generic_checks(SMTPD_STATE *, ARGV *, const char *, const char *, const char *); /* * Reject context. @@ -361,6 +361,49 @@ static int generic_checks(SMTPD_STATE *, ARGV *, char *, char *, char *); * YASLM. */ #define STR vstring_str +#define CONST_STR(x) ((const char *) vstring_str(x)) + +/* resolve_pagein - page in an address resolver result */ + +static void *resolve_pagein(const char *addr, void *unused_context) +{ + static VSTRING *query; + RESOLVE_REPLY *reply; + + /* + * Initialize on the fly. + */ + if (query == 0) + query = vstring_alloc(10); + + /* + * Initialize. + */ + reply = (RESOLVE_REPLY *) mymalloc(sizeof(*reply)); + resolve_clnt_init(reply); + + /* + * Resolve the address. + */ + canon_addr_internal(query, addr); + resolve_clnt_query(STR(query), reply); + lowercase(STR(reply->recipient)); + + /* + * Save the result. + */ + return ((void *) reply); +} + +/* resolve_pageout - page out an address resolver result */ + +static void resolve_pageout(void *data, void *unused_context) +{ + RESOLVE_REPLY *reply = (RESOLVE_REPLY *) data; + + resolve_clnt_free(reply); + myfree((void *) reply); +} /* smtpd_check_parse - pre-parse restrictions */ @@ -474,13 +517,16 @@ void smtpd_check_init(void) DICT_FLAG_LOCK); /* - * Reply is used as a cache for resolved addresses, and error_text is - * used for returning error responses. + * error_text is used for returning error responses. */ - resolve_clnt_init(&reply); - query = vstring_alloc(10); error_text = vstring_alloc(10); + /* + * Initialize the resolved address cache. + */ + smtpd_resolve_cache = ctable_create(100, resolve_pagein, + resolve_pageout, (void *) 0); + /* * Pre-parse the restriction lists. At the same time, pre-open tables * before going to jail. @@ -623,8 +669,10 @@ static const char *check_maps_find(SMTPD_STATE *state, const char *reply_name, /* check_mail_addr_find - reject with temporary failure if dict lookup fails */ -static const char *check_mail_addr_find(SMTPD_STATE *state, const char *reply_name, - MAPS *maps, const char *key, char **ext) +static const char *check_mail_addr_find(SMTPD_STATE *state, + const char *reply_name, + MAPS *maps, const char *key, + char **ext) { const char *result; @@ -819,8 +867,8 @@ static int reject_unknown_hostname(SMTPD_STATE *state, char *name, /* reject_unknown_mailhost - fail if name has no A or MX record */ -static int reject_unknown_mailhost(SMTPD_STATE *state, char *name, - char *reply_name, char *reply_class) +static int reject_unknown_mailhost(SMTPD_STATE *state, const char *name, + const char *reply_name, const char *reply_class) { char *myname = "reject_unknown_mailhost"; int dns_status; @@ -876,7 +924,8 @@ static int check_relay_domains(SMTPD_STATE *state, char *recipient, static int permit_auth_destination(SMTPD_STATE *state, char *recipient) { char *myname = "permit_auth_destination"; - char *domain; + const RESOLVE_REPLY *reply; + const char *domain; if (msg_verbose) msg_info("%s: %s", myname, recipient); @@ -884,14 +933,13 @@ static int permit_auth_destination(SMTPD_STATE *state, char *recipient) /* * Resolve the address. */ - canon_addr_internal(query, recipient); - resolve_clnt_query(STR(query), &reply); - lowercase(STR(reply.recipient)); + reply = (const RESOLVE_REPLY *) + ctable_locate(smtpd_resolve_cache, recipient); /* * Handle special case that is not supposed to happen. */ - if ((domain = strrchr(STR(reply.recipient), '@')) == 0) + if ((domain = strrchr(CONST_STR(reply->recipient), '@')) == 0) return (SMTPD_CHECK_OK); domain += 1; @@ -909,7 +957,7 @@ static int permit_auth_destination(SMTPD_STATE *state, char *recipient) /* * Skip source-routed mail (uncertain destination). */ - if (var_allow_untrust_route == 0 && (reply.flags & RESOLVE_FLAG_ROUTED)) + if (var_allow_untrust_route == 0 && (reply->flags & RESOLVE_FLAG_ROUTED)) return (SMTPD_CHECK_DUNNO); /* @@ -968,7 +1016,7 @@ static int reject_unauth_pipelining(SMTPD_STATE *state) /* has_my_addr - see if this host name lists one of my network addresses */ -static int has_my_addr(char *host) +static int has_my_addr(const char *host) { char *myname = "has_my_addr"; struct in_addr addr; @@ -1012,7 +1060,8 @@ static int has_my_addr(char *host) static int permit_mx_backup(SMTPD_STATE *state, const char *recipient) { char *myname = "permit_mx_backup"; - char *domain; + const RESOLVE_REPLY *reply; + const char *domain; DNS_RR *mx_list; DNS_RR *mx; @@ -1024,15 +1073,14 @@ static int permit_mx_backup(SMTPD_STATE *state, const char *recipient) /* * Resolve the address. */ - canon_addr_internal(query, recipient); - resolve_clnt_query(STR(query), &reply); - lowercase(STR(reply.recipient)); + reply = (const RESOLVE_REPLY *) + ctable_locate(smtpd_resolve_cache, recipient); /* * If the destination is local, it is acceptable, because we are * supposedly MX for our own address. */ - if ((domain = strrchr(STR(reply.recipient), '@')) == 0) + if ((domain = strrchr(CONST_STR(reply->recipient), '@')) == 0) return (SMTPD_CHECK_OK); domain += 1; if (resolve_local(domain) @@ -1048,7 +1096,7 @@ static int permit_mx_backup(SMTPD_STATE *state, const char *recipient) /* * Skip source-routed mail (uncertain destination). */ - if (var_allow_untrust_route == 0 && (reply.flags & RESOLVE_FLAG_ROUTED)) + if (var_allow_untrust_route == 0 && (reply->flags & RESOLVE_FLAG_ROUTED)) return (SMTPD_CHECK_DUNNO); /* @@ -1157,11 +1205,12 @@ static int reject_non_fqdn_address(SMTPD_STATE *state, char *addr, /* reject_unknown_address - fail if address does not resolve */ -static int reject_unknown_address(SMTPD_STATE *state, char *addr, - char *reply_name, char *reply_class) +static int reject_unknown_address(SMTPD_STATE *state, const char *addr, + const char *reply_name, const char *reply_class) { char *myname = "reject_unknown_address"; - char *domain; + const RESOLVE_REPLY *reply; + const char *domain; if (msg_verbose) msg_info("%s: %s", myname, addr); @@ -1169,14 +1218,12 @@ static int reject_unknown_address(SMTPD_STATE *state, char *addr, /* * Resolve the address. */ - canon_addr_internal(query, addr); - resolve_clnt_query(STR(query), &reply); - lowercase(STR(reply.recipient)); + reply = (const RESOLVE_REPLY *) ctable_locate(smtpd_resolve_cache, addr); /* * Skip local destinations and non-DNS forms. */ - if ((domain = strrchr(STR(reply.recipient), '@')) == 0) + if ((domain = strrchr(CONST_STR(reply->recipient), '@')) == 0) return (SMTPD_CHECK_DUNNO); domain += 1; if (resolve_local(domain) @@ -1198,10 +1245,11 @@ static int reject_unknown_address(SMTPD_STATE *state, char *addr, /* check_table_result - translate table lookup result into pass/reject */ -static int check_table_result(SMTPD_STATE *state, char *table, +static int check_table_result(SMTPD_STATE *state, const char *table, const char *value, const char *datum, - char *reply_name, char *reply_class, - char *def_acl) + const char *reply_name, + const char *reply_class, + const char *def_acl) { char *myname = "check_table_result"; int code; @@ -1296,17 +1344,20 @@ static int check_table_result(SMTPD_STATE *state, char *table, /* check_access - table lookup without substring magic */ -static int check_access(SMTPD_STATE *state, char *table, char *name, int flags, - char *reply_name, char *reply_class, char *def_acl) +static int check_access(SMTPD_STATE *state, const char *table, const char *name, + int flags, int *found, const char *reply_name, + const char *reply_class, const char *def_acl) { char *myname = "check_access"; char *low_name = lowercase(mystrdup(name)); const char *value; DICT *dict; -#define CHK_ACCESS_RETURN(x) { myfree(low_name); return(x); } +#define CHK_ACCESS_RETURN(x,y) { *found = y; myfree(low_name); return(x); } #define FULL 0 #define PARTIAL DICT_FLAG_FIXED +#define FOUND 1 +#define MISSED 0 if (msg_verbose) msg_info("%s: %s", myname, name); @@ -1317,19 +1368,20 @@ static int check_access(SMTPD_STATE *state, char *table, char *name, int flags, if ((value = dict_get(dict, low_name)) != 0) CHK_ACCESS_RETURN(check_table_result(state, table, value, name, reply_name, reply_class, - def_acl)); + def_acl), FOUND); if (dict_errno != 0) msg_fatal("%s: table lookup problem", table); } - CHK_ACCESS_RETURN(SMTPD_CHECK_DUNNO); + CHK_ACCESS_RETURN(SMTPD_CHECK_DUNNO, MISSED); } /* check_domain_access - domainname-based table lookup */ -static int check_domain_access(SMTPD_STATE *state, char *table, - char *domain, int flags, - char *reply_name, char *reply_class, - char *def_acl) +static int check_domain_access(SMTPD_STATE *state, const char *table, + const char *domain, int flags, + int *found, const char *reply_name, + const char *reply_class, + const char *def_acl) { char *myname = "check_domain_access"; char *low_domain = lowercase(mystrdup(domain)); @@ -1344,7 +1396,7 @@ static int check_domain_access(SMTPD_STATE *state, char *table, /* * Try the name and its parent domains. Including top-level domains. */ -#define CHK_DOMAIN_RETURN(x) { myfree(low_domain); return(x); } +#define CHK_DOMAIN_RETURN(x,y) { *found = y; myfree(low_domain); return(x); } if ((dict = dict_handle(table)) == 0) msg_panic("%s: dictionary not found: %s", myname, table); @@ -1353,7 +1405,7 @@ static int check_domain_access(SMTPD_STATE *state, char *table, if ((value = dict_get(dict, name)) != 0) CHK_DOMAIN_RETURN(check_table_result(state, table, value, domain, reply_name, reply_class, - def_acl)); + def_acl), FOUND); if (dict_errno != 0) msg_fatal("%s: table lookup problem", table); } @@ -1361,15 +1413,16 @@ static int check_domain_access(SMTPD_STATE *state, char *table, break; flags = PARTIAL; } - CHK_DOMAIN_RETURN(SMTPD_CHECK_DUNNO); + CHK_DOMAIN_RETURN(SMTPD_CHECK_DUNNO, MISSED); } /* check_addr_access - address-based table lookup */ -static int check_addr_access(SMTPD_STATE *state, char *table, - char *address, int flags, - char *reply_name, char *reply_class, - char *def_acl) +static int check_addr_access(SMTPD_STATE *state, const char *table, + const char *address, int flags, + int *found, const char *reply_name, + const char *reply_class, + const char *def_acl) { char *myname = "check_addr_access"; char *addr; @@ -1382,6 +1435,8 @@ static int check_addr_access(SMTPD_STATE *state, char *table, /* * Try the address and its parent networks. */ +#define CHK_ADDR_RETURN(x,y) { *found = y; return(x); } + addr = STR(vstring_strcpy(error_text, address)); if ((dict = dict_handle(table)) == 0) @@ -1389,24 +1444,26 @@ static int check_addr_access(SMTPD_STATE *state, char *table, do { if (flags == 0 || (flags & dict->flags) != 0) { if ((value = dict_get(dict, addr)) != 0) - return (check_table_result(state, table, value, address, - reply_name, reply_class, - def_acl)); + CHK_ADDR_RETURN(check_table_result(state, table, value, address, + reply_name, reply_class, + def_acl), FOUND); if (dict_errno != 0) msg_fatal("%s: table lookup problem", table); } flags = PARTIAL; } while (split_at_right(addr, '.')); - return (SMTPD_CHECK_DUNNO); + CHK_ADDR_RETURN(SMTPD_CHECK_DUNNO, MISSED); } /* check_namadr_access - OK/FAIL based on host name/address lookup */ -static int check_namadr_access(SMTPD_STATE *state, char *table, - char *name, char *addr, int flags, - char *reply_name, char *reply_class, - char *def_acl) +static int check_namadr_access(SMTPD_STATE *state, const char *table, + const char *name, const char *addr, + int flags, int *found, + const char *reply_name, + const char *reply_class, + const char *def_acl) { char *myname = "check_namadr_access"; int status; @@ -1419,16 +1476,16 @@ static int check_namadr_access(SMTPD_STATE *state, char *table, * wildcard may pre-empt a more specific address table entry. */ if ((status = check_domain_access(state, table, name, flags, - reply_name, reply_class, - def_acl)) != 0) + found, reply_name, reply_class, + def_acl)) != 0 || *found) return (status); /* * Look up the network address, or parent networks thereof. */ if ((status = check_addr_access(state, table, addr, flags, - reply_name, reply_class, - def_acl)) != 0) + found, reply_name, reply_class, + def_acl)) != 0 || *found) return (status); /* @@ -1439,12 +1496,15 @@ static int check_namadr_access(SMTPD_STATE *state, char *table, /* check_mail_access - OK/FAIL based on mail address lookup */ -static int check_mail_access(SMTPD_STATE *state, char *table, char *addr, - char *reply_name, char *reply_class, - char *def_acl) +static int check_mail_access(SMTPD_STATE *state, const char *table, + const char *addr, int *found, + const char *reply_name, + const char *reply_class, + const char *def_acl) { char *myname = "check_mail_access"; - char *ratsign; + const RESOLVE_REPLY *reply; + const char *ratsign; int status; char *local_at; @@ -1454,39 +1514,39 @@ static int check_mail_access(SMTPD_STATE *state, char *table, char *addr, /* * Resolve the address. */ - canon_addr_internal(query, addr); - resolve_clnt_query(STR(query), &reply); - lowercase(STR(reply.recipient)); + reply = (const RESOLVE_REPLY *) ctable_locate(smtpd_resolve_cache, addr); /* * Garbage in, garbage out. Every address from canon_addr_internal() and * from resolve_clnt_query() must be fully qualified. */ - if ((ratsign = strrchr(STR(reply.recipient), '@')) == 0) { - msg_warn("%s: no @domain in address: %s", myname, STR(reply.recipient)); + if ((ratsign = strrchr(CONST_STR(reply->recipient), '@')) == 0) { + msg_warn("%s: no @domain in address: %s", myname, CONST_STR(reply->recipient)); return (0); } /* * Look up the full address. */ - if ((status = check_access(state, table, STR(reply.recipient), FULL, - reply_name, reply_class, def_acl)) != 0) + if ((status = check_access(state, table, CONST_STR(reply->recipient), FULL, + found, reply_name, reply_class, def_acl)) != 0 + || *found) return (status); /* * Look up the domain name, or parent domains thereof. */ if ((status = check_domain_access(state, table, ratsign + 1, PARTIAL, - reply_name, reply_class, def_acl)) != 0) + found, reply_name, reply_class, def_acl)) != 0 + || *found) return (status); /* * Look up localpart@ */ - local_at = mystrndup(STR(reply.recipient), - ratsign - STR(reply.recipient) + 1); - status = check_access(state, table, local_at, PARTIAL, + local_at = mystrndup(CONST_STR(reply->recipient), + ratsign - CONST_STR(reply->recipient) + 1); + status = check_access(state, table, local_at, PARTIAL, found, reply_name, reply_class, def_acl); myfree(local_at); if (status != 0) @@ -1587,7 +1647,7 @@ static int reject_maps_rbl(SMTPD_STATE *state) /* is_map_command - restriction has form: check_xxx_access type:name */ -static int is_map_command(char *name, char *command, char ***argp) +static int is_map_command(const char *name, const char *command, char ***argp) { /* @@ -1610,13 +1670,16 @@ static int is_map_command(char *name, char *command, char ***argp) /* generic_checks - generic restrictions */ static int generic_checks(SMTPD_STATE *state, ARGV *restrictions, - char *reply_name, char *reply_class, char *def_acl) + const char *reply_name, + const char *reply_class, + const char *def_acl) { char *myname = "generic_checks"; char **cpp; - char *name; + const char *name; int status = 0; ARGV *list; + int found; if (msg_verbose) msg_info("%s: START", myname); @@ -1663,7 +1726,7 @@ static int generic_checks(SMTPD_STATE *state, ARGV *restrictions, status = permit_mynetworks(state); } else if (is_map_command(name, CHECK_CLIENT_ACL, &cpp)) { status = check_namadr_access(state, *cpp, state->name, state->addr, - FULL, state->namaddr, + FULL, &found, state->namaddr, SMTPD_NAME_CLIENT, def_acl); } else if (strcasecmp(name, REJECT_MAPS_RBL) == 0) { status = reject_maps_rbl(state); @@ -1675,7 +1738,7 @@ static int generic_checks(SMTPD_STATE *state, ARGV *restrictions, else if (is_map_command(name, CHECK_HELO_ACL, &cpp)) { if (state->helo_name) status = check_domain_access(state, *cpp, state->helo_name, - FULL, state->helo_name, + FULL, &found, state->helo_name, SMTPD_NAME_HELO, def_acl); } else if (strcasecmp(name, REJECT_INVALID_HOSTNAME) == 0) { if (state->helo_name) { @@ -1719,7 +1782,7 @@ static int generic_checks(SMTPD_STATE *state, ARGV *restrictions, else if (is_map_command(name, CHECK_SENDER_ACL, &cpp)) { if (state->sender && *state->sender) status = check_mail_access(state, *cpp, state->sender, - state->sender, + &found, state->sender, SMTPD_NAME_SENDER, def_acl); } else if (strcasecmp(name, REJECT_UNKNOWN_ADDRESS) == 0) { if (state->sender && *state->sender) @@ -1741,7 +1804,7 @@ static int generic_checks(SMTPD_STATE *state, ARGV *restrictions, else if (is_map_command(name, CHECK_RECIP_ACL, &cpp)) { if (state->recipient) status = check_mail_access(state, *cpp, state->recipient, - state->recipient, + &found, state->recipient, SMTPD_NAME_RECIPIENT, def_acl); } else if (strcasecmp(name, PERMIT_MX_BACKUP) == 0) { if (state->recipient) @@ -1781,7 +1844,7 @@ static int generic_checks(SMTPD_STATE *state, ARGV *restrictions, else if (is_map_command(name, CHECK_ETRN_ACL, &cpp)) { if (state->etrn_name) status = check_domain_access(state, *cpp, state->etrn_name, - FULL, state->etrn_name, + FULL, &found, state->etrn_name, SMTPD_NAME_ETRN, def_acl); } @@ -2023,7 +2086,8 @@ char *smtpd_check_rcptmap(SMTPD_STATE *state, char *recipient) { char *myname = "smtpd_check_rcptmap"; char *saved_recipient; - char *domain; + const RESOLVE_REPLY *reply; + const char *domain; int status; /* @@ -2050,14 +2114,13 @@ char *smtpd_check_rcptmap(SMTPD_STATE *state, char *recipient) /* * Resolve the address. */ - canon_addr_internal(query, recipient); - resolve_clnt_query(STR(query), &reply); - lowercase(STR(reply.recipient)); + reply = (const RESOLVE_REPLY *) + ctable_locate(smtpd_resolve_cache, recipient); /* * Skip non-DNS forms. Skip non-local numerical forms. */ - if ((domain = strrchr(STR(reply.recipient), '@')) == 0) + if ((domain = strrchr(CONST_STR(reply->recipient), '@')) == 0) SMTPD_CHECK_RCPT_RETURN(0); domain += 1; if (domain[0] == '#' || domain[0] == '[') @@ -2072,11 +2135,11 @@ char *smtpd_check_rcptmap(SMTPD_STATE *state, char *recipient) */ if (*var_virtual_maps && (check_maps_find(state, recipient, virtual_maps, domain, 0))) { - if (NOMATCH(rcpt_canon_maps, STR(reply.recipient)) - && NOMATCH(canonical_maps, STR(reply.recipient)) - && NOMATCH(relocated_maps, STR(reply.recipient)) - && NOMATCH(virt_mailbox_maps, STR(reply.recipient)) - && NOMATCH(virtual_maps, STR(reply.recipient))) { + if (NOMATCH(rcpt_canon_maps, CONST_STR(reply->recipient)) + && NOMATCH(canonical_maps, CONST_STR(reply->recipient)) + && NOMATCH(relocated_maps, CONST_STR(reply->recipient)) + && NOMATCH(virt_mailbox_maps, CONST_STR(reply->recipient)) + && NOMATCH(virtual_maps, CONST_STR(reply->recipient))) { (void) smtpd_check_reject(state, MAIL_ERROR_BOUNCE, "%d <%s>: User unknown", 550, recipient); SMTPD_CHECK_RCPT_RETURN(STR(error_text)); @@ -2087,12 +2150,12 @@ char *smtpd_check_rcptmap(SMTPD_STATE *state, char *recipient) * Reject mail to unknown addresses in Postfix-style virtual domains. */ if (*var_virt_mailbox_maps - && (check_maps_find(state, recipient, virt_mailbox_maps, domain, 0))) { - if (NOMATCH(rcpt_canon_maps, STR(reply.recipient)) - && NOMATCH(canonical_maps, STR(reply.recipient)) - && NOMATCH(relocated_maps, STR(reply.recipient)) - && NOMATCH(virt_mailbox_maps, STR(reply.recipient)) - && NOMATCH(virtual_maps, STR(reply.recipient))) { + && (check_maps_find(state, recipient, virt_mailbox_maps, domain, 0))) { + if (NOMATCH(rcpt_canon_maps, CONST_STR(reply->recipient)) + && NOMATCH(canonical_maps, CONST_STR(reply->recipient)) + && NOMATCH(relocated_maps, CONST_STR(reply->recipient)) + && NOMATCH(virt_mailbox_maps, CONST_STR(reply->recipient)) + && NOMATCH(virtual_maps, CONST_STR(reply->recipient))) { (void) smtpd_check_reject(state, MAIL_ERROR_BOUNCE, "%d <%s>: User unknown", 550, recipient); SMTPD_CHECK_RCPT_RETURN(STR(error_text)); @@ -2105,12 +2168,12 @@ char *smtpd_check_rcptmap(SMTPD_STATE *state, char *recipient) * Sendmail-style virtual domains. */ if (*var_local_rcpt_maps && resolve_local(domain)) { - if (NOMATCH(rcpt_canon_maps, STR(reply.recipient)) - && NOMATCH(canonical_maps, STR(reply.recipient)) - && NOMATCH(relocated_maps, STR(reply.recipient)) - && NOMATCH(virt_mailbox_maps, STR(reply.recipient)) - && NOMATCH(virtual_maps, STR(reply.recipient)) - && NOMATCH(local_rcpt_maps, STR(reply.recipient))) { + if (NOMATCH(rcpt_canon_maps, CONST_STR(reply->recipient)) + && NOMATCH(canonical_maps, CONST_STR(reply->recipient)) + && NOMATCH(relocated_maps, CONST_STR(reply->recipient)) + && NOMATCH(virt_mailbox_maps, CONST_STR(reply->recipient)) + && NOMATCH(virtual_maps, CONST_STR(reply->recipient)) + && NOMATCH(local_rcpt_maps, CONST_STR(reply->recipient))) { (void) smtpd_check_reject(state, MAIL_ERROR_BOUNCE, "%d <%s>: User unknown", 550, recipient); SMTPD_CHECK_RCPT_RETURN(STR(error_text)); @@ -2377,11 +2440,19 @@ static void rest_class(char *class) void resolve_clnt_init(RESOLVE_REPLY *reply) { + reply->flags = 0; reply->transport = vstring_alloc(100); reply->nexthop = vstring_alloc(100); reply->recipient = vstring_alloc(100); } +void resolve_clnt_free(RESOLVE_REPLY *reply) +{ + vstring_free(reply->transport); + vstring_free(reply->nexthop); + vstring_free(reply->recipient); +} + #ifdef USE_SASL_AUTH bool var_smtpd_sasl_enable = 0; @@ -2424,7 +2495,7 @@ VSTRING *canon_addr_internal(VSTRING *result, const char *addr) void resolve_clnt_query(const char *addr, RESOLVE_REPLY *reply) { - if (addr == STR(reply->recipient)) + if (addr == CONST_STR(reply->recipient)) msg_panic("resolve_clnt_query: result clobbers input"); vstring_strcpy(reply->transport, "foo"); vstring_strcpy(reply->nexthop, "foo"); @@ -2446,7 +2517,7 @@ static NORETURN usage(char *myname) msg_fatal("usage: %s", myname); } -main(int argc, char **argv) +int main(int argc, char **argv) { VSTRING *buf = vstring_alloc(100); SMTPD_STATE state; diff --git a/postfix/src/smtpd/smtpd_state.c b/postfix/src/smtpd/smtpd_state.c index ca4929634..c22ebd5c8 100644 --- a/postfix/src/smtpd/smtpd_state.c +++ b/postfix/src/smtpd/smtpd_state.c @@ -91,6 +91,7 @@ void smtpd_state_init(SMTPD_STATE *state, VSTREAM *stream) state->recursion = 0; state->msg_size = 0; state->junk_cmds = 0; + state->error_text = vstring_alloc(100); #ifdef USE_SASL_AUTH if (SMTPD_STAND_ALONE(state)) @@ -123,6 +124,7 @@ void smtpd_state_reset(SMTPD_STATE *state) if (state->buffer) vstring_free(state->buffer); smtpd_peer_reset(state); + vstring_free(state->error_text); #ifdef USE_SASL_AUTH if (var_smtpd_sasl_enable) diff --git a/postfix/src/smtpd/smtpd_token.c b/postfix/src/smtpd/smtpd_token.c index 09d64cd01..66bc6bc9c 100644 --- a/postfix/src/smtpd/smtpd_token.c +++ b/postfix/src/smtpd/smtpd_token.c @@ -196,7 +196,7 @@ int smtpd_token(char *cp, SMTPD_TOKEN **argvp) #include #include -main(int unused_argc, char **unused_argv) +int main(int unused_argc, char **unused_argv) { VSTRING *vp = vstring_alloc(10); int tok_argc; diff --git a/postfix/src/util/Makefile.in b/postfix/src/util/Makefile.in index a2dd61bb2..d7e85a063 100644 --- a/postfix/src/util/Makefile.in +++ b/postfix/src/util/Makefile.in @@ -23,7 +23,7 @@ SRCS = argv.c argv_split.c attr.c basename.c binhash.c chroot_uid.c \ clean_env.c watchdog.c spawn_command.c duplex_pipe.c sane_rename.c \ sane_link.c unescape.c timed_read.c timed_write.c dict_tcp.c \ hex_quote.c dict_alloc.c rand_sleep.c sane_time.c dict_debug.c \ - sane_socketpair.c myrand.c netstring.c + sane_socketpair.c myrand.c netstring.c ctable.c OBJS = argv.o argv_split.o attr.o basename.o binhash.o chroot_uid.o \ close_on_exec.o concatenate.o dict.o dict_db.o dict_dbm.o \ dict_env.o dict_ht.o dict_ldap.o dict_mysql.o dict_ni.o dict_nis.o \ @@ -48,7 +48,7 @@ OBJS = argv.o argv_split.o attr.o basename.o binhash.o chroot_uid.o \ clean_env.o watchdog.o spawn_command.o duplex_pipe.o sane_rename.o \ sane_link.o unescape.o timed_read.o timed_write.o dict_tcp.o \ hex_quote.o dict_alloc.o rand_sleep.o sane_time.o dict_debug.o \ - sane_socketpair.o myrand.o netstring.o + sane_socketpair.o myrand.o netstring.o ctable.o HDRS = argv.h attr.h binhash.h chroot_uid.h connect.h dict.h dict_db.h \ dict_dbm.h dict_env.h dict_ht.h dict_ldap.h dict_mysql.h \ dict_ni.h dict_nis.h dict_nisplus.h dir_forest.h events.h \ @@ -64,7 +64,7 @@ HDRS = argv.h attr.h binhash.h chroot_uid.h connect.h dict.h dict_db.h \ vbuf.h vbuf_print.h vstream.h vstring.h vstring_vstream.h \ dict_unix.h dict_pcre.h dict_regexp.h mac_expand.h clean_env.h \ watchdog.h spawn_command.h sane_fsops.h dict_tcp.h hex_quote.h \ - sane_time.h sane_socketpair.h myrand.h netstring.h + sane_time.h sane_socketpair.h myrand.h netstring.h ctable.h TESTSRC = fifo_open.c fifo_rdwr_bug.c fifo_rdonly_bug.c select_bug.c \ stream_test.c dup2_pass_on_exec.c WARN = -W -Wformat -Wimplicit -Wmissing-prototypes \ @@ -80,7 +80,7 @@ TESTPROG= dict_open dup2_pass_on_exec events exec_command fifo_open \ inet_addr_host inet_addr_local mac_parse make_dirs msg_syslog \ mystrtok sigdelay translit valid_hostname vstream_popen \ vstring vstring_vstream doze select_bug stream_test mac_expand \ - watchdog unescape hex_quote name_mask rand_sleep sane_time + watchdog unescape hex_quote name_mask rand_sleep sane_time ctable LIB_DIR = ../../lib INC_DIR = ../../include @@ -274,6 +274,11 @@ sane_time: $(LIB) $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS) mv junk $@.o +ctable: $(LIB) + mv $@.o junk + $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS) + mv junk $@.o + depend: $(MAKES) (sed '1,/^# do not edit/!d' Makefile.in; \ set -e; for i in [a-z][a-z0-9]*.c; do \ @@ -286,7 +291,7 @@ stream_test: stream_test.c $(LIB) $(CC) $(CFLAGS) -o $@ $@.c $(LIB) $(SYSLIBS) tests: valid_hostname_test mac_expand_test dict_test unescape_test \ - hex_quote_test + hex_quote_test cache_test valid_hostname_test: valid_hostname valid_hostname.in valid_hostname.ref ./valid_hostname valid_hostname.tmp @@ -309,6 +314,11 @@ hex_quote_test: hex_quote cmp hex_quote.ref hex_quote.tmp rm -f hex_quote.ref hex_quote.tmp +ctable_test: ctable + ./ctable ctable.tmp 2>&1 + diff ctable.ref ctable.tmp + rm -f ctable.tmp + DB_TYPE = `../postconf/postconf -h default_database_type` dict_test: dict_open testdb dict_test.in dict_test.ref @@ -368,6 +378,13 @@ concatenate.o: mymalloc.h concatenate.o: stringops.h concatenate.o: vstring.h concatenate.o: vbuf.h +ctable.o: ctable.c +ctable.o: sys_defs.h +ctable.o: msg.h +ctable.o: mymalloc.h +ctable.o: ring.h +ctable.o: htable.h +ctable.o: ctable.h dict.o: dict.c dict.o: sys_defs.h dict.o: msg.h diff --git a/postfix/src/util/cache b/postfix/src/util/cache new file mode 100755 index 0000000000000000000000000000000000000000..607a835a764043b7bbc11d9569c44864f449cf73 GIT binary patch literal 82681 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

    {*ttK3n$sd!qR{=ofjghfafIMRbMPce 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 LpsU$Z(8YfOTCMg9 literal 0 HcmV?d00001 diff --git a/postfix/src/util/cache.in b/postfix/src/util/cache.in new file mode 100644 index 000000000..89974bd2c --- /dev/null +++ b/postfix/src/util/cache.in @@ -0,0 +1,26 @@ +a +1 +b +2 +c +3 +d +4 +e +5 +f +6 +f +e +d +c +b +a +1 +b +c +d +e +f +6 +f diff --git a/postfix/src/util/ctable.c b/postfix/src/util/ctable.c new file mode 100644 index 000000000..4e6d18f5e --- /dev/null +++ b/postfix/src/util/ctable.c @@ -0,0 +1,273 @@ +/*++ +/* NAME +/* ctable 3 +/* SUMMARY +/* cache manager +/* SYNOPSIS +/* #include +/* +/* CTABLE *ctable_create(limit, create, delete, context) +/* int limit; +/* void *(*create)(const char *key, void *context); +/* void (*delete)(void *value, void *context); +/* void *context; +/* +/* const void *ctable_locate(cache, key) +/* CTABLE *cache; +/* const char *key; +/* +/* void ctable_free(cache) +/* CTABLE *cache; +/* +/* void ctable_walk(cache, action) +/* CTABLE *cache; +/* void (*action)(const char *key, const void *value); +/* DESCRIPTION +/* This module maintains multiple caches. Cache items are purged +/* automatically when the number of items exceeds a configurable +/* limit. Caches never shrink. Each cache entry consists of a +/* string-valued lookup key and a generic data pointer value. +/* +/* ctable_create() creates a cache with the specified size limit, and +/* returns a pointer to the result. The create and delete arguments +/* specify pointers to call-back functions that create a value, given +/* a key, and delete a given value, respectively. The context argument +/* is passed on to the call-back routines. +/* +/* ctable_locate() looks up or generates the value that corresponds to +/* the specified key, and returns that value. +/* +/* ctable_free() destroys the specified cache, including its contents. +/* +/* ctable_walk() iterates over all elements in the cache, and invokes +/* the action function for each cache element with the corresponding +/* key and value as arguments. This function is useful mainly for +/* cache performance debugging. +/* DIAGNOSTICS +/* Fatal errors: out of memory. Panic: interface violation. +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +/* System library. */ + +#include +#include +#include + +/* Utility library. */ + +#include +#include +#include +#include +#include + + /* + * Cache entries are kept in most-recently used order. We use a hash table + * to quickly locate cache entries. + */ +#define CTABLE_ENTRY struct ctable_entry + +struct ctable_entry { + RING ring; /* MRU linkage */ + const char *key; /* lookup key */ + void *value; /* corresponding value */ +}; + +#define RING_TO_CTABLE_ENTRY(ring_ptr) \ + RING_TO_APPL(ring_ptr, CTABLE_ENTRY, ring) +#define RING_PTR_OF(x) (&((x)->ring)) + +struct ctable { + HTABLE *table; /* table with key, ctable_entry pairs */ + unsigned limit; /* max nr of entries */ + unsigned used; /* current nr of entries */ + CTABLE_CREATE_FN create; /* constructor */ + CTABLE_DELETE_FN delete; /* destructor */ + RING ring; /* MRU linkage */ + void *context; /* application context */ +}; + +#define CTABLE_MIN_SIZE 5 + +/* ctable_create - create empty cache */ + +CTABLE *ctable_create(int limit, CTABLE_CREATE_FN create, + CTABLE_DELETE_FN delete, void *context) +{ + CTABLE *cache = (CTABLE *) mymalloc(sizeof(CTABLE)); + char *myname = "ctable_create"; + + if (limit < 1) + msg_panic("%s: bad cache limit: %d", myname, limit); + + cache->table = htable_create(limit); + cache->limit = (limit < CTABLE_MIN_SIZE ? CTABLE_MIN_SIZE : limit); + cache->used = 0; + cache->create = create; + cache->delete = delete; + ring_init(RING_PTR_OF(cache)); + cache->context = context; + return (cache); +} + +/* ctable_locate - look up or create cache item */ + +const void *ctable_locate(CTABLE *cache, const char *key) +{ + char *myname = "ctable_locate"; + CTABLE_ENTRY *entry; + + /* + * If the entry is not in the cache, make sure there is room for a new + * entry and install it at the front of the MRU chain. Otherwise, move + * the entry to the front of the MRU chain if it is not already there. + * All this means that the cache never shrinks. + */ + if ((entry = (CTABLE_ENTRY *) htable_find(cache->table, key)) == 0) { + if (cache->used >= cache->limit) { + entry = RING_TO_CTABLE_ENTRY(ring_pred(RING_PTR_OF(cache))); + if (msg_verbose) + msg_info("%s: purge entry key %s", myname, entry->key); + ring_detach(RING_PTR_OF(entry)); + cache->delete(entry->value, cache->context); + htable_delete(cache->table, entry->key, (void (*) (char *)) 0); + } else { + entry = (CTABLE_ENTRY *) mymalloc(sizeof(CTABLE_ENTRY)); + cache->used++; + } + entry->value = cache->create(key, cache->context); + entry->key = htable_enter(cache->table, key, (char *) entry)->key; + ring_append(RING_PTR_OF(cache), RING_PTR_OF(entry)); + if (msg_verbose) + msg_info("%s: install entry key %s", myname, entry->key); + } else if (entry == RING_TO_CTABLE_ENTRY(ring_succ(RING_PTR_OF(cache)))) { + if (msg_verbose) + msg_info("%s: leave existing entry key %s", myname, entry->key); + } else { + ring_detach(RING_PTR_OF(entry)); + ring_append(RING_PTR_OF(cache), RING_PTR_OF(entry)); + if (msg_verbose) + msg_info("%s: move existing entry key %s", myname, entry->key); + } + return (entry->value); +} + +static CTABLE *ctable_free_cache; + +/* ctable_free_callback - callback function */ + +static void ctable_free_callback(char *ptr) +{ + CTABLE_ENTRY *entry = (CTABLE_ENTRY *) ptr; + + ctable_free_cache->delete(entry->value, ctable_free_cache->context); + myfree((char *) entry); +} + +/* ctable_free - destroy cache and contents */ + +void ctable_free(CTABLE *cache) +{ + CTABLE *saved_cache = ctable_free_cache; + + /* + * XXX the hash table does not pass application context so we have to + * store it in a global variable. + */ + ctable_free_cache = cache; + htable_free(cache->table, ctable_free_callback); + myfree((char *) cache); + ctable_free_cache = saved_cache; +} + +/* ctable_walk - iterate over all cache entries */ + +void ctable_walk(CTABLE *cache, void (*action) (const char *, const void *)) +{ + RING *entry = RING_PTR_OF(cache); + + /* Walking down the MRU chain is less work than using ht_walk(). */ + + while ((entry = ring_succ(entry)) != RING_PTR_OF(cache)) + action((RING_TO_CTABLE_ENTRY(entry)->key), + (RING_TO_CTABLE_ENTRY(entry)->value)); +} + +#ifdef TEST + + /* + * Proof-of-concept test program. Read keys from stdin, ask for values not + * in cache. + */ +#include +#include +#include +#include +#include + +#define STR(x) vstring_str(x) + +static void *ask(const char *key, void *context) +{ + VSTRING *data_buf = (VSTRING *) context; + + vstream_printf("ask: %s = ", key); + vstream_fflush(VSTREAM_OUT); + if (vstring_get_nonl(data_buf, VSTREAM_IN) == VSTREAM_EOF) + vstream_longjmp(VSTREAM_IN, 1); + if (!isatty(0)) { + vstream_printf("%s\n", STR(data_buf)); + vstream_fflush(VSTREAM_OUT); + } + return (mystrdup(STR(data_buf))); +} + +static void drop(void *data, void *unused_context) +{ + myfree(data); +} + +int main(int unused_argc, char **argv) +{ + VSTRING *key_buf; + VSTRING *data_buf; + CTABLE *cache; + const char *value; + + msg_vstream_init(argv[0], VSTREAM_ERR); + key_buf = vstring_alloc(100); + data_buf = vstring_alloc(100); + cache = ctable_create(1, ask, drop, (void *) data_buf); + msg_verbose = 1; + vstream_control(VSTREAM_IN, VSTREAM_CTL_EXCEPT, VSTREAM_CTL_END); + + if (vstream_setjmp(VSTREAM_IN) == 0) { + for (;;) { + vstream_printf("key = "); + vstream_fflush(VSTREAM_OUT); + if (vstring_get_nonl(key_buf, VSTREAM_IN) == VSTREAM_EOF) + vstream_longjmp(VSTREAM_IN, 1); + if (!isatty(0)) { + vstream_printf("%s\n", STR(key_buf)); + vstream_fflush(VSTREAM_OUT); + } + value = ctable_locate(cache, STR(key_buf)); + vstream_printf("result: %s\n", value); + } + } + ctable_free(cache); + vstring_free(key_buf); + vstring_free(data_buf); + return (0); +} + +#endif diff --git a/postfix/src/util/ctable.h b/postfix/src/util/ctable.h new file mode 100644 index 000000000..7e3d899b3 --- /dev/null +++ b/postfix/src/util/ctable.h @@ -0,0 +1,39 @@ +#ifndef _CTABLE_H_INCLUDED_ +#define _CTABLE_H_INCLUDED_ + +/*++ +/* NAME +/* ctable 5 +/* SUMMARY +/* cache manager +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * Interface of the cache manager. The structure of a cache is not visible + * to the caller. + */ + +#define CTABLE struct ctable +typedef void *(*CTABLE_CREATE_FN) (const char *, void *); +typedef void (*CTABLE_DELETE_FN) (void *, void *); + +extern CTABLE *ctable_create(int, CTABLE_CREATE_FN, CTABLE_DELETE_FN, void *); +extern void ctable_free(CTABLE *); +extern void ctable_walk(CTABLE *, void (*) (const char *, const void *)); +extern const void *ctable_locate(CTABLE *, const char *); + +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +#endif diff --git a/postfix/src/util/ctable.in b/postfix/src/util/ctable.in new file mode 100644 index 000000000..78763cfaf --- /dev/null +++ b/postfix/src/util/ctable.in @@ -0,0 +1,39 @@ +a +1 +b +2 +c +3 +d +4 +e +5 +f +6 +f +a +1 +b +2 +c +3 +d +4 +e +5 +f +6 +f +e +d +c +b +a +1 +b +c +d +e +f +6 +f diff --git a/postfix/src/util/ctable.ref b/postfix/src/util/ctable.ref new file mode 100644 index 000000000..34e8a951a --- /dev/null +++ b/postfix/src/util/ctable.ref @@ -0,0 +1,99 @@ +key = a +ask: a = 1 +./ctable: ctable_locate: install entry key a +result: 1 +key = b +ask: b = 2 +./ctable: ctable_locate: install entry key b +result: 2 +key = c +ask: c = 3 +./ctable: ctable_locate: install entry key c +result: 3 +key = d +ask: d = 4 +./ctable: ctable_locate: install entry key d +result: 4 +key = e +ask: e = 5 +./ctable: ctable_locate: install entry key e +result: 5 +key = f +./ctable: ctable_locate: purge entry key a +ask: f = 6 +./ctable: ctable_locate: install entry key f +result: 6 +key = f +./ctable: ctable_locate: leave existing entry key f +result: 6 +key = a +./ctable: ctable_locate: purge entry key b +ask: a = 1 +./ctable: ctable_locate: install entry key a +result: 1 +key = b +./ctable: ctable_locate: purge entry key c +ask: b = 2 +./ctable: ctable_locate: install entry key b +result: 2 +key = c +./ctable: ctable_locate: purge entry key d +ask: c = 3 +./ctable: ctable_locate: install entry key c +result: 3 +key = d +./ctable: ctable_locate: purge entry key e +ask: d = 4 +./ctable: ctable_locate: install entry key d +result: 4 +key = e +./ctable: ctable_locate: purge entry key f +ask: e = 5 +./ctable: ctable_locate: install entry key e +result: 5 +key = f +./ctable: ctable_locate: purge entry key a +ask: f = 6 +./ctable: ctable_locate: install entry key f +result: 6 +key = f +./ctable: ctable_locate: leave existing entry key f +result: 6 +key = e +./ctable: ctable_locate: move existing entry key e +result: 5 +key = d +./ctable: ctable_locate: move existing entry key d +result: 4 +key = c +./ctable: ctable_locate: move existing entry key c +result: 3 +key = b +./ctable: ctable_locate: move existing entry key b +result: 2 +key = a +./ctable: ctable_locate: purge entry key f +ask: a = 1 +./ctable: ctable_locate: install entry key a +result: 1 +key = b +./ctable: ctable_locate: move existing entry key b +result: 2 +key = c +./ctable: ctable_locate: move existing entry key c +result: 3 +key = d +./ctable: ctable_locate: move existing entry key d +result: 4 +key = e +./ctable: ctable_locate: move existing entry key e +result: 5 +key = f +./ctable: ctable_locate: purge entry key a +ask: f = 6 +./ctable: ctable_locate: install entry key f +result: 6 +key = f +./ctable: ctable_locate: leave existing entry key f +result: 6 +key = \ No newline at end of file diff --git a/postfix/src/util/dict_ldap.c b/postfix/src/util/dict_ldap.c index 776a437db..528769248 100644 --- a/postfix/src/util/dict_ldap.c +++ b/postfix/src/util/dict_ldap.c @@ -151,6 +151,10 @@ static int dict_ldap_connect(DICT_LDAP *dict_ldap) void (*saved_alarm) (int); int rc = 0; +#ifdef LDAP_API_FEATURE_X_MEMCACHE + LDAPMemCache *dircache; +#endif + #ifdef LDAP_OPT_NETWORK_TIMEOUT struct timeval mytimeval; @@ -162,7 +166,7 @@ static int dict_ldap_connect(DICT_LDAP *dict_ldap) msg_info("%s: Connecting to server %s", myname, dict_ldap->server_host); -#ifdef UNTESTED_LDAP_OPT_NETWORK_TIMEOUT +#ifdef LDAP_OPT_NETWORK_TIMEOUT dict_ldap->ld = ldap_init(dict_ldap->server_host, (int) dict_ldap->server_port); if (dict_ldap->ld == NULL) { @@ -247,6 +251,27 @@ static int dict_ldap_connect(DICT_LDAP *dict_ldap) myname, dict_ldap->cache_size, dict_ldap->ldapsource, dict_ldap->cache_expiry); +#ifdef LDAP_API_FEATURE_X_MEMCACHE + rc = ldap_memcache_init(dict_ldap->cache_expiry, dict_ldap->cache_size, + NULL, NULL, &dircache); + if (rc != LDAP_SUCCESS) { + msg_warn + ("%s: Unable to configure cache for %s: %d (%s) -- continuing", + myname, dict_ldap->ldapsource, rc, ldap_err2string(rc)); + } else { + rc = ldap_memcache_set(dict_ldap->ld, dircache); + if (rc != LDAP_SUCCESS) { + msg_warn + ("%s: Unable to configure cache for %s: %d (%s) -- continuing", + myname, dict_ldap->ldapsource, rc, ldap_err2string(rc)); + } else { + if (msg_verbose) + msg_info("%s: Caching enabled for %s", + myname, dict_ldap->ldapsource); + } + } +#else + rc = ldap_enable_cache(dict_ldap->ld, dict_ldap->cache_expiry, dict_ldap->cache_size); if (rc != LDAP_SUCCESS) { @@ -258,6 +283,8 @@ static int dict_ldap_connect(DICT_LDAP *dict_ldap) msg_info("%s: Caching enabled for %s", myname, dict_ldap->ldapsource); } + +#endif } if (msg_verbose) msg_info("%s: Cached connection handle for LDAP source %s", @@ -315,7 +342,7 @@ static void dict_ldap_get_values(DICT_LDAP *dict_ldap, LDAPMessage * res, if (strcasecmp(dict_ldap->result_attributes->argv[i], attr) == 0) { if (msg_verbose) - msg_info("%s: search returned value(s) for requested result attribute %s", myname, attr); + msg_info("%s: search returned %d value(s) for requested result attribute %s", myname, i, attr); break; } } @@ -390,7 +417,7 @@ static const char *dict_ldap_lookup(DICT *dict, const char *name) * load on the LDAP server. */ if (dict_ldap->domain) { - char *p=strrchr(name,'@'); + const char *p=strrchr(name,'@'); if (p != 0) p=p+1; else @@ -482,7 +509,7 @@ static const char *dict_ldap_lookup(DICT *dict, const char *name) /* * Does the supplied query_filter even include a substitution? */ - if ((char *) strstr(dict_ldap->query_filter, "%s") == NULL) { + if ((char *) strchr(dict_ldap->query_filter, '%') == NULL) { /* * No, log the fact and continue. @@ -494,21 +521,40 @@ static const char *dict_ldap_lookup(DICT *dict, const char *name) /* * Yes, replace all instances of %s with the address to look up. + * Replace %u with the user portion, and %d with the domain portion. */ sub = dict_ldap->query_filter; end = sub + strlen(dict_ldap->query_filter); while (sub < end) { /* - * Make sure it's %s and not something else, though it wouldn't - * really matter; the token could be any single character. + * Make sure it's %[sud] and not something else. For backward + * compatibilty, treat anything other than %u or %d as %s, with + * a warning. */ if (*(sub) == '%') { - if ((sub + 1) != end && *(sub + 1) != 's') - msg_warn - ("%s: Invalid lookup substitution format '%%%c'!", - myname, *(sub + 1)); - vstring_strcat(filter_buf, vstring_str(escaped_name)); + char *u=vstring_str(escaped_name); + char *p=strchr(u,'@'); + switch (*(sub+1)) { + case 'd': + if (p) + vstring_strcat(filter_buf, p+1); + break; + case 'u': + if (p) + vstring_strncat(filter_buf, u, p-u); + else + vstring_strcat(filter_buf, u); + break; + default: + msg_warn + ("%s: Invalid lookup substitution format '%%%c'!", + myname, *(sub + 1)); + /* fall through */ + case 's': + vstring_strcat(filter_buf, u); + break; + } sub++; } else vstring_strncat(filter_buf, sub, 1); @@ -607,7 +653,8 @@ static void dict_ldap_close(DICT *dict) myfree(dict_ldap->ldapsource); myfree(dict_ldap->server_host); myfree(dict_ldap->search_base); - match_list_free(dict_ldap->domain); + if (dict_ldap->domain) + match_list_free(dict_ldap->domain); myfree(dict_ldap->query_filter); argv_free(dict_ldap->result_attributes); myfree(dict_ldap->bind_dn); @@ -626,15 +673,15 @@ DICT *dict_ldap_open(const char *ldapsource, int dummy, int dict_flags) char *scope; char *attr; + if (msg_verbose) + msg_info("%s: Using LDAP source %s", myname, ldapsource); + dict_ldap = (DICT_LDAP *) dict_alloc(DICT_TYPE_LDAP, ldapsource, sizeof(*dict_ldap)); dict_ldap->dict.lookup = dict_ldap_lookup; dict_ldap->dict.close = dict_ldap_close; dict_ldap->dict.flags = dict_flags | DICT_FLAG_FIXED; - if (msg_verbose) - msg_info("%s: Using LDAP source %s", myname, ldapsource); - dict_ldap->ldapsource = mystrdup(ldapsource); config_param = vstring_alloc(15); diff --git a/postfix/src/util/ring.h b/postfix/src/util/ring.h index a96fe2696..d0219cf80 100644 --- a/postfix/src/util/ring.h +++ b/postfix/src/util/ring.h @@ -29,6 +29,17 @@ extern void ring_detach(RING *); #define ring_succ(c) ((c)->succ) #define ring_pred(c) ((c)->pred) + /* + * Typically, an application will embed a RING structure into a larger + * structure that also contains application-specific members. This approach + * gives us the best of both worlds. The application can still use the + * generic RING primitives for manipulating RING structures. The macro below + * transforms a pointer from RING structure to the structure that contains + * it. + */ +#define RING_TO_APPL(ring_ptr,app_type,ring_member) \ + ((app_type *) (((char *) (ring_ptr)) - offsetof(app_type,ring_member))) + /* LICENSE /* .ad /* .fi