]> git.ipfire.org Git - thirdparty/postfix.git/commitdiff
snapshot-20010729
authorWietse Venema <wietse@porcupine.org>
Sun, 29 Jul 2001 05:00:00 +0000 (00:00 -0500)
committerViktor Dukhovni <viktor@dukhovni.org>
Tue, 5 Feb 2013 06:27:23 +0000 (06:27 +0000)
32 files changed:
postfix/.indent.pro
postfix/HISTORY
postfix/MYSQL_README
postfix/conf/sample-ldap.cf
postfix/html/faq.html
postfix/src/cleanup/cleanup.c
postfix/src/global/mail_params.c
postfix/src/global/mail_params.h
postfix/src/global/mail_version.h
postfix/src/master/Makefile.in
postfix/src/master/mail_flow.c
postfix/src/master/mail_flow.h
postfix/src/master/mail_server.h
postfix/src/master/master_flow.c
postfix/src/master/multi_server.c
postfix/src/master/single_server.c
postfix/src/master/trigger_server.c
postfix/src/nqmgr/qmgr.c
postfix/src/qmgr/qmgr.c
postfix/src/smtpd/smtpd.h
postfix/src/smtpd/smtpd_check.c
postfix/src/smtpd/smtpd_state.c
postfix/src/smtpd/smtpd_token.c
postfix/src/util/Makefile.in
postfix/src/util/cache [new file with mode: 0755]
postfix/src/util/cache.in [new file with mode: 0644]
postfix/src/util/ctable.c [new file with mode: 0644]
postfix/src/util/ctable.h [new file with mode: 0644]
postfix/src/util/ctable.in [new file with mode: 0644]
postfix/src/util/ctable.ref [new file with mode: 0644]
postfix/src/util/dict_ldap.c
postfix/src/util/ring.h

index 23f1f46ce679ad352585cb96b4f2949c11ff2e68..a0d1c27f70dea9dc33aca4fff4567ea9a47edbc1 100644 (file)
@@ -20,6 +20,8 @@
 -TCONFIG_STR_TABLE
 -TCONFIG_TIME_FN_TABLE
 -TCONFIG_TIME_TABLE
+-TCTABLE
+-TCTABLE_ENTRY
 -TDELIVER_ATTR
 -TDELIVER_REQUEST
 -TDICT
index a000faf3e5b101f35d0d67dd99843bebc7acfb4a..3d7064771ded4f2ba58890f7c8c3d306222565ae 100644 (file)
@@ -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.
index faecf80a26313268536abb2df5a6bf4e35827525..2f11bd9566ebb6d5f992777f7d98d86f1c881e40 100644 (file)
@@ -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
index c503df0bc66cee434b9d6ef14a95563f540a27c7..8badb4026091b7a4eb5067ef7e8246b1095b933e 100644 (file)
 #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)
 
 #
 #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
index fc787ee47c917705e0a2d9220ac59a7dcecc6ed5..64ea296ba1f2bc037e7cc66f0b745717838d61fe 100644 (file)
@@ -296,6 +296,9 @@ domains with "relay access denied"</a>
 <li><a href="#domain_mailbox">Receiving a virtual domain in a
 mailbox</a>
 
+<li><a href="#virtual_logging">Postfix logs delivery to virtual
+address with the wrong name</a>
+
 </ul>
 
 <a name="address_rewriting"><h3>Address rewriting</h3>
@@ -2716,6 +2719,44 @@ your system supports, use the command <b>postconf -m</b>.
 
 <hr>
 
+<a name="virtual_logging"><h3>Postfix logs delivery to virtual
+address with the wrong name</h3></a>
+
+When Postfix delivers mail for a virtual address <i>vuser@vdomain.name</i>
+that is aliased to a local user, then Postfix logs the local username
+instead of the virtual one.
+
+<p>
+
+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.
+
+<p>
+
+A workaround exists. It uses regular expressions. This
+can be expensive if you have many virtual domains.
+
+<p>
+<blockquote>
+<pre>
+/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
+</pre>
+</blockquote>
+
+<p>
+
+This delivers the mail as
+<i>localuser+vuser=vdomain.name@your.domain</i>.
+
+<hr>
+
 <a name="masquerade"><h3>Address masquerading with exceptions</h3></a>
 
 For people outside your organization it can be desirable to only
index 9891a03cca7b8b27a7a4b8055a1df834294c37f3..1d457753dab06b8b1b1d542325bc2ea37a693805 100644 (file)
@@ -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);
 }
index ec3d780648d76fe339ab580082eab632e2cfcb63..98da1b2d755c5995e951ba07cf0336dc2aed6816 100644 (file)
@@ -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[] = {
index 7c6744d2de2bccc15bc6c484d2487f05afc2ad0d..45eaecaa4c9a2714b1d2af98d272a9a808085ea7 100644 (file)
@@ -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
index 527fb82959e4823bc6dbd0f80b88c28c4a7e1f95..5b94e2288172bff99c77d14022f0980b865abb14 100644 (file)
@@ -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
index 59f1e3cc866562e06f935709e2980f2378a580ed..db445d72ac5aa45d132e4a325308dcb6577ff96b 100644 (file)
@@ -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
index 603a1f37327511a8eed403a6495cb1bd3bd63b1b..3a42df080a8e7ce6e6d285c6e46441579a97f2c7 100644 (file)
@@ -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);
+}
index c146d83bec43a6d542af5b40873712261452c0e4..9e058598f5d2e6c363075d8b8ded42ae128f1fe9 100644 (file)
@@ -16,6 +16,7 @@
   */
 extern int mail_flow_get(int);
 extern int mail_flow_put(int);
