]> git.ipfire.org Git - thirdparty/postfix.git/commitdiff
postfix-2.2-20040628
authorWietse Venema <wietse@porcupine.org>
Mon, 28 Jun 2004 05:00:00 +0000 (00:00 -0500)
committerViktor Dukhovni <viktor@dukhovni.org>
Tue, 5 Feb 2013 06:29:42 +0000 (06:29 +0000)
15 files changed:
postfix/HISTORY
postfix/README_FILES/BACKSCATTER_README
postfix/html/BACKSCATTER_README.html
postfix/html/local.8.html
postfix/html/postconf.5.html
postfix/man/man5/postconf.5
postfix/man/man8/local.8
postfix/proto/BACKSCATTER_README.html
postfix/proto/postconf.proto
postfix/src/global/mail_version.h
postfix/src/local/local.c
postfix/src/local/maildir.c
postfix/src/oqmgr/qmgr_message.c
postfix/src/qmgr/qmgr_message.c
postfix/src/virtual/maildir.c

index 9c442ceed1b1dc936c89753d404d8fdc2b3e6881..5d1e4644d0d1d4588cc97133af63c060fde31b46 100644 (file)
@@ -9422,14 +9422,14 @@ Apologies for any names omitted.
        explicit "multiple sessions per delivery request" model.
        This uncovered ESMTP and SASL missing re-initialization
        problems that were fixed in past week.  Design by Victor
-       and Wietse, initial implementation by Victor Duchovny.
+       and Wietse, initial implementation by Victor Duchovni.
 
 20040620
 
        Future proofing: after the reorganization of SMTP request
        state and session state, added code to the smtp client
        error handling routines to more consistently deal with the
-       possibility that session information is not be available.
+       possibility that session information is not available.
 
 20040621
 
@@ -9448,6 +9448,19 @@ Apologies for any names omitted.
        Support for external command execution directory. Files:
        global/pipe_command.[hc].
 
+20040622
+
+       Safety: when mail is delivered to a transport with per-delivery
+       recipient limit of 1, split the recipient address on the
+       recipient delimiter if one is defined, so that extended
+       addresses don't get extra delivery concurrency slots.
+       Files: *qmgr/qmgr_message.c.
+
+20040623
+
+       Workaround for fragile clients: add microsecond time to
+       maildir filename.  Files: virtual/maildir.c, local/maildir.c.
+
 Open problems:
 
        Low: make sure CCARGS -I options come at the end.
index 86e8aa8e19ca74c41c0b1e6240860ae2af1dd968..bee27caf9f9909736d509ec09ad5adaf84e25d27 100644 (file)
@@ -53,8 +53,9 @@ Thus, if returned mail has a Received: message header like this:
 
     Received: from porcupine.org ...
 
-Then I know that this is almost certainly forged mail. Mail that is really sent
-by my systems looks like this:
+Then I know that this is almost certainly forged mail (almost; see next section
+for the fly in the ointment). Mail that is really sent by my systems looks like
+this:
 
     Received: from hostname.porcupine.org ...
 
@@ -66,6 +67,13 @@ result of forgery:
     Received: from host.example.com (HELO porcupine.org) ...
     Received: from host.example.com (EHLO porcupine.org) ...
 
+Another frequent sign of forgery is the Message-ID: header. My systems produce
+a Message-ID: of <stuff@hostname.porcupine.org>. The following are forgeries,
+especially the first one:
+
+    Message-ID: <1cb479435d8eb9.2beb1.qmail@porcupine.org>
+    Message-ID: <yulszqocfzsficvzzju@porcupine.org>
+
 To block such backscatter I use header_checks and body_checks patterns like
 this:
 
@@ -79,6 +87,8 @@ this:
         /^Received: +from +[^ ]+ +\(([^ ]+ +[he]+lo=|[he]+lo +)
     (porcupine\.org)\)/
             reject forged client name in Received: header: $2
+        /^Message-ID:.*@(porcupine\.org)/
+       reject forged domain name in Message-ID: header: $1
 
     /etc/postfix/body_checks:
         /^[> ]*Received: +from +(porcupine\.org) /
