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 | 20 +- 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, 930 insertions(+), 175 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. - */ -#define VAR_GLOB_FLOW_CTL "global_mail_flow_control" -#define DEF_GLOB_FLOW_CTL 0 -extern int var_glob_flow_ctl; + * 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_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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 +/* +/* 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 -- 2.47.3