]> git.ipfire.org Git - thirdparty/postfix.git/commitdiff
postfix-20010228-pl04 v20010228-pl04
authorWietse Venema <wietse@porcupine.org>
Tue, 31 Jul 2001 05:00:00 +0000 (00:00 -0500)
committerViktor Dukhovni <viktor@dukhovni.org>
Tue, 5 Feb 2013 15:51:31 +0000 (15:51 +0000)
25 files changed:
postfix/HISTORY
postfix/MYSQL_README
postfix/conf/sample-ldap.cf
postfix/html/faq.html
postfix/src/global/mail_version.h
postfix/src/global/own_inet_addr.c
postfix/src/smtp/smtp_connect.c
postfix/src/smtpd/Makefile.in
postfix/src/smtpd/smtpd_acl.in [new file with mode: 0644]
postfix/src/smtpd/smtpd_acl.ref [new file with mode: 0644]
postfix/src/smtpd/smtpd_check.c
postfix/src/smtpd/smtpd_check_access
postfix/src/util/Makefile.in
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/dict_pcre.c
postfix/src/util/dict_regexp.c
postfix/src/util/inet_addr_list.c
postfix/src/util/inet_addr_list.h
postfix/src/util/inet_addr_list.in [new file with mode: 0644]
postfix/src/util/inet_addr_list.ref [new file with mode: 0644]
postfix/src/util/ring.h

index 13a9bcfad5714985c8bda07180760b547c0818dc..ab2d0fbb550ccb711dbcf0a86c1ee970a74d7b6d 100644 (file)
@@ -5063,3 +5063,36 @@ Apologies for any names omitted.
        sending QUIT after process idle timeout while the LMTP
        server had disconnected.  Files:  smtp/smtp_proto.c,
        lmtp/lmtp_proto.c.
+
+20010727
+
+       Bugfix: updated LDAP client module from LaMont Jones, HP.
+       This also introduces new LDAP query filter patterns: %u
+       (address localpart) and %d (domain part). Files:
+       conf/sample-ldap.cf, util/dict_ldap.c.
+
+20010729
+
+       Bugfix: recursive smtpd_whatever_restrictions clobbered
+       intermediate results when switching between sender and
+       recipient address restrictions. Problem found by Victor
+       Duchovni, morganstanley.com. In order to fix, introduced
+       address resolver result caching, which should also help to
+       speed up sender/recipient address restriction processing.
+
+       Bugfix: the not yet announced DUNNO access table lookup
+       result did not prevent lookups with substrings of the same
+       lookup key. Found by Victor Duchovni, morganstanley.com.
+
+20010730
+
+       Robustness: trim trailing whitespace from regexp and pcre
+       right-hand sides, for consistency with DB/DBM tables.
+       Files: util/dict_pcre.c, util/dict_regexp.c.
+
+20010731
+
+       Robustness: eliminate duplicate IP addresses after expansion
+       of hostnames in $inet_interfaces, so that Postfix does not
+       suddenly refuse to start up after someone changes the DNS.
+       Files:  util/inet_addr_list.c global/own_inet_addr.c.
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 a2b8b627a8f1960d45e2153bc8d2c101a11820dc..425304fddb95f205de929e822b8dda37e55e86c3 100644 (file)
@@ -1027,7 +1027,7 @@ Berkeley DB library version.
 
 <hr>
 
-<a name="nosuid"><h1>sendmail has set-uid root file permissions, or is run from a
+<a name="nosuid"><h3>sendmail has set-uid root file permissions, or is run from a
 set-uid root process</h3></a>
 
 Traditionally, the UNIX <b>sendmail</b> command is installed with
index f725dbca0f667b9eaf065164a9cb63f14bba0634..4ccf192f8c4e97c7d8b87ff0d955ab185498765e 100644 (file)
@@ -15,7 +15,7 @@
   * Version of this program.
   */
 #define VAR_MAIL_VERSION       "mail_version"