@@ -86,6 +96,8 @@ this:
         /^[> ]*Received: +from +[^ ]+ +\(([^ ]+ +[he]+lo=|[he]+lo +)
     (porcupine\.org)\)/
             reject forged client name in Received: header: $2
+        /^[> ]*Message-ID:.*@(porcupine\.org)/
+       reject forged domain name in Message-ID: header: $1
 
 Notes:
 
@@ -135,11 +147,11 @@ mail is obviously forged and is very easy to stop.
 
     /etc/postfix/header_checks:
         /^(From|Return-Path):.*[[:<:]](user@domain\.tld)[[:>:]]/
-            reject forged sender address in $1: message header: $2
+            reject forged sender address in $1: header: $2
 
     /etc/postfix/body_checks:
         /^[> ]*(From|Return-Path):.*[[:<:]](user@domain\.tld)[[:>:]]/
-            reject forged sender address in $1: message header: $2
+            reject forged sender address in $1: header: $2
 
 Notes:
 
index f2889feaf4b59e0a52440df62a45422b970fa187..0af36f39826bd37a83a85a982e783bc255bdd9d2 100644 (file)
@@ -104,8 +104,9 @@ Received: from porcupine.org ...
 </pre>
 </blockquote>
 
-<p> Then I know that this is almost certainly forged mail.  Mail
-that is really sent by my systems looks like this:  </p>
+<p> Then I know that this is almost certainly forged mail (almost;
+see next section for the fly in the ointment). Mail that is really
+sent by my systems looks like this: </p>
 
 <blockquote>
 <pre>
@@ -125,6 +126,18 @@ Received: from host.example.com (EHLO porcupine.org) ...
 </pre>
 </blockquote>
 
+<p> Another frequent sign of forgery is the Message-ID: header. My
+systems produce a Message-ID: of
+&lt;<i>stuff</i>@<i>hostname</i>.porcupine.org&gt;.  The following
+are forgeries, especially the first one:
+
+<blockquote>
+<pre>
+Message-ID: &lt;1cb479435d8eb9.2beb1.qmail@porcupine.org&gt;
+Message-ID: &lt;yulszqocfzsficvzzju@porcupine.org&gt;
+</pre>
+</blockquote>
+
 <p> To block such backscatter I use <a href="postconf.5.html#header_checks">header_checks</a> and <a href="postconf.5.html#body_checks">body_checks</a>
 patterns like this: </p>
 
@@ -139,12 +152,16 @@ patterns like this: </p>
         reject forged client name in Received: header: $1
     /^Received: +from +[^ ]+ +\(([^ ]+ +[he]+lo=|[he]+lo +)(porcupine\.org)\)/
         reject forged client name in Received: header: $2
+    /^Message-ID:.*@(porcupine\.org)/
+       reject forged domain name in Message-ID: header: $1
 
 /etc/postfix/body_checks:
     /^[&gt; ]*Received: +from +(porcupine\.org) /
         reject forged client name in Received: header: $1
     /^[&gt; ]*Received: +from +[^ ]+ +\(([^ ]+ +[he]+lo=|[he]+lo +)(porcupine\.org)\)/
         reject forged client name in Received: header: $2
+    /^[&gt; ]*Message-ID:.*@(porcupine\.org)/
+       reject forged domain name in Message-ID: header: $1
 </pre>
 </blockquote>
 
@@ -213,11 +230,11 @@ and is very easy to stop.
 
 /etc/postfix/header_checks:
     /^(From|Return-Path):.*[[:&lt;:]](user@domain\.tld)[[:&gt;:]]/ 
-        reject forged sender address in $1: message header: $2
+        reject forged sender address in $1: header: $2
 
 /etc/postfix/body_checks:
     /^[&gt; ]*(From|Return-Path):.*[[:&lt;:]](user@domain\.tld)[[:&gt;:]]/ 
-        reject forged sender address in $1: message header: $2
+        reject forged sender address in $1: header: $2
 </pre>
 </blockquote>
 