+extern int mail_flow_count(void);
 
 /* LICENSE
 /* .ad
index 6c55603c1546603f565df540ce94d22f1c74c795..47b8cfeeed28a163c9d794ccf4fec1e8c250301b 100644 (file)
@@ -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 **);
index cc13f8cc8c1a17237ecbe4da1fa5c0e14db96829..ed9b492d22b2273e5ffb1cce3e6f3daebd6802d3 100644 (file)
@@ -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);
 }
index e845ca5ff45ab095732ef8a841c5750cbdc991e8..746e7c03310f70da3dd5c4eb47b633fc03b9810d 100644 (file)
@@ -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.
 #include <mail_conf.h>
 #include <timed_ipc.h>
 #include <resolve_local.h>
+#include <mail_flow.h>
 
 /* 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);
        }
index 58cc6f7ce5b7e84e15a553ced66cabb3c277b940..5860fc79823dd046cfc51d241dd4597f10a069cb 100644 (file)
@@ -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);
index 61307e2a4d37a5932b3d4098e8652f25f7ba38e3..62ce674aa2ff1dda5247eaf391b19e73bf3c2b2e 100644 (file)
@@ -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.
 #include <debug_process.h>
 #include <mail_conf.h>
 #include <resolve_local.h>
+#include <mail_flow.h>
 
 /* 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);
        }
index c9f4618774fcc43a70482ad885444368b3f9581a..6185a0be9ccbb52a4e731443873e5d3ce8dbc8e5 100644 (file)
@@ -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 */
index 1c48b118b94258138f32159127720ad6e7794708..a705aadc4ef09ed99296c80107b389d6ef66e14f 100644 (file)
@@ -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);
index 29fb3d2d743dd8b0cee2d23d2bd86d29b1691324..feedc802097b384b64004de9cad35d586d1f745f 100644 (file)
@@ -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;
index 75e7694f41ebaea186aa6b039cf0be1662ec3146..2fc9a6ba9e312d0cd8fd3d3952d81f34e15f23cd 100644 (file)
 #include <mymalloc.h>
 #include <dict.h>
 #include <htable.h>
+#include <ctable.h>
 
 /* 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;
index ca492963487f621014c416b0e4630265033e54a0..c22ebd5c8985969d18136d7cd7ab752214806727 100644 (file)
@@ -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)
index 09d64cd01e502b55af84336671aff0af28e80d5c..66bc6bc9c3841e5359ed46991369902230d96e22 100644 (file)
@@ -196,7 +196,7 @@ int     smtpd_token(char *cp, SMTPD_TOKEN **argvp)
 #include <vstream.h>
 #include <vstring_vstream.h>
 
-main(int unused_argc, char **unused_argv)
+int     main(int unused_argc, char **unused_argv)
 {
     VSTRING *vp = vstring_alloc(10);
     int     tok_argc;
index a2dd61bb24d35ebe70437a886a926fb78ebe758c..d7e85a063d3124ee27eb6a27608e6e618efe0acd 100644 (file)
@@ -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.in 2>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.in >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 (executable)
index 0000000..607a835
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 (file)
index 0000000..89974bd
--- /dev/null
@@ -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 (file)
index 0000000..4e6d18f
--- /dev/null
@@ -0,0 +1,273 @@
+/*++
+/* NAME
+/*     ctable 3
+/* SUMMARY
+/*     cache manager
+/* SYNOPSIS
+/*     #include <ctable.h>
+/*
+/*     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 <sys_defs.h>
+#include <stdlib.h>
+#include <stddef.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <ring.h>
+#include <htable.h>
+#include <ctable.h>
+
+ /*
+  * 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 <unistd.h>
+#include <vstream.h>
+#include <vstring.h>
+#include <vstring_vstream.h>
+#include <msg_vstream.h>
+
+#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 (file)
index 0000000..7e3d899
--- /dev/null
@@ -0,0 +1,39 @@
+#ifndef _CTABLE_H_INCLUDED_
+#define _CTABLE_H_INCLUDED_
+
+/*++
+/* NAME
+/*     ctable 5
+/* SUMMARY
+/*     cache manager
+/* SYNOPSIS
+/*     #include <ctable.h>
+/* 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 (file)
index 0000000..78763cf
--- /dev/null
@@ -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 (file)
index 0000000..34e8a95
--- /dev/null
@@ -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
index 776a437db560b9e6c03c48a99f2b13f9250227ab..5287692489623aac20e09bdee559372055a64b55 100644 (file)
@@ -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);
index a96fe2696af450eefa1394c0788bfdeeeae284d2..d0219cf804cc27b0784661db3e32280ae0b72b04 100644 (file)
@@ -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