-#define DEF_MAIL_VERSION       "Postfix-20010228-pl03"
+#define DEF_MAIL_VERSION       "Postfix-20010228-pl04"
 extern char *var_mail_version;
 
 /* LICENSE
index f06232989c641b615553efa78a2a25cda901e5f0..1dc8aaf4fdb427e182c0e4dc0baf59df22782fe2 100644 (file)
@@ -107,6 +107,14 @@ static void own_inet_addr_init(INET_ADDR_LIST *addr_list,
                          VAR_INET_INTERFACES, host);
        myfree(hosts);
 
+       /*
+        * Weed out duplicate IP addresses. Duplicates happen when the same
+        * IP address is listed under multiple hostnames. If we don't weed
+        * out duplicates, Postfix can suddenly stop working after the DNS is
+        * changed.
+        */
+       inet_addr_list_uniq(addr_list);
+
        inet_addr_list_init(&local_addrs);
        inet_addr_list_init(&local_masks);
        if (inet_addr_local(&local_addrs, &local_masks) == 0)
index 6b9c58f6ecb08b1385733fbeb60603816c152347..8c947052017ac76692596c0dd8adc4fd8a22e70e 100644 (file)
@@ -394,7 +394,7 @@ SMTP_SESSION *smtp_connect(char *destination, VSTRING *why)
     char   *save;
     char   *dest;
     char   *cp;