index 0ca93411d7bdf1a05279192ca80913f1caa4c03f..aaf1769c9cf38f8b854a1d61ebbcb003338b7e1e 100644 (file)
@@ -49,8 +49,8 @@ LOCAL(8)                                                 LOCAL(8)
        <b>$user</b>  (recipient  username), <b>$home</b> (recipient home direc-
        tory),  <b>$shell</b>  (recipient  shell),  <b>$recipient</b>  (complete
        recipient  address),  <b>$extension</b> (recipient address exten-
-       sion), <b>$domain</b> (recipient domain), <b>local</b> (entire recipient
-       address  localpart)  and  <b>$<a href="postconf.5.html#recipient_delimiter">recipient_delimiter</a>.</b>  The forms
+       sion), <b>$domain</b> (recipient domain), <b>$local</b> (entire  recipi-
+       ent address localpart) and <b>$<a href="postconf.5.html#recipient_delimiter">recipient_delimiter</a>.</b> The forms
        <i>${name?value}</i> and <i>${name:value}</i>  expand  conditionally  to
        <i>value</i> when <i>$name</i> is (is not) defined.  Characters that may
        have special meaning to  the  shell  or  file  system  are
@@ -154,7 +154,7 @@ LOCAL(8)                                                 LOCAL(8)
        (recipient  home  directory),  <b>$shell</b>  (recipient  shell),
        <b>$recipient</b>  (complete   recipient   address),   <b>$extension</b>
        (recipient address extension), <b>$domain</b> (recipient domain),
-       <b>local</b> (entire recipient address  localpart)  and  <b>$recipi-</b>
+       <b>$local</b> (entire recipient address localpart)  and  <b>$recipi-</b>
        <b>ent_delimiter.</b>   The forms <i>${name?value}</i> and <i>${name:value}</i>
        expand conditionally to  <i>value</i>  when  <i>$name</i>  is  (is  not)
        defined.   Characters that may have special meaning to the
index e91f7492462ff322dc8668b23cd56ee70443f9bc..3edff6d40a10927bbd830a314b0dd14dbef22662 100644 (file)
@@ -607,6 +607,11 @@ NOTE: if mail to the BCC address bounces it will be returned to
 the sender.
 </p>
 
+<p> NOTE: automatic BCC recipients are produced only for new mail.
+To avoid mailer loops, automatic BCC recipients are not generated
+for mail that Postfix forwards internally, nor for mail that Postfix
+generates itself. </p>
+
 
 </DD>
 
@@ -4539,6 +4544,11 @@ NOTE: if mail to the BCC address bounces it will be returned to
 the sender.
 </p>
 
+<p> NOTE: automatic BCC recipients are produced only for new mail.
+To avoid mailer loops, automatic BCC recipients are not generated
+for mail that Postfix forwards internally, nor for mail that Postfix
+generates itself. </p>
+
 <p>
 Example:
 </p>
@@ -4948,6 +4958,11 @@ NOTE: if mail to the BCC address bounces it will be returned to
 the sender.
 </p>
 
+<p> NOTE: automatic BCC recipients are produced only for new mail.
+To avoid mailer loops, automatic BCC recipients are not generated
+for mail that Postfix forwards internally, nor for mail that Postfix
+generates itself. </p>
+
 <p>
 Example:
 </p>
index a60c2b9b1bc4149641443dc1e1985a69e06ce468..c473165cd918110bf5694fe8195578ab45478358 100644 (file)
@@ -326,6 +326,11 @@ that is received by the Postfix mail system.
 .PP
 NOTE: if mail to the BCC address bounces it will be returned to
 the sender.
+.PP
+NOTE: automatic BCC recipients are produced only for new mail.
+To avoid mailer loops, automatic BCC recipients are not generated
+for mail that Postfix forwards internally, nor for mail that Postfix
+generates itself.
 .SH anvil_rate_time_unit (default: 60s)
 The time unit over which client connection rates and other rates
 are calculated.
@@ -2328,6 +2333,11 @@ run "\fBpostmap /etc/postfix/recipient_bcc\fR".
 NOTE: if mail to the BCC address bounces it will be returned to
 the sender.
 .PP
