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 000000000..607a835a7 Binary files /dev/null and b/postfix/src/util/cache differ 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