-    int     found_myself;
+    int     found_myself = 0;
 
     /*
      * First try to deliver to the indicated destination, then try to deliver
index 90d9aa96bbc293d78bcc3c958864201b7d2a7e3e..aebfcaeac19b5dce8818e577fac1aa8ea1dd5c72 100644 (file)
@@ -67,7 +67,7 @@ depend: $(MAKES)
        done) | grep -v '[.][o][:][ ][/]' >$$$$ && mv $$$$ Makefile.in
        @$(EXPORT) make -f Makefile.in Makefile 1>&2
 
-tests: smtpd_check_test smtpd_check_test2 smtpd_token_test
+tests: smtpd_check_test smtpd_check_test2 smtpd_acl_test smtpd_token_test
 
 smtpd_check_test: smtpd_check smtpd_check.in smtpd_check.ref
        ../postmap/postmap smtpd_check_access
@@ -81,6 +81,12 @@ smtpd_check_test2: smtpd_check smtpd_check.in2 smtpd_check.ref2
        diff smtpd_check.ref2 smtpd_check.tmp
        rm -f smtpd_check.tmp smtpd_check_access.*
 
+smtpd_acl_test: smtpd_check smtpd_acl.in smtpd_acl.ref
+       ../postmap/postmap smtpd_check_access
+       ./smtpd_check <smtpd_acl.in >smtpd_check.tmp 2>&1
+       diff smtpd_acl.ref smtpd_check.tmp
+       rm -f smtpd_check.tmp smtpd_check_access.*
+
 smtpd_token_test: smtpd_token smtpd_token.in smtpd_token.ref
        ./smtpd_token <smtpd_token.in >smtpd_token.tmp 2>&1
        diff smtpd_token.ref smtpd_token.tmp
@@ -164,6 +170,7 @@ smtpd_check.o: ../../include/mymalloc.h
 smtpd_check.o: ../../include/dict.h
 smtpd_check.o: ../../include/vstream.h
 smtpd_check.o: ../../include/htable.h
+smtpd_check.o: ../../include/ctable.h
 smtpd_check.o: ../../include/dns.h
 smtpd_check.o: ../../include/namadr_list.h
 smtpd_check.o: ../../include/domain_list.h
diff --git a/postfix/src/smtpd/smtpd_acl.in b/postfix/src/smtpd/smtpd_acl.in
new file mode 100644 (file)
index 0000000..e633c48
--- /dev/null
@@ -0,0 +1,107 @@
+#
+# Initialize
+#
+smtpd_delay_reject 0
+mynetworks 127.0.0.0/8,168.100.189.0/28
+relay_domains porcupine.org
+#
+# Test check_domain_access()
+#
+helo_restrictions hash:./smtpd_check_access
+# Expect: REJECT
+helo foo.dunno.com
+# Expect: OK
+helo bar.dunno.com
+# Expect: OK
+helo foo.duuno.com
+#
+# Test check_namadr_access(), domain part
+#
+client_restrictions hash:./smtpd_check_access
+# Expect: REJECT
+client foo.dunno.com 131.155.210.17
+# Expect: OK
+client bar.dunno.com 131.155.210.17
+# Expect: OK
+client bar.dunno.com 131.155.210.19
+#
+# Test check_namadr_access(), address part
+#
+# Expect: OK
+client bar.duno.com 131.155.210.17
+# Expect: REJECT
+client bar.duno.com 131.155.210.19
+# Expect: REJECT
+client bar.duno.com 44.33.22.11
+# Expect: OK
+client bar.duno.com 44.33.22.55
+# Expect: REJECT
+client bar.duno.com 44.33.44.33
+#
+# Test check_mail_access()
+#
+sender_restrictions hash:./smtpd_check_access
+# Expect: REJECT
+mail reject@dunno.domain
+# Expect: OK
+mail ok@dunno.domain
+# Expect: OK
+mail anyone@dunno.domain
+# Expect: OK
+mail bad-sender@dunno.domain
+#
+# Again, with a domain that rejects by default
+#
+# Expect: REJECT  
+mail reject@reject.domain
+# Expect: OK
+mail ok@reject.domain
+# Expect: REJECT
+mail anyone@reject.domain
+# Expect: REJECT
+mail good-sender@reject.domain
+#
+# Again, with a domain that accepts by default
+#
+# Expect: REJECT
+mail reject@ok.domain
+# Expect: OK
+mail ok@ok.domain
+# Expect: OK
+mail anyone@ok.domain
+# Expect: OK
+mail bad-sender@ok.domain
+#
+# Test check_mail_access()
+#
+recipient_restrictions hash:./smtpd_check_access
+# Expect: REJECT
+rcpt reject@dunno.domain
+# Expect: OK
+rcpt ok@dunno.domain
+# Expect: OK
+rcpt anyone@dunno.domain
+# Expect: OK
+rcpt bad-sender@dunno.domain
+#
+# Again, with a domain that rejects by default
+#
+# Expect: REJECT  
+rcpt reject@reject.domain
+# Expect: OK
+rcpt ok@reject.domain
+# Expect: REJECT
+rcpt anyone@reject.domain
+# Expect: REJECT
+rcpt good-sender@reject.domain
+#
+# Again, with a domain that accepts by default
+#
+# Expect: REJECT
+rcpt reject@ok.domain
+# Expect: OK
+rcpt ok@ok.domain
+# Expect: OK
+rcpt anyone@ok.domain
+# Expect: OK
+rcpt bad-sender@ok.domain
diff --git a/postfix/src/smtpd/smtpd_acl.ref b/postfix/src/smtpd/smtpd_acl.ref
new file mode 100644 (file)
index 0000000..34e3518
--- /dev/null
@@ -0,0 +1,164 @@
+>>> #
+>>> # Initialize
+>>> #
+>>> smtpd_delay_reject 0
+OK
+>>> mynetworks 127.0.0.0/8,168.100.189.0/28
+OK
+>>> relay_domains porcupine.org
+OK
+>>> #
+>>> # Test check_domain_access()
+>>> #
+>>> helo_restrictions hash:./smtpd_check_access
+OK
+>>> # Expect: REJECT
+>>> helo foo.dunno.com
+./smtpd_check: reject: HELO from localhost[127.0.0.1]: 554 <foo.dunno.com>: Helo command rejected: Access denied
+554 <foo.dunno.com>: Helo command rejected: Access denied
+>>> # Expect: OK
+>>> helo bar.dunno.com
+OK
+>>> # Expect: OK
+>>> helo foo.duuno.com
+OK
+>>> #
+>>> # Test check_namadr_access(), domain part
+>>> #
+>>> client_restrictions hash:./smtpd_check_access
+OK
+>>> # Expect: REJECT
+>>> client foo.dunno.com 131.155.210.17
+./smtpd_check: reject: CONNECT from foo.dunno.com[131.155.210.17]: 554 <foo.dunno.com[131.155.210.17]>: Client host rejected: Access denied
+554 <foo.dunno.com[131.155.210.17]>: Client host rejected: Access denied
+>>> # Expect: OK
+>>> client bar.dunno.com 131.155.210.17
+OK
+>>> # Expect: OK
+>>> client bar.dunno.com 131.155.210.19
+OK
+>>> #
+>>> # Test check_namadr_access(), address part
+>>> #
+>>> # Expect: OK
+>>> client bar.duno.com 131.155.210.17
+OK
+>>> # Expect: REJECT
+>>> client bar.duno.com 131.155.210.19
+./smtpd_check: reject: CONNECT from bar.duno.com[131.155.210.19]: 554 <bar.duno.com[131.155.210.19]>: Client host rejected: Access denied
+554 <bar.duno.com[131.155.210.19]>: Client host rejected: Access denied
+>>> # Expect: REJECT
+>>> client bar.duno.com 44.33.22.11
+./smtpd_check: reject: CONNECT from bar.duno.com[44.33.22.11]: 554 <bar.duno.com[44.33.22.11]>: Client host rejected: Access denied
+554 <bar.duno.com[44.33.22.11]>: Client host rejected: Access denied
+>>> # Expect: OK
+>>> client bar.duno.com 44.33.22.55
+OK
+>>> # Expect: REJECT
+>>> client bar.duno.com 44.33.44.33
+./smtpd_check: reject: CONNECT from bar.duno.com[44.33.44.33]: 554 <bar.duno.com[44.33.44.33]>: Client host rejected: Access denied
+554 <bar.duno.com[44.33.44.33]>: Client host rejected: Access denied
+>>> #
+>>> # Test check_mail_access()
+>>> #
+>>> sender_restrictions hash:./smtpd_check_access
+OK
+>>> # Expect: REJECT
+>>> mail reject@dunno.domain
+./smtpd_check: reject: MAIL from bar.duno.com[44.33.44.33]: 554 <reject@dunno.domain>: Sender address rejected: Access denied; from=<reject@dunno.domain>
+554 <reject@dunno.domain>: Sender address rejected: Access denied
+>>> # Expect: OK
+>>> mail ok@dunno.domain
+OK
+>>> # Expect: OK
+>>> mail anyone@dunno.domain
+OK
+>>> # Expect: OK
+>>> mail bad-sender@dunno.domain
+OK
+>>> #
+>>> # Again, with a domain that rejects by default
+>>> #
+>>> # Expect: REJECT  
+>>> mail reject@reject.domain
+./smtpd_check: reject: MAIL from bar.duno.com[44.33.44.33]: 554 <reject@reject.domain>: Sender address rejected: Access denied; from=<reject@reject.domain>
+554 <reject@reject.domain>: Sender address rejected: Access denied
+>>> # Expect: OK
+>>> mail ok@reject.domain
+OK
+>>> # Expect: REJECT
+>>> mail anyone@reject.domain
+./smtpd_check: reject: MAIL from bar.duno.com[44.33.44.33]: 554 <anyone@reject.domain>: Sender address rejected: Access denied; from=<anyone@reject.domain>
+554 <anyone@reject.domain>: Sender address rejected: Access denied
+>>> # Expect: REJECT
+>>> mail good-sender@reject.domain
+./smtpd_check: reject: MAIL from bar.duno.com[44.33.44.33]: 554 <good-sender@reject.domain>: Sender address rejected: Access denied; from=<good-sender@reject.domain>
+554 <good-sender@reject.domain>: Sender address rejected: Access denied
+>>> #
+>>> # Again, with a domain that accepts by default
+>>> #
+>>> # Expect: REJECT
+>>> mail reject@ok.domain
+./smtpd_check: reject: MAIL from bar.duno.com[44.33.44.33]: 554 <reject@ok.domain>: Sender address rejected: Access denied; from=<reject@ok.domain>
+554 <reject@ok.domain>: Sender address rejected: Access denied
+>>> # Expect: OK
+>>> mail ok@ok.domain
+OK
+>>> # Expect: OK
+>>> mail anyone@ok.domain
+OK
+>>> # Expect: OK
+>>> mail bad-sender@ok.domain
+OK
+>>> #
+>>> # Test check_mail_access()
+>>> #
+>>> recipient_restrictions hash:./smtpd_check_access
+OK
+>>> # Expect: REJECT
+>>> rcpt reject@dunno.domain
+./smtpd_check: reject: RCPT from bar.duno.com[44.33.44.33]: 554 <reject@dunno.domain>: Recipient address rejected: Access denied; from=<bad-sender@ok.domain> to=<reject@dunno.domain>
+554 <reject@dunno.domain>: Recipient address rejected: Access denied
+>>> # Expect: OK
+>>> rcpt ok@dunno.domain
+OK
+>>> # Expect: OK
+>>> rcpt anyone@dunno.domain
+OK
+>>> # Expect: OK
+>>> rcpt bad-sender@dunno.domain
+OK
+>>> #
+>>> # Again, with a domain that rejects by default
+>>> #
+>>> # Expect: REJECT  
+>>> rcpt reject@reject.domain
+./smtpd_check: reject: RCPT from bar.duno.com[44.33.44.33]: 554 <reject@reject.domain>: Recipient address rejected: Access denied; from=<bad-sender@ok.domain> to=<reject@reject.domain>
+554 <reject@reject.domain>: Recipient address rejected: Access denied
+>>> # Expect: OK
+>>> rcpt ok@reject.domain
+OK
+>>> # Expect: REJECT
+>>> rcpt anyone@reject.domain
+./smtpd_check: reject: RCPT from bar.duno.com[44.33.44.33]: 554 <anyone@reject.domain>: Recipient address rejected: Access denied; from=<bad-sender@ok.domain> to=<anyone@reject.domain>
+554 <anyone@reject.domain>: Recipient address rejected: Access denied
+>>> # Expect: REJECT
+>>> rcpt good-sender@reject.domain
+./smtpd_check: reject: RCPT from bar.duno.com[44.33.44.33]: 554 <good-sender@reject.domain>: Recipient address rejected: Access denied; from=<bad-sender@ok.domain> to=<good-sender@reject.domain>
+554 <good-sender@reject.domain>: Recipient address rejected: Access denied
+>>> #
+>>> # Again, with a domain that accepts by default
+>>> #
+>>> # Expect: REJECT
+>>> rcpt reject@ok.domain
+./smtpd_check: reject: RCPT from bar.duno.com[44.33.44.33]: 554 <reject@ok.domain>: Recipient address rejected: Access denied; from=<bad-sender@ok.domain> to=<reject@ok.domain>
+554 <reject@ok.domain>: Recipient address rejected: Access denied
+>>> # Expect: OK
+>>> rcpt ok@ok.domain
+OK
+>>> # Expect: OK
+>>> rcpt anyone@ok.domain
+OK
+>>> # Expect: OK
+>>> rcpt bad-sender@ok.domain
+OK
index 5f9596d3d585e5c98cf1347e60a2e7f32fc62d15..a8ed776f6ea9ad6722c2de0c0f1b6f1f730bcbcd 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.
@@ -345,7 +345,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.
@@ -360,6 +360,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 */
 