+NOTE: automatic BCC recipients are produced only for new mail.
+To avoid mailer loops, automatic BCC recipients are not generated
+for mail that Postfix forwards internally, nor for mail that Postfix
+generates itself.
+.PP
 Example:
 .PP
 .nf
@@ -2579,6 +2589,11 @@ run "\fBpostmap /etc/postfix/sender_bcc\fR".
 NOTE: if mail to the BCC address bounces it will be returned to
 the sender.
 .PP
+NOTE: automatic BCC recipients are produced only for new mail.
+To avoid mailer loops, automatic BCC recipients are not generated
+for mail that Postfix forwards internally, nor for mail that Postfix
+generates itself.
+.PP
 Example:
 .PP
 .nf
index a7c20e36432515bd29925830bf16f2d1db4c0ada..6d56a772790ea9240d395e1bf01aaa8836f59dd2 100644 (file)
@@ -51,7 +51,7 @@ The \fBforward_path\fR parameter is subject to interpolation of
 \fB$user\fR (recipient username), \fB$home\fR (recipient home
 directory), \fB$shell\fR (recipient shell), \fB$recipient\fR
 (complete recipient address), \fB$extension\fR (recipient address
-extension), \fB$domain\fR (recipient domain), \fBlocal\fR
+extension), \fB$domain\fR (recipient domain), \fB$local\fR
 (entire recipient address localpart) and
 \fB$recipient_delimiter.\fR The forms \fI${name?value}\fR and
 \fI${name:value}\fR expand conditionally to \fIvalue\fR when
@@ -165,7 +165,7 @@ to interpolation of \fB$user\fR (recipient username),
 \fB$home\fR (recipient home directory), \fB$shell\fR
 (recipient shell), \fB$recipient\fR (complete recipient
 address), \fB$extension\fR (recipient address extension),