@@ -471,13 +514,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.
@@ -620,8 +666,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;
 
@@ -816,8 +864,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;
@@ -873,7 +921,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);
@@ -881,14 +930,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;
 
@@ -904,7 +952,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);
 
     /*
@@ -963,7 +1011,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;
@@ -1007,7 +1055,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;
@@ -1019,15 +1068,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)
@@ -1041,7 +1089,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);
 
     /*
@@ -1150,11 +1198,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);
@@ -1162,14 +1211,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)
@@ -1189,10 +1236,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;
@@ -1287,17 +1335,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);
@@ -1308,19 +1359,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));
@@ -1335,7 +1387,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);
@@ -1344,7 +1396,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);
        }
@@ -1352,15 +1404,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;
@@ -1373,6 +1426,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)
@@ -1380,24 +1435,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;
@@ -1410,16 +1467,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);
 
     /*
@@ -1430,12 +1487,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;
 
@@ -1445,42 +1505,42 @@ 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)
+    if (status != 0 || *found)
        return (status);
 
     /*
@@ -1573,7 +1633,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)
 {
 
     /*
@@ -1596,13 +1656,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);
@@ -1649,7 +1712,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);
@@ -1661,7 +1724,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) {
@@ -1705,7 +1768,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)
@@ -1727,7 +1790,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)
@@ -1767,7 +1830,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);
        }
 
@@ -2002,7 +2065,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;
 
     /*
@@ -2029,14 +2093,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] == '[')
@@ -2051,10 +2114,10 @@ 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(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(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));
@@ -2067,11 +2130,11 @@ 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(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(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));
@@ -2336,11 +2399,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;
@@ -2383,7 +2454,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");
@@ -2405,7 +2476,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 f7f6e42d7fc34b79c01b1f9482dc557c17a1241c..afb5487c965698565922f815c5d036113962fdfb 100644 (file)
@@ -2,9 +2,30 @@ bad.domain             554 match bad.domain
 friend.bad.domain      OK
 bad-sender@            554 match bad-sender@
 bad-sender@good.domain OK
+good-sender@           OK
 131.155.210            554 match 131.155.210
 131.155.210.17         OK
+131.155.210.19         REJECT
 reject@this.address    554 match reject@this.address
 open_user@some.site    open
 strict_user@some.site  strict
 auth_client            123456
+
+dunno.com              dunno
+foo.dunno.com          reject
+
+44.33.22               dunno
+44.33.22.11            REJECT
+44.33                  REJECT
+
+reject@dunno.domain    REJECT
+ok@dunno.domain                OK
+dunno.domain           DUNNO
+
+reject@reject.domain   REJECT
+ok@reject.domain       OK
+reject.domain          REJECT
+
+reject@ok.domain       REJECT
+ok@ok.domain           OK
+ok.domain              OK
index f792599c2162be659fe363be634212d1696d429f..0f9afce80b15a00de45ae361c5ef99d09c2902c6 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
+       sane_socketpair.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
+       sane_socketpair.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
+       sane_time.h sane_socketpair.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,8 @@ 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 \
+       inet_addr_list
 
 LIB_DIR        = ../../lib
 INC_DIR        = ../../include
@@ -274,6 +275,16 @@ 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
+
+inet_addr_list: $(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 +297,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 ctable_test inet_addr_list_test
 
 valid_hostname_test: valid_hostname valid_hostname.in valid_hostname.ref
        ./valid_hostname <valid_hostname.in 2>valid_hostname.tmp
@@ -309,6 +320,16 @@ 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
+
+inet_addr_list_test: inet_addr_list
+       ./inet_addr_list `cat inet_addr_list.in` >inet_addr_list.tmp 2>&1
+       diff inet_addr_list.ref inet_addr_list.tmp
+       rm -f inet_addr_list.tmp
+
 DB_TYPE        = `../postconf/postconf -h default_database_type`
 
 dict_test: dict_open testdb dict_test.in dict_test.ref
@@ -368,6 +389,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/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 1e8e84b6bf5a33ff28fd5bcafd25e38172bcd74c..6d3c6f5239321630f2aafc7e05b23bd22577f5a4 100644 (file)
@@ -266,6 +266,7 @@ DICT   *dict_pcre_open(const char *map, int unused_flags, int dict_flags)
            continue;
 
        p = vstring_str(line_buffer);
+       trimblanks(p, 0)[0] = 0;                /* Trim space at end */
        re_delimiter = *p++;
        regexp = p;
 