-\fB$domain\fR (recipient domain), \fBlocal\fR (entire
+\fB$domain\fR (recipient domain), \fB$local\fR (entire
 recipient address localpart) and \fB$recipient_delimiter.\fR
 The forms \fI${name?value}\fR and \fI${name:value}\fR expand
 conditionally to \fIvalue\fR when \fI$name\fR is (is not)
index 069bf60e1b0e501c31ca4f9ef70b703a8c51263f..258d85fde89b094092367a54568ec81d7e4027c2 100644 (file)
@@ -104,8 +104,9 @@ Received: from porcupine.org ...
 </pre>
 </blockquote>
 
-<p> Then I know that this is almost certainly forged mail.  Mail
-that is really sent by my systems looks like this:  </p>
+<p> Then I know that this is almost certainly forged mail (almost;
+see next section for the fly in the ointment). Mail that is really
+sent by my systems looks like this: </p>
 
 <blockquote>
 <pre>
@@ -125,6 +126,18 @@ Received: from host.example.com (EHLO porcupine.org) ...
 </pre>
 </blockquote>
 
+<p> Another frequent sign of forgery is the Message-ID: header. My
+systems produce a Message-ID: of
+&lt;<i>stuff</i>@<i>hostname</i>.porcupine.org&gt;.  The following
+are forgeries, especially the first one:
+
+<blockquote>
+<pre>
+Message-ID: &lt;1cb479435d8eb9.2beb1.qmail@porcupine.org&gt;
+Message-ID: &lt;yulszqocfzsficvzzju@porcupine.org&gt;
+</pre>
+</blockquote>
+
 <p> To block such backscatter I use header_checks and body_checks
 patterns like this: </p>
 
@@ -139,12 +152,16 @@ patterns like this: </p>
         reject forged client name in Received: header: $1
     /^Received: +from +[^ ]+ +\(([^ ]+ +[he]+lo=|[he]+lo +)(porcupine\.org)\)/
         reject forged client name in Received: header: $2
+    /^Message-ID:.*@(porcupine\.org)/
+       reject forged domain name in Message-ID: header: $1
 
 /etc/postfix/body_checks:
     /^[&gt; ]*Received: +from +(porcupine\.org) /
         reject forged client name in Received: header: $1
     /^[&gt; ]*Received: +from +[^ ]+ +\(([^ ]+ +[he]+lo=|[he]+lo +)(porcupine\.org)\)/
         reject forged client name in Received: header: $2
+    /^[&gt; ]*Message-ID:.*@(porcupine\.org)/
+       reject forged domain name in Message-ID: header: $1
 </pre>
 </blockquote>
 
@@ -213,11 +230,11 @@ and is very easy to stop.
 
 /etc/postfix/header_checks:
     /^(From|Return-Path):.*[[:&lt;:]](user@domain\.tld)[[:&gt;:]]/ 
-        reject forged sender address in $1: message header: $2
+        reject forged sender address in $1: header: $2
 
 /etc/postfix/body_checks:
     /^[&gt; ]*(From|Return-Path):.*[[:&lt;:]](user@domain\.tld)[[:&gt;:]]/ 
-        reject forged sender address in $1: message header: $2
+        reject forged sender address in $1: header: $2
 </pre>
 </blockquote>
 
index 80dfd2a39e4179f309d31f377828c3600eee8c95..a71bccf556c5e4619e30bf68c5cb36a185909271 100644 (file)
@@ -535,6 +535,11 @@ NOTE: if mail to the BCC address bounces it will be returned to
 the sender.
 </p>
 
+<p> NOTE: automatic BCC recipients are produced only for new mail.
+To avoid mailer loops, automatic BCC recipients are not generated
+for mail that Postfix forwards internally, nor for mail that Postfix
+generates itself. </p>
+
 %PARAM berkeley_db_create_buffer_size 16777216
 
 <p>
@@ -2827,6 +2832,11 @@ NOTE: if mail to the BCC address bounces it will be returned to
 the sender.
 </p>
 
+<p> NOTE: automatic BCC recipients are produced only for new mail.
+To avoid mailer loops, automatic BCC recipients are not generated
+for mail that Postfix forwards internally, nor for mail that Postfix
+generates itself. </p>
+
 <p>
 Example:
 </p>
@@ -3094,6 +3104,11 @@ NOTE: if mail to the BCC address bounces it will be returned to
 the sender.
 </p>
 
+<p> NOTE: automatic BCC recipients are produced only for new mail.
+To avoid mailer loops, automatic BCC recipients are not generated
+for mail that Postfix forwards internally, nor for mail that Postfix
+generates itself. </p>
+
 <p>
 Example:
 </p>
index 23b74218431f433e1bee106ec5bc53a12d5d0b2f..0ab9ea1e9478ddac02de7ef6bf66e446944c896c 100644 (file)
@@ -20,7 +20,7 @@
   * Patches change the patchlevel and the release date. Snapshots change the
   * release date only.
   */
-#define MAIL_RELEASE_DATE      "20040621"
+#define MAIL_RELEASE_DATE      "20040628"
 #define MAIL_VERSION_NUMBER    "2.2"
 
 #define VAR_MAIL_VERSION       "mail_version"
index 4fb88a99d2227552eb5dd8e903a8369b1768bc50..3cc7dc01165550ffca10b100a6b9b12bf9975a43 100644 (file)
@@ -43,7 +43,7 @@
 /*     \fB$user\fR (recipient username), \fB$home\fR (recipient home
 /*     directory), \fB$shell\fR (recipient shell), \fB$recipient\fR
 /*     (complete recipient address), \fB$extension\fR (recipient address
-/*     extension), \fB$domain\fR (recipient domain), \fBlocal\fR
+/*     extension), \fB$domain\fR (recipient domain), \fB$local\fR
 /*     (entire recipient address localpart) and
 /*     \fB$recipient_delimiter.\fR The forms \fI${name?value}\fR and
 /*     \fI${name:value}\fR expand conditionally to \fIvalue\fR when
 /*     \fB$home\fR (recipient home directory), \fB$shell\fR
 /*     (recipient shell), \fB$recipient\fR (complete recipient
 /*     address), \fB$extension\fR (recipient address extension),
-/*     \fB$domain\fR (recipient domain), \fBlocal\fR (entire
+/*     \fB$domain\fR (recipient domain), \fB$local\fR (entire
 /*     recipient address localpart) and \fB$recipient_delimiter.\fR
 /*     The forms \fI${name?value}\fR and \fI${name:value}\fR expand
 /*     conditionally to \fIvalue\fR when \fI$name\fR is (is not)
index e2d20cb42dc47730c8d16d047b322ed5751cb279..5d0ab04af84d3baba03b19689ad199727d74e0cb 100644 (file)
@@ -41,6 +41,7 @@
 
 #include "sys_defs.h"
 #include <sys/stat.h>
+#include <sys/time.h>
 #include <unistd.h>
 #include <time.h>
 #include <errno.h>
@@ -86,7 +87,9 @@ int     deliver_maildir(LOCAL_STATE state, USER_ATTR usr_attr, char *path)
     int     deliver_status;
     int     copy_flags;
     struct stat st;
-    time_t  starttime = time((time_t *) 0);
+    struct timeval starttime;
+
+    GETTIMEOFDAY(&starttime);
 
     /*
      * Make verbose logging easier to understand.
@@ -166,7 +169,8 @@ int     deliver_maildir(LOCAL_STATE state, USER_ATTR usr_attr, char *path)
      * filesystem: a maildir has to be within a single UNIX device for link()
      * and rename() to work.)
      * 
-     * [...]
+     * Mn, where n is (in decimal) the microsecond counter from the same
+     * gettimeofday() used for the left part of the unique name.
      * 
      * Pn, where n is (in decimal) the process ID.
      * 
@@ -176,7 +180,7 @@ int     deliver_maildir(LOCAL_STATE state, USER_ATTR usr_attr, char *path)
 
     set_eugid(usr_attr.uid, usr_attr.gid);
     vstring_sprintf(buf, "%lu.P%d.%s",
-                   (unsigned long) starttime, var_pid, get_hostname());
+                (unsigned long) starttime.tv_sec, var_pid, get_hostname());
     tmpfile = concatenate(tmpdir, STR(buf), (char *) 0);
     newfile = 0;
     if ((dst = vstream_fopen(tmpfile, O_WRONLY | O_CREAT | O_EXCL, 0600)) == 0
@@ -187,9 +191,12 @@ int     deliver_maildir(LOCAL_STATE state, USER_ATTR usr_attr, char *path)
     } else if (fstat(vstream_fileno(dst), &st) < 0) {
        vstring_sprintf(why, "create %s: %m", tmpfile);
     } else {
-       vstring_sprintf(buf, "%lu.V%lxI%lx.%s",
-                       (unsigned long) starttime, (unsigned long) st.st_dev,
-                       (unsigned long) st.st_ino, get_hostname());
+       vstring_sprintf(buf, "%lu.V%lxI%lxM%lu.%s",
+                       (unsigned long) starttime.tv_sec,
+                       (unsigned long) st.st_dev,
+                       (unsigned long) st.st_ino,
+                       (unsigned long) starttime.tv_usec,
+                       get_hostname());
        newfile = concatenate(newdir, STR(buf), (char *) 0);
        if ((mail_copy_status = mail_copy(COPY_ATTR(state.msg_attr),
                                        dst, copy_flags, "\n", why)) == 0) {
index a722d0908d18ac3a2feff2ddf9e4ed90437c8be4..658d1d74e2e37d0483535f11edc8f907171fbd7a 100644 (file)
@@ -936,10 +936,9 @@ static void qmgr_message_resolve(QMGR_MESSAGE *message)
         * agent resources. We use recipient@nexthop as queue name rather
         * than the actual recipient domain name, so that one recipient in
         * multiple equivalent domains cannot evade the per-recipient
-        * concurrency limit. XXX Should split the address on the recipient
-        * delimiter if one is defined, but doing a proper job requires
-        * knowledge of local aliases. Yuck! I don't want to duplicate
-        * delivery-agent specific knowledge in the queue manager.
+        * concurrency limit. Split the address on the recipient delimiter if
+        * one is defined, so that extended addresses don't get extra
+        * delivery slots.
         * 
         * Fold the result to lower case so that we don't have multiple queues
         * for the same name.
@@ -947,18 +946,32 @@ static void qmgr_message_resolve(QMGR_MESSAGE *message)
         * Important! All recipients in a queue must have the same nexthop
         * value. It is OK to have multiple queues with the same nexthop
         * value, but only when those queues are named after recipients.
+        * 
+        * The single-recipient code below was written for local(8) like
+        * delivery agents, and assumes that all domains that deliver to the
+        * same (transport + nexthop) are aliases for $nexthop. Delivery
+        * concurrency is changed from per-domain into per-recipient, by
+        * changing the queue name from nexthop into localpart@nexthop.
+        * 
+        * XXX This assumption is incorrect when different destinations share
+        * the same (transport + nexthop). In reality, such transports are
+        * rarely configured to use single-recipient deliveries. The fix is
+        * to decouple the per-destination recipient limit from the
+        * per-destination concurrency.
         */
        vstring_strcpy(queue_name, STR(reply.nexthop));
        if (strcmp(transport->name, MAIL_SERVICE_ERROR) != 0
            && transport->recipient_limit == 1) {
+           /* Copy the recipient localpart. */
            at = strrchr(STR(reply.recipient), '@');
            len = (at ? (at - STR(reply.recipient))
                   : strlen(STR(reply.recipient)));
-           VSTRING_SPACE(queue_name, len + 2);
-           memmove(STR(queue_name) + len + 1, STR(queue_name),
-                   LEN(queue_name) + 1);
-           memcpy(STR(queue_name), STR(reply.recipient), len);
-           STR(queue_name)[len] = '@';
+           vstring_strncpy(queue_name, STR(reply.recipient), len);
+           /* Remove the address extension from the recipient localpart. */
+           if (*var_rcpt_delim && split_addr(STR(queue_name), *var_rcpt_delim))
+               vstring_truncate(queue_name, strlen(STR(queue_name)));
+           /* Assume the recipient domain is equivalent to nexthop. */
+           vstring_sprintf_append(queue_name, "@%s", STR(reply.nexthop));
        }
        lowercase(STR(queue_name));
 
index 708f48923581dcf19b440fd54c8f54df014c5b43..724cbd1ab64f7ef36197abab9691b1364258ef31 100644 (file)
@@ -980,10 +980,9 @@ static void qmgr_message_resolve(QMGR_MESSAGE *message)
         * agent resources. We use recipient@nexthop as queue name rather
         * than the actual recipient domain name, so that one recipient in
         * multiple equivalent domains cannot evade the per-recipient
-        * concurrency limit. XXX Should split the address on the recipient
-        * delimiter if one is defined, but doing a proper job requires
-        * knowledge of local aliases. Yuck! I don't want to duplicate
-        * delivery-agent specific knowledge in the queue manager.
+        * concurrency limit. Split the address on the recipient delimiter if
+        * one is defined, so that extended addresses don't get extra
+        * delivery slots.
         * 
         * Fold the result to lower case so that we don't have multiple queues
         * for the same name.
@@ -991,18 +990,32 @@ static void qmgr_message_resolve(QMGR_MESSAGE *message)
         * Important! All recipients in a queue must have the same nexthop
         * value. It is OK to have multiple queues with the same nexthop
         * value, but only when those queues are named after recipients.
+        * 
+        * The single-recipient code below was written for local(8) like
+        * delivery agents, and assumes that all domains that deliver to the
+        * same (transport + nexthop) are aliases for $nexthop. Delivery
+        * concurrency is changed from per-domain into per-recipient, by
+        * changing the queue name from nexthop into localpart@nexthop.
+        * 
+        * XXX This assumption is incorrect when different destinations share
+        * the same (transport + nexthop). In reality, such transports are
+        * rarely configured to use single-recipient deliveries. The fix is
+        * to decouple the per-destination recipient limit from the
+        * per-destination concurrency.
         */
        vstring_strcpy(queue_name, STR(reply.nexthop));
        if (strcmp(transport->name, MAIL_SERVICE_ERROR) != 0
            && transport->recipient_limit == 1) {
+           /* Copy the recipient localpart. */
            at = strrchr(STR(reply.recipient), '@');
            len = (at ? (at - STR(reply.recipient))
                   : strlen(STR(reply.recipient)));
-           VSTRING_SPACE(queue_name, len + 2);
-           memmove(STR(queue_name) + len + 1, STR(queue_name),
-                   LEN(queue_name) + 1);
-           memcpy(STR(queue_name), STR(reply.recipient), len);
-           STR(queue_name)[len] = '@';
+           vstring_strncpy(queue_name, STR(reply.recipient), len);
+           /* Remove the address extension from the recipient localpart. */
+           if (*var_rcpt_delim && split_addr(STR(queue_name), *var_rcpt_delim))
+               vstring_truncate(queue_name, strlen(STR(queue_name)));
+           /* Assume the recipient domain is equivalent to nexthop. */
+           vstring_sprintf_append(queue_name, "@%s", STR(reply.nexthop));
        }
        lowercase(STR(queue_name));
 
index c51a4e7c51f0b7d0ab90b83228c2ef8e64e34a9c..b7f46bddd1f00b8f909bac93938c75fe8acf09cf 100644 (file)
@@ -36,6 +36,8 @@
 
 #include "sys_defs.h"
 #include <sys/stat.h>
+#include <sys/time.h>
+#include <unistd.h>
 #include <time.h>
 #include <errno.h>
 
@@ -84,7 +86,9 @@ int     deliver_maildir(LOCAL_STATE state, USER_ATTR usr_attr)
     int     deliver_status;
     int     copy_flags;
     struct stat st;
-    time_t  starttime = time((time_t *) 0);
+    struct timeval starttime;
+
+    GETTIMEOFDAY(&starttime);
 
     /*
      * Make verbose logging easier to understand.
@@ -163,7 +167,8 @@ int     deliver_maildir(LOCAL_STATE state, USER_ATTR usr_attr)
      * filesystem: a maildir has to be within a single UNIX device for link()
      * and rename() to work.)
      * 
-     * [...]
+     * Mn, where n is (in decimal) the microsecond counter from the same
+     * gettimeofday() used for the left part of the unique name.
      * 
      * Pn, where n is (in decimal) the process ID.
      * 
@@ -173,7 +178,7 @@ int     deliver_maildir(LOCAL_STATE state, USER_ATTR usr_attr)
 
     set_eugid(usr_attr.uid, usr_attr.gid);
     vstring_sprintf(buf, "%lu.P%d.%s",
-                   (unsigned long) starttime, var_pid, get_hostname());
+                (unsigned long) starttime.tv_sec, var_pid, get_hostname());
     tmpfile = concatenate(tmpdir, STR(buf), (char *) 0);
     newfile = 0;
     if ((dst = vstream_fopen(tmpfile, O_WRONLY | O_CREAT | O_EXCL, 0600)) == 0
@@ -184,9 +189,12 @@ int     deliver_maildir(LOCAL_STATE state, USER_ATTR usr_attr)
     } else if (fstat(vstream_fileno(dst), &st) < 0) {
        vstring_sprintf(why, "create %s: %m", tmpfile);
     } else {
-       vstring_sprintf(buf, "%lu.V%lxI%lx.%s",
-                       (unsigned long) starttime, (unsigned long) st.st_dev,
-                       (unsigned long) st.st_ino, get_hostname());
+       vstring_sprintf(buf, "%lu.V%lxI%lxM%lu.%s",
+                       (unsigned long) starttime.tv_sec,
+                       (unsigned long) st.st_dev,
+                       (unsigned long) st.st_ino,
+                       (unsigned long) starttime.tv_usec,
+                       get_hostname());
        newfile = concatenate(newdir, STR(buf), (char *) 0);
        if ((mail_copy_status = mail_copy(COPY_ATTR(state.msg_attr),
                                        dst, copy_flags, "\n", why)) == 0) {