index 0d4eaf852352693c00d706117c6050e3cbc5f5ef..1debbe96ff2c99cbaa620b52413f5e0a3f628215 100644 (file)
@@ -365,6 +365,8 @@ DICT   *dict_regexp_open(const char *map, int unused_flags, int dict_flags)
        if (*p == 0)                            /* Skip blank lines */
            continue;
 
+       trimblanks(p, 0)[0] = 0;                /* Trim space at end */
+
        rule = dict_regexp_parseline(lineno, p, &nsub, map_fp);
        if (rule) {
            if (nsub > max_nsub)
index d449e76c77db7122cf9e65e24e1f3f8eee6b2dd9..fa1cbd3990087202767b87488c6bf0d1977d738a 100644 (file)
@@ -13,6 +13,9 @@
 /*     INET_ADDR_LIST *list;
 /*     struct in_addr *addr;
 /*
+/*     void    inet_addr_list_uniq(list)
+/*     INET_ADDR_LIST *list;
+/*
 /*     void    inet_addr_list_free(list)
 /*     INET_ADDR_LIST *list;
 /* DESCRIPTION
@@ -25,6 +28,9 @@
 /*     inet_addr_list_append() appends the specified address to
 /*     the specified list, extending the list on the fly.
 /*
+/*     inet_addr_list_uniq() sorts the specified address list and
+/*     eliminates duplicates.
+/*
 /*     inet_addr_list_free() reclaims memory used for the
 /*     specified address list.
 /* LICENSE
@@ -43,6 +49,7 @@
 #include <sys_defs.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
+#include <stdlib.h>
 
 /* Utility library. */
 
@@ -77,9 +84,81 @@ void    inet_addr_list_append(INET_ADDR_LIST *list, struct in_addr * addr)
     list->addrs[list->used++] = *addr;
 }
 
+/* inet_addr_list_comp - compare addresses */
+
+static int inet_addr_list_comp(const void *a, const void *b)
+{
+    const struct in_addr *a_addr = (const struct in_addr *) a;
+    const struct in_addr *b_addr = (const struct in_addr *) b;
+
+    return (a_addr->s_addr - b_addr->s_addr);
+}
+
+/* inet_addr_list_uniq - weed out duplicates */
+
+void    inet_addr_list_uniq(INET_ADDR_LIST *list)
+{
+    int     n;
+    int     m;
+
+    /*
+     * Put the identical members right next to each other.
+     */
+    qsort((void *) list->addrs, list->used,
+         sizeof(list->addrs[0]), inet_addr_list_comp);
+
+    /*
+     * Nuke the duplicates. Postcondition after while loop: m is the largest
+     * index for which list->addrs[n] == list->addrs[m].
+     */
+    for (m = n = 0; m < list->used; m++, n++) {
+       if (m != n)
+           list->addrs[n] = list->addrs[m];
+       while (m + 1 < list->used
+              && inet_addr_list_comp((void *) &(list->addrs[n]),
+                                     (void *) &(list->addrs[m + 1])) == 0)
+           m += 1;
+    }
+    list->used = n;
+}
+
 /* inet_addr_list_free - destroy internet address list */
 
 void    inet_addr_list_free(INET_ADDR_LIST *list)
 {
     myfree((char *) list->addrs);
 }
+
+#ifdef TEST
+
+ /*
+  * Duplicate elimination needs to be tested.
+  */
+#include <inet_addr_host.h>
+
+static void inet_addr_list_print(INET_ADDR_LIST *list)
+{
+    int     n;
+
+    for (n = 0; n < list->used; n++)
+       msg_info("%s", inet_ntoa(list->addrs[n]));
+}
+
+int     main(int argc, char **argv)
+{
+    INET_ADDR_LIST list;
+
+    inet_addr_list_init(&list);
+    while (--argc && *++argv)
+       if (inet_addr_host(&list, *argv) == 0)
+           msg_fatal("host not found: %s", *argv);
+    msg_info("list before sort/uniq");
+    inet_addr_list_print(&list);
+    inet_addr_list_uniq(&list);
+    msg_info("list after sort/uniq");
+    inet_addr_list_print(&list);
+    inet_addr_list_free(&list);
+    return (0);
+}
+
+#endif
index 48a4c9712d51a14b9f4a52bc346d1a4897611836..372b3ccb76adc7fc15915a772cad9335bcf5989b 100644 (file)
@@ -27,6 +27,7 @@ typedef struct INET_ADDR_LIST {
 
 extern void inet_addr_list_init(INET_ADDR_LIST *);
 extern void inet_addr_list_free(INET_ADDR_LIST *);
+extern void inet_addr_list_uniq(INET_ADDR_LIST *);
 extern void inet_addr_list_append(INET_ADDR_LIST *, struct in_addr *);
 
 /* LICENSE
diff --git a/postfix/src/util/inet_addr_list.in b/postfix/src/util/inet_addr_list.in
new file mode 100644 (file)
index 0000000..742281f
--- /dev/null
@@ -0,0 +1,9 @@
+168.100.189.2
+168.100.189.2
+168.100.189.1
+168.100.189.3
+168.100.189.3
+168.100.189.3
+168.100.189.4
+168.100.189.1
+168.100.189.4
diff --git a/postfix/src/util/inet_addr_list.ref b/postfix/src/util/inet_addr_list.ref
new file mode 100644 (file)
index 0000000..0a8a56e
--- /dev/null
@@ -0,0 +1,15 @@
+unknown: list before sort/uniq
+unknown: 168.100.189.2
+unknown: 168.100.189.2
+unknown: 168.100.189.1
+unknown: 168.100.189.3
+unknown: 168.100.189.3
+unknown: 168.100.189.3
+unknown: 168.100.189.4
+unknown: 168.100.189.1
+unknown: 168.100.189.4
+unknown: list after sort/uniq
+unknown: 168.100.189.1
+unknown: 168.100.189.2
+unknown: 168.100.189.3
+unknown: 168.100.189.4
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