2
0
mirror of https://github.com/vdukhovni/postfix synced 2025-09-01 14:45:32 +00:00

postfix-2.4-20061217

This commit is contained in:
Wietse Venema
2006-12-17 00:00:00 -05:00
committed by Viktor Dukhovni
parent 39feae3a7e
commit 4be2bc6036
36 changed files with 878 additions and 303 deletions

View File

@@ -12935,7 +12935,8 @@ Apologies for any names omitted.
after-the-fact consistency of the thread that was interrupted. after-the-fact consistency of the thread that was interrupted.
File: util/msg_output.c. File: util/msg_output.c.
Robustness: replace exit() calls by _exit(). File: util/msg.c. Robustness: replace exit() calls by _exit(). File: util/msg.c,
bounce/bounce_cleanup.c.
20061207 20061207
@@ -12982,7 +12983,47 @@ Apologies for any names omitted.
Cleanup: streamline the signal handler reentrancy protections, Cleanup: streamline the signal handler reentrancy protections,
and document under what conditions these protections work, and document under what conditions these protections work,
with REENTRANCY sections in the relevant man pages. Files: with REENTRANCY sections in the relevant man pages. Files:
util/vbuf.c. util/msg.c, util/msg_output.c. util/vbuf_print.c. util/msg.c, util/msg_output.c.
20061211
When doing server access control by the TLS client fingerprint,
do not require client certificate verification. Victor
Duchovni. File: smtpd/smtpd_check.c.
When the remote SMTP client certificate isn't verified,
don't send ccert_subject and ccert_issuer attributes in
check_policy_service requests. Victor Duchovni. File:
smtpd/smtpd_check.c.
Bugfix: the postconf command still complained about an
unqualified machine name, because it was not updated with
the 20050513 change that introduced a default "mydomain =
localdomain". File: postconf/postconf.c.
20061213
Bugfix: race condition in "ETRN site", "sendmail -qRsite"
and "postqueue -s site". When the command arrived while an
incoming queue scan was already in progress, mail could
stay deferred instead of being flushed. The fix was to
unthrottle the queue manager before moving files from the
deferred queue to the incoming queue. Files: flush/flush.c,
qmgr/qmgr_scan.c.
Feature: "sendmail -qIqueueid" and "postqueue -i queueid"
to flush a specific queue file. Files: sendmail/sendmail.c,
postqueue/postqueue.c, global/flush_clnt.c, flush/flush.c.
20061214
Performance: "sendmail -qIqueueid" and "postqueue -i queueid"
unthrottle only the necessary message delivery transports
and queues. The unthrottle request now is propagated to the
queue manager via queue file group read permission bits.
Based on initial implementation by Victor Duchovni. Files:
flush/flush.c, *qmgr/qmgr.c, *qmgr/qmgr_scan.c,
*qmgr/qmgr_active.c, *qmgr/qmgr_message.c.
Wish list: Wish list:
@@ -13037,9 +13078,6 @@ Wish list:
Are transport:nexthop null fields the same as in the case Are transport:nexthop null fields the same as in the case
of default_transport etc. parameters? of default_transport etc. parameters?
Introduce structured API for tls_server_mumble() just like
with smtp(8): this eliminates ever-growing lists of arguments.
Don't lose bits when converting st_dev into maildir file Don't lose bits when converting st_dev into maildir file
name. It's 64 bits on Linux. Found with the BEAM source name. It's 64 bits on Linux. Found with the BEAM source
code analyzer. Is this really a problem, or are they just code analyzer. Is this really a problem, or are they just
@@ -13054,9 +13092,6 @@ Wish list:
while it is configured in an SMTP server that runs before while it is configured in an SMTP server that runs before
the smtpd_proxy filter. the smtpd_proxy filter.
The sendmail command should not return non-std exit status
after fatal error in some internal library routine.
Log DSN original recipient when rejecting mail. Log DSN original recipient when rejecting mail.
Keep whitespace between label and ":"? Keep whitespace between label and ":"?
@@ -13190,20 +13225,13 @@ Wish list:
would allow correlation of rejected RCPT TO requests with would allow correlation of rejected RCPT TO requests with
accepted requests for the same mail transaction. accepted requests for the same mail transaction.
Med: silly queue file bit so that the queue manager doesn't
skip files when fast flush is requested while a queue scan
is in progress. The bit is set by the flush server and is
reset when the mail is deferred, so that it survives queue
manager restart. It's not clear, however, how one would
unthrottle disabled transports or queues.
Med: postsuper -r should do something with recipients in Med: postsuper -r should do something with recipients in
bounce logfiles, to make sure the sender will be notified. bounce logfiles, to make sure the sender will be notified.
To be perfectly safe, no process other than the queue manager To be perfectly safe, no process other than the queue manager
should move a queue file away from the active queue. should move a queue file away from the active queue.
This could involve tagging a queue file, and use up another This could involve tagging a queue file, and use up another
permission bit. permission bit (postsuper tags a "hot" file, qmgr requeues it).
Low: postsuper re-run after renaming files, but only a Low: postsuper re-run after renaming files, but only a
limited number of times. limited number of times.
@@ -13222,7 +13250,7 @@ Wish list:
Low: have a configurable list of errno values for mailbox Low: have a configurable list of errno values for mailbox
or maildir delivery that result in deferral rather than or maildir delivery that result in deferral rather than
bouncing mail. bouncing mail. What about "killed by signal" exits?
Low: after reorganizing configuration parameters, add flags Low: after reorganizing configuration parameters, add flags
to all parameters whose value can be read from file. to all parameters whose value can be read from file.

View File

@@ -110,6 +110,7 @@ this:
/^Received:.* +by +(porcupine\.org)[[:>:]]/ /^Received:.* +by +(porcupine\.org)[[:>:]]/
reject forged mail server name in Received: header: $1 reject forged mail server name in Received: header: $1
endif endif
/^Message-ID:.* <!&!/ DUNNO
/^Message-ID:.*@(porcupine\.org)/ /^Message-ID:.*@(porcupine\.org)/
reject forged domain name in Message-ID: header: $1 reject forged domain name in Message-ID: header: $1
@@ -123,6 +124,7 @@ this:
/^[> ]*Received:.* +by +(porcupine\.org)[[:>:]]/ /^[> ]*Received:.* +by +(porcupine\.org)[[:>:]]/
reject forged mail server name in Received: header: $1 reject forged mail server name in Received: header: $1
endif endif
/^[> ]*Message-ID:.* <!&!/ DUNNO
/^[> ]*Message-ID:.*@(porcupine\.org)/ /^[> ]*Message-ID:.*@(porcupine\.org)/
reject forged domain name in Message-ID: header: $1 reject forged domain name in Message-ID: header: $1
@@ -143,16 +145,19 @@ Notes:
* The "if /pattern/" and "endif" eliminate unnecessary matching attempts. DO * The "if /pattern/" and "endif" eliminate unnecessary matching attempts. DO
NOT indent lines starting with /pattern/ between the "if" and "endif"! NOT indent lines starting with /pattern/ between the "if" and "endif"!
* The two "Message-ID:.* <!&!" rules are workarounds for some versions of
Outlook express, as described in the caveats section below.
CCaavveeaattss CCaavveeaattss
Netscape Messenger (and reportedly, Mozilla) sends a HELO name that is * Netscape Messenger (and reportedly, Mozilla) sends a HELO name that is
identical to the sender address domain part. If you have such clients then the identical to the sender address domain part. If you have such clients then
above patterns would block legitimate email. the above patterns would block legitimate email.
My network has only one such machine, and to prevent its mail from being My network has only one such machine, and to prevent its mail from being
blocked I have configured it to send mail as user@hostname.porcupine.org. On blocked I have configured it to send mail as user@hostname.porcupine.org.
the Postfix server, a canonical mapping translates this temporary address into On the Postfix server, a canonical mapping translates this temporary
user@porcupine.org. address into user@porcupine.org.
/etc/postfix/main.cf: /etc/postfix/main.cf:
canonical_maps = hash:/etc/postfix/canonical canonical_maps = hash:/etc/postfix/canonical
@@ -160,13 +165,34 @@ user@porcupine.org.
/etc/postfix/canonical: /etc/postfix/canonical:
@hostname.porcupine.org @porcupine.org @hostname.porcupine.org @porcupine.org
This is of course practical only when you have very few systems that send HELO This is of course practical only when you have very few systems that send
commands like this, and when you never have to send mail to a user on such a HELO commands like this, and when you never have to send mail to a user on
host. such a host.
An alternative would be to remove the hostname from "hostname.porcupine.org" An alternative would be to remove the hostname from
with address masquerading, as described in the ADDRESS_REWRITING_README "hostname.porcupine.org" with address masquerading, as described in the
document. ADDRESS_REWRITING_README document.
* Reportedly, Outlook 2003 (perhaps Outlook Express, and other versions as
well) present substantially different Message-ID headers depending upon
whether or not a DSN is requested (via Options "Request a delivery receipt
for this message").
When a DSN is requested, Outlook 2003 uses a Message-ID string that ends in
the sender's domain name:
Message-ID: <!&! ...very long string... ==@example.com>
where example.com is the domain name part of the email address specified in
Outlook's account settings for the user. Since many users configure their
email addresses as username@example.com, messages with DSN turned on will
trigger the REJECT action in the previous section.
If you have such clients then you can to exclude their Message-ID strings
with the two "Message-ID:.* <!&!" patterns that are shown in the previous
section. Otherwise you will not be able to use the two backscatter rules to
stop forged Message ID strings. Of course this workaround may break the
next time Outlook is changed.
BBlloocckkiinngg bbaacckkssccaatttteerr mmaaiill wwiitthh ffoorrggeedd sseennddeerr iinnffoorrmmaattiioonn BBlloocckkiinngg bbaacckkssccaatttteerr mmaaiill wwiitthh ffoorrggeedd sseennddeerr iinnffoorrmmaattiioonn

View File

@@ -17,6 +17,14 @@ Incompatibility with Postfix 2.2 and earlier
If you upgrade from Postfix 2.2 or earlier, read RELEASE_NOTES-2.3 If you upgrade from Postfix 2.2 or earlier, read RELEASE_NOTES-2.3
before proceeding. before proceeding.
Incompatible changes with Postfix snapshot 20061217
===================================================
Postfix no longer requires a domain name. It uses "localdomain" as
the default Internet domain name when no domain is specified in
main.cf, and when the machine hostname does not include domain name
information.
Incompatible changes with Postfix snapshot 20061209 Incompatible changes with Postfix snapshot 20061209
=================================================== ===================================================

View File

@@ -186,6 +186,7 @@ patterns like this: </p>
/^Received:.* +by +(porcupine\.org)[[:&gt;:]]/ /^Received:.* +by +(porcupine\.org)[[:&gt;:]]/
reject forged mail server name in Received: header: $1 reject forged mail server name in Received: header: $1
endif endif
/^Message-ID:.* &lt;!&amp;!/ DUNNO
/^Message-ID:.*@(porcupine\.org)/ /^Message-ID:.*@(porcupine\.org)/
reject forged domain name in Message-ID: header: $1 reject forged domain name in Message-ID: header: $1
@@ -198,6 +199,7 @@ patterns like this: </p>
/^[&gt; ]*Received:.* +by +(porcupine\.org)[[:&gt;:]]/ /^[&gt; ]*Received:.* +by +(porcupine\.org)[[:&gt;:]]/
reject forged mail server name in Received: header: $1 reject forged mail server name in Received: header: $1
endif endif
/^[&gt; ]*Message-ID:.* &lt;!&amp;!/ DUNNO
/^[&gt; ]*Message-ID:.*@(porcupine\.org)/ /^[&gt; ]*Message-ID:.*@(porcupine\.org)/
reject forged domain name in Message-ID: header: $1 reject forged domain name in Message-ID: header: $1
</pre> </pre>
@@ -226,10 +228,18 @@ see your system documentation. </p>
matching attempts. DO NOT indent lines starting with /pattern/ matching attempts. DO NOT indent lines starting with /pattern/
between the "if" and "endif"! </p> between the "if" and "endif"! </p>
<li> <p> The two "<tt>Message-ID:.* &lt;!&amp;!</tt>" rules are
workarounds for some versions of Outlook express, as described in
the <a href="#caveats"> caveats </a> section below.
</ul> </ul>
<p><a name="caveats"><strong>Caveats</strong></a></p> <p><a name="caveats"><strong>Caveats</strong></a></p>
<ul>
<li>
<p> Netscape Messenger (and reportedly, Mozilla) sends a HELO name <p> Netscape Messenger (and reportedly, Mozilla) sends a HELO name
that is identical to the sender address domain part. If you have that is identical to the sender address domain part. If you have
such clients then the above patterns would block legitimate email. such clients then the above patterns would block legitimate email.
@@ -260,6 +270,35 @@ mail to a user on such a host. </p>
masquerading, as described in the <a href="ADDRESS_REWRITING_README.html">ADDRESS_REWRITING_README</a> document. masquerading, as described in the <a href="ADDRESS_REWRITING_README.html">ADDRESS_REWRITING_README</a> document.
</p> </p>
<li> <p> Reportedly, Outlook 2003 (perhaps Outlook Express, and
other versions as well) present substantially different Message-ID
headers depending upon whether or not a DSN is requested (via Options
"Request a delivery receipt for this message"). </p>
<p> When a DSN is requested, Outlook 2003 uses a Message-ID string
that ends in the sender's domain name: </p>
<blockquote>
<pre>
Message-ID: &lt;!&amp;! ...very long string... ==@example.com&gt;
</pre>
</blockquote>
<p> where <i>example.com</i> is the domain name part of the email
address specified in Outlook's account settings for the user. Since
many users configure their email addresses as <i>username@example.com</i>,
messages with DSN turned on will trigger the REJECT action in the
previous section. </p>
<p> If you have such clients then you can to exclude their Message-ID
strings with the two "<tt>Message-ID:.* &lt;!&amp;!</tt>" patterns
that are shown in the previous section. Otherwise you will not be
able to use the two backscatter rules to stop forged Message ID
strings. Of course this workaround may break the next time Outlook
is changed. </p>
</ul>
<h3><a name="forged_sender">Blocking backscatter mail with forged <h3><a name="forged_sender">Blocking backscatter mail with forged
sender information</a></h3> sender information</a></h3>

View File

@@ -38,10 +38,13 @@ FLUSH(8) FLUSH(8)
the specified queue ID is queued for the specified the specified queue ID is queued for the specified
destination. destination.
<b>send</b> <i>sitename</i> <b>send_site</b> <i>sitename</i>
Request delivery of mail that is queued for the Request delivery of mail that is queued for the
specified destination. specified destination.
<b>send_file</b> <i>queueid</i>
Request delivery of the specified deferred message.
<b>refresh</b> <b>refresh</b>
Refresh non-empty per-destination logfiles that Refresh non-empty per-destination logfiles that
were not read in <b>$<a href="postconf.5.html#fast_flush_refresh_time">fast_flush_refresh_time</a></b> hours, by were not read in <b>$<a href="postconf.5.html#fast_flush_refresh_time">fast_flush_refresh_time</a></b> hours, by

View File

@@ -9190,9 +9190,10 @@ client network address information.
<dt><b><a name="check_ccert_access">check_ccert_access</a> <i><a href="DATABASE_README.html">type:table</a></i></b></dt> <dt><b><a name="check_ccert_access">check_ccert_access</a> <i><a href="DATABASE_README.html">type:table</a></i></b></dt>
<dd>When the remote SMTP client certificate is verified successfully, <dd> Use the client certificate fingerprint as lookup key for the
use the client certificate fingerprint as lookup key for the specified specified <a href="access.5.html">access(5)</a> database; with Postfix version 2.2, also require
<a href="access.5.html">access(5)</a> database. This feature is available with Postfix version 2.2.</dd> that the SMTP client certificate is verified successfully. This
feature is available with Postfix version 2.2 and later.</dd>
<dt><b><a name="check_client_access">check_client_access</a> <i><a href="DATABASE_README.html">type:table</a></i></b></dt> <dt><b><a name="check_client_access">check_client_access</a> <i><a href="DATABASE_README.html">type:table</a></i></b></dt>

View File

@@ -11,6 +11,7 @@ POSTQUEUE(1) POSTQUEUE(1)
<b>SYNOPSIS</b> <b>SYNOPSIS</b>
<b>postqueue</b> [<b>-v</b>] [<b>-c</b> <i>config</i><b>_</b><i>dir</i>] <b>-f</b> <b>postqueue</b> [<b>-v</b>] [<b>-c</b> <i>config</i><b>_</b><i>dir</i>] <b>-f</b>
<b>postqueue</b> [<b>-v</b>] [<b>-c</b> <i>config</i><b>_</b><i>dir</i>] <b>-i</b> <i>queue</i><b>_</b><i>id</i>
<b>postqueue</b> [<b>-v</b>] [<b>-c</b> <i>config</i><b>_</b><i>dir</i>] <b>-p</b> <b>postqueue</b> [<b>-v</b>] [<b>-c</b> <i>config</i><b>_</b><i>dir</i>] <b>-p</b>
<b>postqueue</b> [<b>-v</b>] [<b>-c</b> <i>config</i><b>_</b><i>dir</i>] <b>-s</b> <i>site</i> <b>postqueue</b> [<b>-v</b>] [<b>-c</b> <i>config</i><b>_</b><i>dir</i>] <b>-s</b> <i>site</i>
@@ -41,6 +42,12 @@ POSTQUEUE(1) POSTQUEUE(1)
will result in poor delivery performance of all will result in poor delivery performance of all
other mail. other mail.
<b>-i</b> <i>queue</i><b>_</b><i>id</i>
Schedule immediate delivery of mail with the speci-
fied queue ID. This feature uses the <a href="flush.8.html"><b>flush</b>(8)</a>
server, and is available with Postfix 2.4 and
later.
<b>-p</b> Produce a traditional sendmail-style queue listing. <b>-p</b> Produce a traditional sendmail-style queue listing.
This option implements the traditional <b>mailq</b> com- This option implements the traditional <b>mailq</b> com-
mand, by contacting the Postfix <a href="showq.8.html"><b>showq</b>(8)</a> daemon. mand, by contacting the Postfix <a href="showq.8.html"><b>showq</b>(8)</a> daemon.

View File

@@ -217,6 +217,12 @@ SENDMAIL(1) SENDMAIL(1)
The interval between queue runs. Use the The interval between queue runs. Use the
<b><a href="postconf.5.html#queue_run_delay">queue_run_delay</a></b> configuration parameter instead. <b><a href="postconf.5.html#queue_run_delay">queue_run_delay</a></b> configuration parameter instead.
<b>-qI</b><i>queueid</i>
Schedule immediate delivery of mail with the speci-
fied queue ID. This option is implemented by exe-
cuting the <a href="postqueue.1.html"><b>postqueue</b>(1)</a> command, and is available
with Postfix version 2.4 and later.
<b>-qR</b><i>site</i> <b>-qR</b><i>site</i>
Schedule immediate delivery of all mail that is Schedule immediate delivery of all mail that is
queued for the named <i>site</i>. This option accepts only queued for the named <i>site</i>. This option accepts only

View File

@@ -10,6 +10,8 @@ Postfix queue control
.nf .nf
\fBpostqueue\fR [\fB-v\fR] [\fB-c \fIconfig_dir\fR] \fB-f\fR \fBpostqueue\fR [\fB-v\fR] [\fB-c \fIconfig_dir\fR] \fB-f\fR
.br .br
\fBpostqueue\fR [\fB-v\fR] [\fB-c \fIconfig_dir\fR] \fB-i \fIqueue_id\fR
.br
\fBpostqueue\fR [\fB-v\fR] [\fB-c \fIconfig_dir\fR] \fB-p\fR \fBpostqueue\fR [\fB-v\fR] [\fB-c \fIconfig_dir\fR] \fB-p\fR
.br .br
\fBpostqueue\fR [\fB-v\fR] [\fB-c \fIconfig_dir\fR] \fB-s \fIsite\fR \fBpostqueue\fR [\fB-v\fR] [\fB-c \fIconfig_dir\fR] \fB-s \fIsite\fR
@@ -36,6 +38,10 @@ by contacting the Postfix \fBqmgr\fR(8) daemon.
Warning: flushing undeliverable mail frequently will result in Warning: flushing undeliverable mail frequently will result in
poor delivery performance of all other mail. poor delivery performance of all other mail.
.IP "\fB-i \fIqueue_id\fR"
Schedule immediate delivery of mail with the specified queue ID.
This feature uses the \fBflush\fR(8) server, and is available
with Postfix 2.4 and later.
.IP \fB-p\fR .IP \fB-p\fR
Produce a traditional sendmail-style queue listing. Produce a traditional sendmail-style queue listing.
This option implements the traditional \fBmailq\fR command, This option implements the traditional \fBmailq\fR command,

View File

@@ -174,6 +174,11 @@ poor delivery performance of all other mail.
.IP "\fB-q\fIinterval\fR (ignored)" .IP "\fB-q\fIinterval\fR (ignored)"
The interval between queue runs. Use the \fBqueue_run_delay\fR The interval between queue runs. Use the \fBqueue_run_delay\fR
configuration parameter instead. configuration parameter instead.
.IP \fB-qI\fIqueueid\fR
Schedule immediate delivery of mail with the specified queue
ID. This option is implemented by executing the
\fBpostqueue\fR(1) command, and is available with Postfix
version 2.4 and later.
.IP \fB-qR\fIsite\fR .IP \fB-qR\fIsite\fR
Schedule immediate delivery of all mail that is queued for the named Schedule immediate delivery of all mail that is queued for the named
\fIsite\fR. This option accepts only \fIsite\fR names that are \fIsite\fR. This option accepts only \fIsite\fR names that are

View File

@@ -5408,9 +5408,10 @@ restriction that matches wins.
The following restrictions are specific to client hostname or The following restrictions are specific to client hostname or
client network address information. client network address information.
.IP "\fBcheck_ccert_access \fItype:table\fR\fR" .IP "\fBcheck_ccert_access \fItype:table\fR\fR"
When the remote SMTP client certificate is verified successfully, Use the client certificate fingerprint as lookup key for the
use the client certificate fingerprint as lookup key for the specified specified \fBaccess\fR(5) database; with Postfix version 2.2, also require
\fBaccess\fR(5) database. This feature is available with Postfix version 2.2. that the SMTP client certificate is verified successfully. This
feature is available with Postfix version 2.2 and later.
.IP "\fBcheck_client_access \fItype:table\fR\fR" .IP "\fBcheck_client_access \fItype:table\fR\fR"
Search the specified access database for the client hostname, Search the specified access database for the client hostname,
parent domains, client IP address, or networks obtained by stripping parent domains, client IP address, or networks obtained by stripping

View File

@@ -36,9 +36,11 @@ This server implements the following requests:
.IP "\fBadd\fI sitename queueid\fR" .IP "\fBadd\fI sitename queueid\fR"
Inform the \fBflush\fR(8) server that the message with the specified Inform the \fBflush\fR(8) server that the message with the specified
queue ID is queued for the specified destination. queue ID is queued for the specified destination.
.IP "\fBsend\fI sitename\fR" .IP "\fBsend_site\fI sitename\fR"
Request delivery of mail that is queued for the specified Request delivery of mail that is queued for the specified
destination. destination.
.IP "\fBsend_file\fI queueid\fR"
Request delivery of the specified deferred message.
.IP \fBrefresh\fR .IP \fBrefresh\fR
Refresh non-empty per-destination logfiles that were not read in Refresh non-empty per-destination logfiles that were not read in
\fB$fast_flush_refresh_time\fR hours, by simulating \fB$fast_flush_refresh_time\fR hours, by simulating

View File

@@ -186,6 +186,7 @@ patterns like this: </p>
/^Received:.* +by +(porcupine\.org)[[:&gt;:]]/ /^Received:.* +by +(porcupine\.org)[[:&gt;:]]/
reject forged mail server name in Received: header: $1 reject forged mail server name in Received: header: $1
endif endif
/^Message-ID:.* &lt;!&amp;!/ DUNNO
/^Message-ID:.*@(porcupine\.org)/ /^Message-ID:.*@(porcupine\.org)/
reject forged domain name in Message-ID: header: $1 reject forged domain name in Message-ID: header: $1
@@ -198,6 +199,7 @@ patterns like this: </p>
/^[&gt; ]*Received:.* +by +(porcupine\.org)[[:&gt;:]]/ /^[&gt; ]*Received:.* +by +(porcupine\.org)[[:&gt;:]]/
reject forged mail server name in Received: header: $1 reject forged mail server name in Received: header: $1
endif endif
/^[&gt; ]*Message-ID:.* &lt;!&amp;!/ DUNNO
/^[&gt; ]*Message-ID:.*@(porcupine\.org)/ /^[&gt; ]*Message-ID:.*@(porcupine\.org)/
reject forged domain name in Message-ID: header: $1 reject forged domain name in Message-ID: header: $1
</pre> </pre>
@@ -226,10 +228,18 @@ see your system documentation. </p>
matching attempts. DO NOT indent lines starting with /pattern/ matching attempts. DO NOT indent lines starting with /pattern/
between the "if" and "endif"! </p> between the "if" and "endif"! </p>
<li> <p> The two "<tt>Message-ID:.* &lt;!&amp;!</tt>" rules are
workarounds for some versions of Outlook express, as described in
the <a href="#caveats"> caveats </a> section below.
</ul> </ul>
<p><a name="caveats"><strong>Caveats</strong></a></p> <p><a name="caveats"><strong>Caveats</strong></a></p>
<ul>
<li>
<p> Netscape Messenger (and reportedly, Mozilla) sends a HELO name <p> Netscape Messenger (and reportedly, Mozilla) sends a HELO name
that is identical to the sender address domain part. If you have that is identical to the sender address domain part. If you have
such clients then the above patterns would block legitimate email. such clients then the above patterns would block legitimate email.
@@ -260,6 +270,35 @@ mail to a user on such a host. </p>
masquerading, as described in the ADDRESS_REWRITING_README document. masquerading, as described in the ADDRESS_REWRITING_README document.
</p> </p>
<li> <p> Reportedly, Outlook 2003 (perhaps Outlook Express, and
other versions as well) present substantially different Message-ID
headers depending upon whether or not a DSN is requested (via Options
"Request a delivery receipt for this message"). </p>
<p> When a DSN is requested, Outlook 2003 uses a Message-ID string
that ends in the sender's domain name: </p>
<blockquote>
<pre>
Message-ID: &lt;!&amp;! ...very long string... ==@example.com&gt;
</pre>
</blockquote>
<p> where <i>example.com</i> is the domain name part of the email
address specified in Outlook's account settings for the user. Since
many users configure their email addresses as <i>username@example.com</i>,
messages with DSN turned on will trigger the REJECT action in the
previous section. </p>
<p> If you have such clients then you can to exclude their Message-ID
strings with the two "<tt>Message-ID:.* &lt;!&amp;!</tt>" patterns
that are shown in the previous section. Otherwise you will not be
able to use the two backscatter rules to stop forged Message ID
strings. Of course this workaround may break the next time Outlook
is changed. </p>
</ul>
<h3><a name="forged_sender">Blocking backscatter mail with forged <h3><a name="forged_sender">Blocking backscatter mail with forged
sender information</a></h3> sender information</a></h3>

View File

@@ -4577,9 +4577,10 @@ client network address information.
<dt><b><a name="check_ccert_access">check_ccert_access</a> <i><a href="DATABASE_README.html">type:table</a></i></b></dt> <dt><b><a name="check_ccert_access">check_ccert_access</a> <i><a href="DATABASE_README.html">type:table</a></i></b></dt>
<dd>When the remote SMTP client certificate is verified successfully, <dd> Use the client certificate fingerprint as lookup key for the
use the client certificate fingerprint as lookup key for the specified specified access(5) database; with Postfix version 2.2, also require
access(5) database. This feature is available with Postfix version 2.2.</dd> that the SMTP client certificate is verified successfully. This
feature is available with Postfix version 2.2 and later.</dd>
<dt><b><a name="check_client_access">check_client_access</a> <i><a href="DATABASE_README.html">type:table</a></i></b></dt> <dt><b><a name="check_client_access">check_client_access</a> <i><a href="DATABASE_README.html">type:table</a></i></b></dt>

View File

@@ -78,6 +78,7 @@ flush.o: ../../include/match_ops.h
flush.o: ../../include/match_parent_style.h flush.o: ../../include/match_parent_style.h
flush.o: ../../include/msg.h flush.o: ../../include/msg.h
flush.o: ../../include/myflock.h flush.o: ../../include/myflock.h
flush.o: ../../include/safe_open.h
flush.o: ../../include/scan_dir.h flush.o: ../../include/scan_dir.h
flush.o: ../../include/stringops.h flush.o: ../../include/stringops.h
flush.o: ../../include/sys_defs.h flush.o: ../../include/sys_defs.h

View File

@@ -30,9 +30,11 @@
/* .IP "\fBadd\fI sitename queueid\fR" /* .IP "\fBadd\fI sitename queueid\fR"
/* Inform the \fBflush\fR(8) server that the message with the specified /* Inform the \fBflush\fR(8) server that the message with the specified
/* queue ID is queued for the specified destination. /* queue ID is queued for the specified destination.
/* .IP "\fBsend\fI sitename\fR" /* .IP "\fBsend_site\fI sitename\fR"
/* Request delivery of mail that is queued for the specified /* Request delivery of mail that is queued for the specified
/* destination. /* destination.
/* .IP "\fBsend_file\fI queueid\fR"
/* Request delivery of the specified deferred message.
/* .IP \fBrefresh\fR /* .IP \fBrefresh\fR
/* Refresh non-empty per-destination logfiles that were not read in /* Refresh non-empty per-destination logfiles that were not read in
/* \fB$fast_flush_refresh_time\fR hours, by simulating /* \fB$fast_flush_refresh_time\fR hours, by simulating
@@ -166,6 +168,7 @@
#include <dict.h> #include <dict.h>
#include <scan_dir.h> #include <scan_dir.h>
#include <stringops.h> #include <stringops.h>
#include <safe_open.h>
/* Global library. */ /* Global library. */
@@ -210,7 +213,7 @@ static DOMAIN_LIST *flush_domains;
* Silly little macros. * Silly little macros.
*/ */
#define STR(x) vstring_str(x) #define STR(x) vstring_str(x)
#define STREQ(x,y) (strcmp(x,y) == 0) #define STREQ(x,y) ((x) == (y) || strcmp(x,y) == 0)
/* /*
* Forward declarations resulting from breaking up routines according to * Forward declarations resulting from breaking up routines according to
@@ -223,9 +226,24 @@ static int flush_send_path(const char *, int);
* Do we only refresh the per-destination logfile, or do we really request * Do we only refresh the per-destination logfile, or do we really request
* mail delivery as if someone sent ETRN? If the latter, we must override * mail delivery as if someone sent ETRN? If the latter, we must override
* information about unavailable hosts or unavailable transports. * information about unavailable hosts or unavailable transports.
*
* When selectively flushing deferred mail, we need to override the queue
* manager's "dead destination" information and unthrottle transports and
* queues. There are two options:
*
* - Unthrottle all transports and queues before we move mail to the incoming
* queue. This is less accurate, but has the advantage when flushing lots of
* mail, because Postfix can skip delivery of flushed messages after it
* discovers that a destination is (still) unavailable.
*
* - Unthrottle some transports and queues after the queue manager moves mail
* to the active queue. This is more accurate, but has the disadvantage when
* flushing lots of mail, because Postfix cannot skip delivery of flushed
* messages after it discovers that a destination is (still) unavailable.
*/ */
#define REFRESH_ONLY 0 #define REFRESH_ONLY 0
#define REFRESH_AND_DELIVER 1 #define UNTHROTTLE_BEFORE (1<<0)
#define UNTHROTTLE_AFTER (1<<1)
/* flush_site_to_path - convert domain or [addr] to harmless string */ /* flush_site_to_path - convert domain or [addr] to harmless string */
@@ -366,6 +384,86 @@ static int flush_send_service(const char *site, int how)
return (status); return (status);
} }
/* flush_one_file - move one queue file to incoming queue */
static int flush_one_file(const char *queue_id, VSTRING *queue_file,
struct utimbuf * tbuf, int how)
{
const char *myname = "flush_one_file";
const char *queue_name;
const char *path;
/*
* Some other instance of this program may flush some logfile and may
* just have moved this queue file to the incoming queue.
*/
for (queue_name = MAIL_QUEUE_DEFERRED; /* see below */ ;
queue_name = MAIL_QUEUE_INCOMING) {
path = mail_queue_path(queue_file, queue_name, queue_id);
if (utime(path, tbuf) == 0)
break;
if (errno != ENOENT)
msg_warn("%s: update %s time stamps: %m", myname, path);
if (STREQ(queue_name, MAIL_QUEUE_INCOMING))
return (0);
}
/*
* With the UNTHROTTLE_AFTER strategy, we leave it up to the queue
* manager to unthrottle transports and queues as it reads recipients
* from a queue file. We request this unthrottle operation by setting the
* group read permission bit.
*
* Note: we must avoid using chmod(). It is not only slower than fchmod()
* but it is also less secure. With chmod(), an attacker could repeatedly
* send requests to the flush server and trick it into changing
* permissions of non-queue files, by exploiting a race condition.
*
* We use safe_open() because we don't validate the file content before
* modifying the file status.
*/
if (how & UNTHROTTLE_AFTER) {
VSTRING *why;
struct stat st;
VSTREAM *fp;
for (why = vstring_alloc(1); /* see below */ ;
queue_name = MAIL_QUEUE_INCOMING,
path = mail_queue_path(queue_file, queue_name, queue_id)) {
if ((fp = safe_open(path, O_RDWR, 0, &st, -1, -1, why)) != 0)
break;
if (errno != ENOENT)
msg_warn("%s: open %s: %s", myname, path, STR(why));
if (errno != ENOENT || STREQ(queue_name, MAIL_QUEUE_INCOMING)) {
vstring_free(why);
return (0);
}
}
vstring_free(why);
if ((st.st_mode & MAIL_QUEUE_STAT_READY) != MAIL_QUEUE_STAT_READY) {
(void) vstream_fclose(fp);
return (0);
}
if (fchmod(vstream_fileno(fp), st.st_mode | MAIL_QUEUE_STAT_UNTHROTTLE) < 0)
msg_warn("%s: fchmod %s: %m", myname, path);
(void) vstream_fclose(fp);
}
/*
* Move the file to the incoming queue, if it isn't already there.
*/
if (STREQ(queue_name, MAIL_QUEUE_INCOMING) == 0
&& mail_queue_rename(queue_id, queue_name, MAIL_QUEUE_INCOMING) < 0
&& errno != ENOENT)
msg_warn("%s: rename from %s to %s: %m",
path, queue_name, MAIL_QUEUE_INCOMING);
/*
* If we got here, we achieved something, so let's claim succes.
*/
return (1);
}
/* flush_send_path - flush logfile file */ /* flush_send_path - flush logfile file */
static int flush_send_path(const char *path, int how) static int flush_send_path(const char *path, int how)
@@ -375,11 +473,10 @@ static int flush_send_path(const char *path, int how)
VSTRING *queue_file; VSTRING *queue_file;
VSTREAM *log; VSTREAM *log;
struct utimbuf tbuf; struct utimbuf tbuf;
static char qmgr_deliver_trigger[] = { static char qmgr_flush_trigger[] = {
QMGR_REQ_SCAN_INCOMING, /* scan incoming queue */
QMGR_REQ_FLUSH_DEAD, /* flush dead site/transport cache */ QMGR_REQ_FLUSH_DEAD, /* flush dead site/transport cache */
}; };
static char qmgr_refresh_trigger[] = { static char qmgr_scan_trigger[] = {
QMGR_REQ_SCAN_INCOMING, /* scan incoming queue */ QMGR_REQ_SCAN_INCOMING, /* scan incoming queue */
}; };
HTABLE *dup_filter; HTABLE *dup_filter;
@@ -410,6 +507,23 @@ static int flush_send_path(const char *path, int how)
if (myflock(vstream_fileno(log), INTERNAL_LOCK, MYFLOCK_OP_EXCLUSIVE) < 0) if (myflock(vstream_fileno(log), INTERNAL_LOCK, MYFLOCK_OP_EXCLUSIVE) < 0)
msg_fatal("%s: lock fast flush logfile %s: %m", myname, path); msg_fatal("%s: lock fast flush logfile %s: %m", myname, path);
/*
* With the UNTHROTTLE_BEFORE strategy, we ask the queue manager to
* unthrottle all transports and queues before we move a deferred queue
* file to the incoming queue. This minimizes a race condition where the
* queue manager seizes a queue file before it knows that we want to
* flush that message.
*
* This reduces the race condition time window to a very small amount (the
* flush server does not really know when the queue manager reads its
* command fifo). But there is a worse race, where the queue manager
* moves a deferred queue file to the active queue before we have a
* chance to expedite its delivery.
*/
if (how & UNTHROTTLE_BEFORE)
mail_trigger(MAIL_CLASS_PUBLIC, var_queue_service,
qmgr_flush_trigger, sizeof(qmgr_flush_trigger));
/* /*
* This is the part that dominates running time: schedule the listed * This is the part that dominates running time: schedule the listed
* queue files for delivery by updating their file time stamps and by * queue files for delivery by updating their file time stamps and by
@@ -444,25 +558,7 @@ static int flush_send_path(const char *path, int how)
myname, path, STR(queue_id)); myname, path, STR(queue_id));
if (dup_filter->used <= FLUSH_DUP_FILTER_SIZE) if (dup_filter->used <= FLUSH_DUP_FILTER_SIZE)
htable_enter(dup_filter, STR(queue_id), 0); htable_enter(dup_filter, STR(queue_id), 0);
count += flush_one_file(STR(queue_id), queue_file, &tbuf, how);
mail_queue_path(queue_file, MAIL_QUEUE_DEFERRED, STR(queue_id));
if (utime(STR(queue_file), &tbuf) < 0) {
if (errno != ENOENT)
msg_warn("%s: update %s time stamps: %m",
myname, STR(queue_file));
/* XXX Wart... */
mail_queue_path(queue_file, MAIL_QUEUE_INCOMING, STR(queue_id));
if (utime(STR(queue_file), &tbuf) < 0)
if (errno != ENOENT)
msg_warn("%s: update %s time stamps: %m",
myname, STR(queue_file));
} else if (mail_queue_rename(STR(queue_id), MAIL_QUEUE_DEFERRED,
MAIL_QUEUE_INCOMING) < 0) {
if (errno != ENOENT)
msg_warn("%s: rename from %s to %s: %m",
STR(queue_file), MAIL_QUEUE_DEFERRED,
MAIL_QUEUE_INCOMING);
}
} else { } else {
if (msg_verbose) if (msg_verbose)
msg_info("%s: logfile %s: skip queue file %s as duplicate", msg_info("%s: logfile %s: skip queue file %s as duplicate",
@@ -489,16 +585,42 @@ static int flush_send_path(const char *path, int how)
if (count > 0) { if (count > 0) {
if (msg_verbose) if (msg_verbose)
msg_info("%s: requesting delivery for logfile %s", myname, path); msg_info("%s: requesting delivery for logfile %s", myname, path);
if (how == REFRESH_ONLY)
mail_trigger(MAIL_CLASS_PUBLIC, var_queue_service, mail_trigger(MAIL_CLASS_PUBLIC, var_queue_service,
qmgr_refresh_trigger, sizeof(qmgr_refresh_trigger)); qmgr_scan_trigger, sizeof(qmgr_scan_trigger));
else
mail_trigger(MAIL_CLASS_PUBLIC, var_queue_service,
qmgr_deliver_trigger, sizeof(qmgr_deliver_trigger));
} }
return (FLUSH_STAT_OK); return (FLUSH_STAT_OK);
} }
/* flush_send_file_service - flush one queue file */
static int flush_send_file_service(const char *queue_id)
{
const char *myname = "flush_send_file_service";
VSTRING *queue_file;
struct utimbuf tbuf;
static char qmgr_scan_trigger[] = {
QMGR_REQ_SCAN_INCOMING, /* scan incoming queue */
};
/*
* Sanity check.
*/
if (!mail_queue_id_ok(queue_id))
return (FLUSH_STAT_BAD);
if (msg_verbose)
msg_info("%s: requesting delivery for queue_id %s", myname, queue_id);
queue_file = vstring_alloc(30);
tbuf.actime = tbuf.modtime = event_time();
if (flush_one_file(queue_id, queue_file, &tbuf, UNTHROTTLE_AFTER) > 0)
mail_trigger(MAIL_CLASS_PUBLIC, var_queue_service,
qmgr_scan_trigger, sizeof(qmgr_scan_trigger));
vstring_free(queue_file);
return (FLUSH_STAT_OK);
}
/* flush_refresh_service - refresh logfiles beyond some age */ /* flush_refresh_service - refresh logfiles beyond some age */
static int flush_refresh_service(int max_age) static int flush_refresh_service(int max_age)
@@ -633,13 +755,22 @@ static void flush_service(VSTREAM *client_stream, char *unused_service,
attr_print(client_stream, ATTR_FLAG_NONE, attr_print(client_stream, ATTR_FLAG_NONE,
ATTR_TYPE_INT, MAIL_ATTR_STATUS, status, ATTR_TYPE_INT, MAIL_ATTR_STATUS, status,
ATTR_TYPE_END); ATTR_TYPE_END);
} else if (STREQ(STR(request), FLUSH_REQ_SEND)) { } else if (STREQ(STR(request), FLUSH_REQ_SEND_SITE)) {
site = vstring_alloc(10); site = vstring_alloc(10);
if (attr_scan(client_stream, ATTR_FLAG_STRICT, if (attr_scan(client_stream, ATTR_FLAG_STRICT,
ATTR_TYPE_STR, MAIL_ATTR_SITE, site, ATTR_TYPE_STR, MAIL_ATTR_SITE, site,
ATTR_TYPE_END) == 1) ATTR_TYPE_END) == 1)
status = flush_send_service(lowercase(STR(site)), status = flush_send_service(lowercase(STR(site)),
REFRESH_AND_DELIVER); UNTHROTTLE_BEFORE);
attr_print(client_stream, ATTR_FLAG_NONE,
ATTR_TYPE_INT, MAIL_ATTR_STATUS, status,
ATTR_TYPE_END);
} else if (STREQ(STR(request), FLUSH_REQ_SEND_FILE)) {
queue_id = vstring_alloc(10);
if (attr_scan(client_stream, ATTR_FLAG_STRICT,
ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, queue_id,
ATTR_TYPE_END) == 1)
status = flush_send_file_service(STR(queue_id));
attr_print(client_stream, ATTR_FLAG_NONE, attr_print(client_stream, ATTR_FLAG_NONE,
ATTR_TYPE_INT, MAIL_ATTR_STATUS, status, ATTR_TYPE_INT, MAIL_ATTR_STATUS, status,
ATTR_TYPE_END); ATTR_TYPE_END);

View File

@@ -12,9 +12,12 @@
/* const char *site; /* const char *site;
/* const char *queue_id; /* const char *queue_id;
/* /*
/* int flush_send(site) /* int flush_send_site(site)
/* const char *site; /* const char *site;
/* /*
/* int flush_send_file(queue_id)
/* const char *queue_id;
/*
/* int flush_refresh() /* int flush_refresh()
/* /*
/* int flush_purge() /* int flush_purge()
@@ -30,9 +33,12 @@
/* flush_add() informs the "fast flush" cache manager that mail is /* flush_add() informs the "fast flush" cache manager that mail is
/* queued for the specified site with the specified queue ID. /* queued for the specified site with the specified queue ID.
/* /*
/* flush_send() requests delivery of all mail that is queued for /* flush_send_site() requests delivery of all mail that is queued for
/* the specified destination. /* the specified destination.
/* /*
/* flush_send_file() requests delivery of mail with the specified
/* queue ID.
/*
/* flush_refresh() requests the "fast flush" cache manager to refresh /* flush_refresh() requests the "fast flush" cache manager to refresh
/* cached information that was not used for some configurable amount /* cached information that was not used for some configurable amount
/* time. /* time.
@@ -153,11 +159,11 @@ int flush_refresh(void)
return (status); return (status);
} }
/* flush_send - deliver mail queued for site */ /* flush_send_site - deliver mail queued for site */
int flush_send(const char *site) int flush_send_site(const char *site)
{ {
const char *myname = "flush_send"; const char *myname = "flush_send_site";
int status; int status;
if (msg_verbose) if (msg_verbose)
@@ -173,7 +179,7 @@ int flush_send(const char *site)
status = FLUSH_STAT_DENY; status = FLUSH_STAT_DENY;
else else
status = mail_command_client(MAIL_CLASS_PUBLIC, var_flush_service, status = mail_command_client(MAIL_CLASS_PUBLIC, var_flush_service,
ATTR_TYPE_STR, MAIL_ATTR_REQ, FLUSH_REQ_SEND, ATTR_TYPE_STR, MAIL_ATTR_REQ, FLUSH_REQ_SEND_SITE,
ATTR_TYPE_STR, MAIL_ATTR_SITE, site, ATTR_TYPE_STR, MAIL_ATTR_SITE, site,
ATTR_TYPE_END); ATTR_TYPE_END);
@@ -183,6 +189,30 @@ int flush_send(const char *site)
return (status); return (status);
} }
/* flush_send_file - deliver specific message */
int flush_send_file(const char *queue_id)
{
const char *myname = "flush_send_file";
int status;
if (msg_verbose)
msg_info("%s: queue_id %s", myname, queue_id);
/*
* Require that the service is turned on.
*/
status = mail_command_client(MAIL_CLASS_PUBLIC, var_flush_service,
ATTR_TYPE_STR, MAIL_ATTR_REQ, FLUSH_REQ_SEND_FILE,
ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, queue_id,
ATTR_TYPE_END);
if (msg_verbose)
msg_info("%s: queue_id %s status %d", myname, queue_id, status);
return (status);
}
/* flush_add - inform "fast flush" cache manager */ /* flush_add - inform "fast flush" cache manager */
int flush_add(const char *site, const char *queue_id) int flush_add(const char *site, const char *queue_id)

View File

@@ -16,7 +16,8 @@
*/ */
extern void flush_init(void); extern void flush_init(void);
extern int flush_add(const char *, const char *); extern int flush_add(const char *, const char *);
extern int flush_send(const char *); extern int flush_send_site(const char *);
extern int flush_send_file(const char *);
extern int flush_refresh(void); extern int flush_refresh(void);
extern int flush_purge(void); extern int flush_purge(void);
@@ -24,7 +25,8 @@ extern int flush_purge(void);
* Mail flush server requests. * Mail flush server requests.
*/ */
#define FLUSH_REQ_ADD "add" /* append queue ID to site log */ #define FLUSH_REQ_ADD "add" /* append queue ID to site log */
#define FLUSH_REQ_SEND "send" /* flush mail queued for site */ #define FLUSH_REQ_SEND_SITE "send_site" /* flush mail for site */
#define FLUSH_REQ_SEND_FILE "send_file" /* flush one queue file */
#define FLUSH_REQ_REFRESH "rfrsh" /* refresh old logfiles */ #define FLUSH_REQ_REFRESH "rfrsh" /* refresh old logfiles */
#define FLUSH_REQ_PURGE "purge" /* refresh all logfiles */ #define FLUSH_REQ_PURGE "purge" /* refresh all logfiles */

View File

@@ -38,9 +38,17 @@
/* /*
* Queue file modes. * Queue file modes.
*
* 4.4BSD-like systems don't allow (sticky AND executable) together, so we use
* group read permission bits instead. These are more portable, but they
* also are more likely to be turned on by accident. It would not be the end
* of the world.
*/ */
#define MAIL_QUEUE_STAT_READY (S_IRUSR | S_IWUSR | S_IXUSR) #define MAIL_QUEUE_STAT_READY (S_IRUSR | S_IWUSR | S_IXUSR)
#define MAIL_QUEUE_STAT_CORRUPT (S_IRUSR) #define MAIL_QUEUE_STAT_CORRUPT (S_IRUSR)
#ifndef MAIL_QUEUE_STAT_UNTHROTTLE
#define MAIL_QUEUE_STAT_UNTHROTTLE (S_IRGRP)
#endif
extern struct VSTREAM *mail_queue_enter(const char *, mode_t, struct timeval *); extern struct VSTREAM *mail_queue_enter(const char *, mode_t, struct timeval *);
extern struct VSTREAM *mail_queue_open(const char *, const char *, int, mode_t); extern struct VSTREAM *mail_queue_open(const char *, const char *, int, mode_t);

View File

@@ -20,7 +20,7 @@
* Patches change both the patchlevel and the release date. Snapshots have no * Patches change both the patchlevel and the release date. Snapshots have no
* patchlevel; they change the release date only. * patchlevel; they change the release date only.
*/ */
#define MAIL_RELEASE_DATE "20061210" #define MAIL_RELEASE_DATE "20061217"
#define MAIL_VERSION_NUMBER "2.4" #define MAIL_VERSION_NUMBER "2.4"
#ifdef SNAPSHOT #ifdef SNAPSHOT

View File

@@ -367,6 +367,8 @@ static void qmgr_trigger_event(char *buf, int len,
* request in order. And as long as we don't have conflicting requests we * request in order. And as long as we don't have conflicting requests we
* are free to sort them into the most suitable order. * are free to sort them into the most suitable order.
*/ */
#define QMGR_FLUSH_BEFORE (QMGR_FLUSH_ONCE | QMGR_FLUSH_DFXP)
for (i = 0; i < len; i++) { for (i = 0; i < len; i++) {
if (msg_verbose) if (msg_verbose)
msg_info("request: %d (%c)", msg_info("request: %d (%c)",
@@ -380,8 +382,8 @@ static void qmgr_trigger_event(char *buf, int len,
deferred_flag |= QMGR_SCAN_START; deferred_flag |= QMGR_SCAN_START;
break; break;
case QMGR_REQ_FLUSH_DEAD: case QMGR_REQ_FLUSH_DEAD:
deferred_flag |= QMGR_FLUSH_DEAD; deferred_flag |= QMGR_FLUSH_BEFORE;
incoming_flag |= QMGR_FLUSH_DEAD; incoming_flag |= QMGR_FLUSH_BEFORE;
break; break;
case QMGR_REQ_SCAN_ALL: case QMGR_REQ_SCAN_ALL:
deferred_flag |= QMGR_SCAN_ALL; deferred_flag |= QMGR_SCAN_ALL;

View File

@@ -256,7 +256,7 @@ extern int qmgr_recipient_count;
extern void qmgr_message_free(QMGR_MESSAGE *); extern void qmgr_message_free(QMGR_MESSAGE *);
extern void qmgr_message_update_warn(QMGR_MESSAGE *); extern void qmgr_message_update_warn(QMGR_MESSAGE *);
extern void qmgr_message_kill_record(QMGR_MESSAGE *, long); extern void qmgr_message_kill_record(QMGR_MESSAGE *, long);
extern QMGR_MESSAGE *qmgr_message_alloc(const char *, const char *, int); extern QMGR_MESSAGE *qmgr_message_alloc(const char *, const char *, int, mode_t);
extern QMGR_MESSAGE *qmgr_message_realloc(QMGR_MESSAGE *); extern QMGR_MESSAGE *qmgr_message_realloc(QMGR_MESSAGE *);
#define QMGR_MSG_STATS(stats, message) \ #define QMGR_MSG_STATS(stats, message) \
@@ -317,7 +317,9 @@ struct QMGR_SCAN {
*/ */
#define QMGR_SCAN_START (1<<0) /* start now/restart when done */ #define QMGR_SCAN_START (1<<0) /* start now/restart when done */
#define QMGR_SCAN_ALL (1<<1) /* all queue file time stamps */ #define QMGR_SCAN_ALL (1<<1) /* all queue file time stamps */
#define QMGR_FLUSH_DEAD (1<<2) /* all sites, all transports */ #define QMGR_FLUSH_ONCE (1<<2) /* unthrottle once */
#define QMGR_FLUSH_DFXP (1<<3) /* override defer_transports */
#define QMGR_FLUSH_EACH (1<<4) /* unthrottle per message */
/* /*
* qmgr_scan.c * qmgr_scan.c

View File

@@ -226,8 +226,15 @@ int qmgr_active_feed(QMGR_SCAN *scan_info, const char *queue_id)
* being delivered. In that case (the file is locked), defer delivery by * being delivered. In that case (the file is locked), defer delivery by
* a minimal amount of time. * a minimal amount of time.
*/ */
#define QMGR_FLUSH_AFTER (QMGR_FLUSH_EACH | QMGR_FLUSH_DFXP)
if ((message = qmgr_message_alloc(MAIL_QUEUE_ACTIVE, queue_id, if ((message = qmgr_message_alloc(MAIL_QUEUE_ACTIVE, queue_id,
scan_info->flags)) == 0) { (st.st_mode & MAIL_QUEUE_STAT_UNTHROTTLE) ?
scan_info->flags | QMGR_FLUSH_AFTER :
scan_info->flags,
(st.st_mode & MAIL_QUEUE_STAT_UNTHROTTLE) ?
st.st_mode & ~MAIL_QUEUE_STAT_UNTHROTTLE :
0)) == 0) {
qmgr_active_corrupt(queue_id); qmgr_active_corrupt(queue_id);
return (0); return (0);
} else if (message == QMGR_MESSAGE_LOCKED) { } else if (message == QMGR_MESSAGE_LOCKED) {

View File

@@ -9,10 +9,11 @@
/* int qmgr_message_count; /* int qmgr_message_count;
/* int qmgr_recipient_count; /* int qmgr_recipient_count;
/* /*
/* QMGR_MESSAGE *qmgr_message_alloc(class, name, qflags) /* QMGR_MESSAGE *qmgr_message_alloc(class, name, qflags, mode)
/* const char *class; /* const char *class;
/* const char *name; /* const char *name;
/* int qflags; /* int qflags;
/* mode_t mode;
/* /*
/* QMGR_MESSAGE *qmgr_message_realloc(message) /* QMGR_MESSAGE *qmgr_message_realloc(message)
/* QMGR_MESSAGE *message; /* QMGR_MESSAGE *message;
@@ -49,6 +50,7 @@
/* run through the resolver, and are assigned to destination /* run through the resolver, and are assigned to destination
/* queues. Recipients that cannot be assigned are deferred or /* queues. Recipients that cannot be assigned are deferred or
/* bounced. Mail that has bounced twice is silently absorbed. /* bounced. Mail that has bounced twice is silently absorbed.
/* A non-zero mode means change the queue file permissions.
/* /*
/* qmgr_message_realloc() resumes reading recipients from the queue /* qmgr_message_realloc() resumes reading recipients from the queue
/* file, and updates the recipient list and \fIrcpt_offset\fR message /* file, and updates the recipient list and \fIrcpt_offset\fR message
@@ -929,9 +931,9 @@ static void qmgr_message_resolve(QMGR_MESSAGE *message)
for (recipient = list.info; recipient < list.info + list.len; recipient++) { for (recipient = list.info; recipient < list.info + list.len; recipient++) {
/* /*
* Redirect overrides all else. But only once (per entire * Redirect overrides all else. But only once (per entire message).
* message). For consistency with the remainder of Postfix, * For consistency with the remainder of Postfix, rewrite the address
* rewrite the address to canonical form before resolving it. * to canonical form before resolving it.
*/ */
if (message->redirect_addr) { if (message->redirect_addr) {
if (recipient > list.info) { if (recipient > list.info) {
@@ -1040,7 +1042,7 @@ static void qmgr_message_resolve(QMGR_MESSAGE *message)
* Optionally defer deliveries over specific transports, unless the * Optionally defer deliveries over specific transports, unless the
* restriction is lifted temporarily. * restriction is lifted temporarily.
*/ */
if (*var_defer_xports && (message->qflags & QMGR_FLUSH_DEAD) == 0) { if (*var_defer_xports && (message->qflags & QMGR_FLUSH_DFXP) == 0) {
if (defer_xport_argv == 0) if (defer_xport_argv == 0)
defer_xport_argv = argv_split(var_defer_xports, " \t\r\n,"); defer_xport_argv = argv_split(var_defer_xports, " \t\r\n,");
for (cpp = defer_xport_argv->argv; *cpp; cpp++) for (cpp = defer_xport_argv->argv; *cpp; cpp++)
@@ -1061,6 +1063,14 @@ static void qmgr_message_resolve(QMGR_MESSAGE *message)
queue = 0; queue = 0;
} }
/*
* This message is being flushed. If need-be unthrottle the
* transport.
*/
if ((message->qflags & QMGR_FLUSH_EACH) != 0
&& QMGR_TRANSPORT_THROTTLED(transport))
qmgr_transport_unthrottle(transport);
/* /*
* This transport is dead. Defer delivery to this recipient. * This transport is dead. Defer delivery to this recipient.
*/ */
@@ -1137,6 +1147,13 @@ static void qmgr_message_resolve(QMGR_MESSAGE *message)
STR(reply.nexthop)); STR(reply.nexthop));
} }
/*
* This message is being flushed. If need-be unthrottle the queue.
*/
if ((message->qflags & QMGR_FLUSH_EACH) != 0
&& QMGR_QUEUE_THROTTLED(queue))
qmgr_queue_unthrottle(queue);
/* /*
* This queue is dead. Defer delivery to this recipient. * This queue is dead. Defer delivery to this recipient.
*/ */
@@ -1242,7 +1259,7 @@ void qmgr_message_free(QMGR_MESSAGE *message)
/* qmgr_message_alloc - create in-core message structure */ /* qmgr_message_alloc - create in-core message structure */
QMGR_MESSAGE *qmgr_message_alloc(const char *queue_name, const char *queue_id, QMGR_MESSAGE *qmgr_message_alloc(const char *queue_name, const char *queue_id,
int qflags) int qflags, mode_t mode)
{ {
const char *myname = "qmgr_message_alloc"; const char *myname = "qmgr_message_alloc";
QMGR_MESSAGE *message; QMGR_MESSAGE *message;
@@ -1277,6 +1294,13 @@ QMGR_MESSAGE *qmgr_message_alloc(const char *queue_name, const char *queue_id,
return (0); return (0);
} else { } else {
/*
* We have validated the queue file content, so it is safe to modify
* the file properties now.
*/
if (mode != 0 && fchmod(vstream_fileno(message->fp), mode) < 0)
msg_fatal("fchmod %s: %m", VSTREAM_PATH(message->fp));
/* /*
* Reset the defer log. This code should not be here, but we must * Reset the defer log. This code should not be here, but we must
* reset the defer log *after* acquiring the exclusive lock on the * reset the defer log *after* acquiring the exclusive lock on the

View File

@@ -31,12 +31,17 @@
/* qmgr_scan_request() records a request for the next queue scan. The /* qmgr_scan_request() records a request for the next queue scan. The
/* flags argument is the bit-wise OR of zero or more of the following, /* flags argument is the bit-wise OR of zero or more of the following,
/* unrecognized flags being ignored: /* unrecognized flags being ignored:
/* .IP QMGR_FLUSH_DEAD /* .IP QMGR_FLUSH_ONCE
/* Forget state information about dead hosts or transports. This /* Forget state information about dead hosts or transports.
/* request takes effect upon the next queue scan. /* This request takes effect immediately.
/* .IP QMGR_FLUSH_DFXP
/* Override the defer_transports setting. This takes effect
/* immediately when a queue scan is in progress, and affects
/* the next queue scan.
/* .IP QMGR_SCAN_ALL /* .IP QMGR_SCAN_ALL
/* Ignore queue file time stamps. /* Ignore queue file time stamps. This takes effect immediately
/* This flag is passed on to the qmgr_active_feed() routine. /* when a queue scan is in progress, and affects the next queue
/* scan.
/* .IP QMGR_SCAN_START /* .IP QMGR_SCAN_START
/* Start a queue scan when none is in progress, or restart the /* Start a queue scan when none is in progress, or restart the
/* current scan upon completion. /* current scan upon completion.
@@ -94,12 +99,6 @@ static void qmgr_scan_start(QMGR_SCAN *scan_info)
scan_info->nflags & QMGR_SCAN_START ? "re" : "", scan_info->nflags & QMGR_SCAN_START ? "re" : "",
scan_info->queue); scan_info->queue);
/*
* Optionally forget all dead host information.
*/
if (scan_info->nflags & QMGR_FLUSH_DEAD)
qmgr_enable_all();
/* /*
* Start or restart the scan. * Start or restart the scan.
*/ */
@@ -113,6 +112,33 @@ static void qmgr_scan_start(QMGR_SCAN *scan_info)
void qmgr_scan_request(QMGR_SCAN *scan_info, int flags) void qmgr_scan_request(QMGR_SCAN *scan_info, int flags)
{ {
/*
* Apply "forget all dead destinations" requests immediately. Throttle
* dead transports and queues at the earliest opportunity: preferably
* during an already ongoing queue scan, otherwise the throttling will
* have to wait until a "start scan" trigger arrives.
*
* The QMGR_FLUSH_ONCE request always comes with QMGR_FLUSH_DFXP, and
* sometimes it also comes with QMGR_SCAN_ALL. It becomes a completely
* different story when a flush request is encoded in file permissions.
*/
if (flags & QMGR_FLUSH_ONCE)
qmgr_enable_all();
/*
* Apply "ignore time stamp" requests also towards the scan that is
* already in progress.
*/
if (scan_info->handle != 0 && (flags & QMGR_SCAN_ALL))
scan_info->flags |= QMGR_SCAN_ALL;
/*
* Apply "override defer_transports" requests also towards the scan that
* is already in progress.
*/
if (scan_info->handle != 0 && (flags & QMGR_FLUSH_DFXP))
scan_info->flags |= QMGR_FLUSH_DFXP;
/* /*
* If a scan is in progress, just record the request. * If a scan is in progress, just record the request.
*/ */

View File

@@ -381,18 +381,13 @@ static const char *check_myhostname(void)
/* /*
* If the local machine name is not in FQDN form, try to append the * If the local machine name is not in FQDN form, try to append the
* contents of $mydomain. * contents of $mydomain.
*
* XXX Do not complain when running as "postconf -d".
*/ */
name = get_hostname(); name = get_hostname();
if ((cmd_mode & SHOW_DEFS) == 0 && (dot = strchr(name, '.')) == 0) { if ((dot = strchr(name, '.')) == 0) {
if ((domain = mail_conf_lookup_eval(VAR_MYDOMAIN)) == 0) { if ((domain = mail_conf_lookup_eval(VAR_MYDOMAIN)) == 0)
msg_warn("My hostname %s is not a fully qualified name - set %s or %s in %s/main.cf", domain = DEF_MYDOMAIN;
name, VAR_MYHOSTNAME, VAR_MYDOMAIN, var_config_dir);
} else {
name = concatenate(name, ".", domain, (char *) 0); name = concatenate(name, ".", domain, (char *) 0);
} }
}
return (name); return (name);
} }
@@ -420,7 +415,7 @@ static const char *check_mydomainname(void)
if (var_myhostname == 0) if (var_myhostname == 0)
get_myhostname(); get_myhostname();
if ((dot = strchr(var_myhostname, '.')) == 0 || strchr(dot + 1, '.') == 0) if ((dot = strchr(var_myhostname, '.')) == 0 || strchr(dot + 1, '.') == 0)
return (var_myhostname); return (DEF_MYDOMAIN);
return (dot + 1); return (dot + 1);
} }

View File

@@ -68,6 +68,7 @@ postqueue.o: ../../include/mail_dict.h
postqueue.o: ../../include/mail_flush.h postqueue.o: ../../include/mail_flush.h
postqueue.o: ../../include/mail_params.h postqueue.o: ../../include/mail_params.h
postqueue.o: ../../include/mail_proto.h postqueue.o: ../../include/mail_proto.h
postqueue.o: ../../include/mail_queue.h
postqueue.o: ../../include/mail_run.h postqueue.o: ../../include/mail_run.h
postqueue.o: ../../include/mail_task.h postqueue.o: ../../include/mail_task.h
postqueue.o: ../../include/msg.h postqueue.o: ../../include/msg.h

View File

@@ -6,6 +6,8 @@
/* SYNOPSIS /* SYNOPSIS
/* \fBpostqueue\fR [\fB-v\fR] [\fB-c \fIconfig_dir\fR] \fB-f\fR /* \fBpostqueue\fR [\fB-v\fR] [\fB-c \fIconfig_dir\fR] \fB-f\fR
/* .br /* .br
/* \fBpostqueue\fR [\fB-v\fR] [\fB-c \fIconfig_dir\fR] \fB-i \fIqueue_id\fR
/* .br
/* \fBpostqueue\fR [\fB-v\fR] [\fB-c \fIconfig_dir\fR] \fB-p\fR /* \fBpostqueue\fR [\fB-v\fR] [\fB-c \fIconfig_dir\fR] \fB-p\fR
/* .br /* .br
/* \fBpostqueue\fR [\fB-v\fR] [\fB-c \fIconfig_dir\fR] \fB-s \fIsite\fR /* \fBpostqueue\fR [\fB-v\fR] [\fB-c \fIconfig_dir\fR] \fB-s \fIsite\fR
@@ -30,6 +32,10 @@
/* /*
/* Warning: flushing undeliverable mail frequently will result in /* Warning: flushing undeliverable mail frequently will result in
/* poor delivery performance of all other mail. /* poor delivery performance of all other mail.
/* .IP "\fB-i \fIqueue_id\fR"
/* Schedule immediate delivery of mail with the specified queue ID.
/* This feature uses the \fBflush\fR(8) server, and is available
/* with Postfix 2.4 and later.
/* .IP \fB-p\fR /* .IP \fB-p\fR
/* Produce a traditional sendmail-style queue listing. /* Produce a traditional sendmail-style queue listing.
/* This option implements the traditional \fBmailq\fR command, /* This option implements the traditional \fBmailq\fR command,
@@ -186,6 +192,7 @@
#include <mail_task.h> #include <mail_task.h>
#include <mail_run.h> #include <mail_run.h>
#include <mail_flush.h> #include <mail_flush.h>
#include <mail_queue.h>
#include <flush_clnt.h> #include <flush_clnt.h>
#include <smtp_stream.h> #include <smtp_stream.h>
#include <user_acl.h> #include <user_acl.h>
@@ -214,6 +221,7 @@
#define PQ_MODE_MAILQ_LIST 1 /* list mail queue */ #define PQ_MODE_MAILQ_LIST 1 /* list mail queue */
#define PQ_MODE_FLUSH_QUEUE 2 /* flush queue */ #define PQ_MODE_FLUSH_QUEUE 2 /* flush queue */
#define PQ_MODE_FLUSH_SITE 3 /* flush site */ #define PQ_MODE_FLUSH_SITE 3 /* flush site */
#define PQ_MODE_FLUSH_FILE 4 /* flush message */
/* /*
* Silly little macros (SLMs). * Silly little macros (SLMs).
@@ -345,7 +353,7 @@ static void flush_site(const char *site)
flush_init(); flush_init();
switch (status = flush_send(site)) { switch (status = flush_send_site(site)) {
case FLUSH_STAT_OK: case FLUSH_STAT_OK:
exit(0); exit(0);
case FLUSH_STAT_BAD: case FLUSH_STAT_BAD:
@@ -363,11 +371,46 @@ static void flush_site(const char *site)
} }
} }
/* flush_file - flush mail with specific queue ID */
static void flush_file(const char *queue_id)
{
int status;
const char *errstr;
uid_t uid = getuid();
if (uid != 0 && uid != var_owner_uid
&& (errstr = check_user_acl_byuid(var_flush_acl, uid)) != 0)
msg_fatal_status(EX_NOPERM,
"User %s(%ld) is not allowed to flush the mail queue",
errstr, (long) uid);
switch (status = flush_send_file(queue_id)) {
case FLUSH_STAT_OK:
exit(0);
case FLUSH_STAT_BAD:
msg_fatal_status(EX_USAGE, "Invalid request: \"%s\"", queue_id);
case FLUSH_STAT_FAIL:
msg_fatal_status(EX_UNAVAILABLE,
"Cannot flush mail queue - mail system is down");
default:
msg_fatal_status(EX_SOFTWARE,
"Unexpected flush server reply status %d", status);
}
}
/* unavailable - sanitize exit status from library run-time errors */
static void unavailable(void)
{
exit(EX_UNAVAILABLE);
}
/* usage - scream and die */ /* usage - scream and die */
static NORETURN usage(void) static NORETURN usage(void)
{ {
msg_fatal_status(EX_USAGE, "usage: postqueue -f | postqueue -p | postqueue -s site"); msg_fatal_status(EX_USAGE, "usage: postqueue -f | postqueue -i queueid | postqueue -p | postqueue -s site");
} }
/* main - the main program */ /* main - the main program */
@@ -380,6 +423,7 @@ int main(int argc, char **argv)
int fd; int fd;
int mode = PQ_MODE_DEFAULT; int mode = PQ_MODE_DEFAULT;
char *site_to_flush = 0; char *site_to_flush = 0;
char *id_to_flush = 0;
ARGV *import_env; ARGV *import_env;
int bad_site; int bad_site;
@@ -406,6 +450,7 @@ int main(int argc, char **argv)
if ((slash = strrchr(argv[0], '/')) != 0 && slash[1]) if ((slash = strrchr(argv[0], '/')) != 0 && slash[1])
argv[0] = slash + 1; argv[0] = slash + 1;
msg_vstream_init(argv[0], VSTREAM_ERR); msg_vstream_init(argv[0], VSTREAM_ERR);
msg_cleanup(unavailable);
msg_syslog_init(mail_task("postqueue"), LOG_PID, LOG_FACILITY); msg_syslog_init(mail_task("postqueue"), LOG_PID, LOG_FACILITY);
set_mail_conf_str(VAR_PROCNAME, var_procname = mystrdup(argv[0])); set_mail_conf_str(VAR_PROCNAME, var_procname = mystrdup(argv[0]));
@@ -415,7 +460,7 @@ int main(int argc, char **argv)
* mail configuration read routine. Don't do complex things until we have * mail configuration read routine. Don't do complex things until we have
* completed initializations. * completed initializations.
*/ */
while ((c = GETOPT(argc, argv, "c:fps:v")) > 0) { while ((c = GETOPT(argc, argv, "c:fi:ps:v")) > 0) {
switch (c) { switch (c) {
case 'c': /* non-default configuration */ case 'c': /* non-default configuration */
if (setenv(CONF_ENV_PATH, optarg, 1) < 0) if (setenv(CONF_ENV_PATH, optarg, 1) < 0)
@@ -426,6 +471,12 @@ int main(int argc, char **argv)
usage(); usage();
mode = PQ_MODE_FLUSH_QUEUE; mode = PQ_MODE_FLUSH_QUEUE;
break; break;
case 'i': /* flush queue file */
if (mode != PQ_MODE_DEFAULT)
usage();
mode = PQ_MODE_FLUSH_FILE;
id_to_flush = optarg;
break;
case 'p': /* traditional mailq */ case 'p': /* traditional mailq */
if (mode != PQ_MODE_DEFAULT) if (mode != PQ_MODE_DEFAULT)
usage(); usage();
@@ -489,6 +540,12 @@ int main(int argc, char **argv)
"Cannot flush mail queue - invalid destination: \"%.100s%s\"", "Cannot flush mail queue - invalid destination: \"%.100s%s\"",
site_to_flush, strlen(site_to_flush) > 100 ? "..." : ""); site_to_flush, strlen(site_to_flush) > 100 ? "..." : "");
} }
if (id_to_flush != 0) {
if (!mail_queue_id_ok(id_to_flush))
msg_fatal_status(EX_USAGE,
"Cannot flush queue ID - invalid name: \"%.100s%s\"",
id_to_flush, strlen(id_to_flush) > 100 ? "..." : "");
}
/* /*
* Start processing. * Start processing.
@@ -505,6 +562,10 @@ int main(int argc, char **argv)
flush_site(site_to_flush); flush_site(site_to_flush);
exit(0); exit(0);
break; break;
case PQ_MODE_FLUSH_FILE:
flush_file(id_to_flush);
exit(0);
break;
case PQ_MODE_FLUSH_QUEUE: case PQ_MODE_FLUSH_QUEUE:
flush_queue(); flush_queue();
exit(0); exit(0);

View File

@@ -427,6 +427,8 @@ static void qmgr_trigger_event(char *buf, int len,
* request in order. And as long as we don't have conflicting requests we * request in order. And as long as we don't have conflicting requests we
* are free to sort them into the most suitable order. * are free to sort them into the most suitable order.
*/ */
#define QMGR_FLUSH_BEFORE (QMGR_FLUSH_ONCE | QMGR_FLUSH_DFXP)
for (i = 0; i < len; i++) { for (i = 0; i < len; i++) {
if (msg_verbose) if (msg_verbose)
msg_info("request: %d (%c)", msg_info("request: %d (%c)",
@@ -440,8 +442,8 @@ static void qmgr_trigger_event(char *buf, int len,
deferred_flag |= QMGR_SCAN_START; deferred_flag |= QMGR_SCAN_START;
break; break;
case QMGR_REQ_FLUSH_DEAD: case QMGR_REQ_FLUSH_DEAD:
deferred_flag |= QMGR_FLUSH_DEAD; deferred_flag |= QMGR_FLUSH_BEFORE;
incoming_flag |= QMGR_FLUSH_DEAD; incoming_flag |= QMGR_FLUSH_BEFORE;
break; break;
case QMGR_REQ_SCAN_ALL: case QMGR_REQ_SCAN_ALL:
deferred_flag |= QMGR_SCAN_ALL; deferred_flag |= QMGR_SCAN_ALL;

View File

@@ -304,7 +304,7 @@ extern int qmgr_recipient_count;
extern void qmgr_message_free(QMGR_MESSAGE *); extern void qmgr_message_free(QMGR_MESSAGE *);
extern void qmgr_message_update_warn(QMGR_MESSAGE *); extern void qmgr_message_update_warn(QMGR_MESSAGE *);
extern void qmgr_message_kill_record(QMGR_MESSAGE *, long); extern void qmgr_message_kill_record(QMGR_MESSAGE *, long);
extern QMGR_MESSAGE *qmgr_message_alloc(const char *, const char *, int); extern QMGR_MESSAGE *qmgr_message_alloc(const char *, const char *, int, mode_t);
extern QMGR_MESSAGE *qmgr_message_realloc(QMGR_MESSAGE *); extern QMGR_MESSAGE *qmgr_message_realloc(QMGR_MESSAGE *);
#define QMGR_MSG_STATS(stats, message) \ #define QMGR_MSG_STATS(stats, message) \
@@ -423,7 +423,9 @@ struct QMGR_SCAN {
*/ */
#define QMGR_SCAN_START (1<<0) /* start now/restart when done */ #define QMGR_SCAN_START (1<<0) /* start now/restart when done */
#define QMGR_SCAN_ALL (1<<1) /* all queue file time stamps */ #define QMGR_SCAN_ALL (1<<1) /* all queue file time stamps */
#define QMGR_FLUSH_DEAD (1<<2) /* all sites, all transports */ #define QMGR_FLUSH_ONCE (1<<2) /* unthrottle once */
#define QMGR_FLUSH_DFXP (1<<3) /* override defer_transports */
#define QMGR_FLUSH_EACH (1<<4) /* unthrottle per message */
/* /*
* qmgr_scan.c * qmgr_scan.c

View File

@@ -226,8 +226,15 @@ int qmgr_active_feed(QMGR_SCAN *scan_info, const char *queue_id)
* being delivered. In that case (the file is locked), defer delivery by * being delivered. In that case (the file is locked), defer delivery by
* a minimal amount of time. * a minimal amount of time.
*/ */
#define QMGR_FLUSH_AFTER (QMGR_FLUSH_EACH | QMGR_FLUSH_DFXP)
if ((message = qmgr_message_alloc(MAIL_QUEUE_ACTIVE, queue_id, if ((message = qmgr_message_alloc(MAIL_QUEUE_ACTIVE, queue_id,
scan_info->flags)) == 0) { (st.st_mode & MAIL_QUEUE_STAT_UNTHROTTLE) ?
scan_info->flags | QMGR_FLUSH_AFTER :
scan_info->flags,
(st.st_mode & MAIL_QUEUE_STAT_UNTHROTTLE) ?
st.st_mode & ~MAIL_QUEUE_STAT_UNTHROTTLE :
0)) == 0) {
qmgr_active_corrupt(queue_id); qmgr_active_corrupt(queue_id);
return (0); return (0);
} else if (message == QMGR_MESSAGE_LOCKED) { } else if (message == QMGR_MESSAGE_LOCKED) {

View File

@@ -9,10 +9,11 @@
/* int qmgr_message_count; /* int qmgr_message_count;
/* int qmgr_recipient_count; /* int qmgr_recipient_count;
/* /*
/* QMGR_MESSAGE *qmgr_message_alloc(class, name, qflags) /* QMGR_MESSAGE *qmgr_message_alloc(class, name, qflags, mode)
/* const char *class; /* const char *class;
/* const char *name; /* const char *name;
/* int qflags; /* int qflags;
/* mode_t mode;
/* /*
/* QMGR_MESSAGE *qmgr_message_realloc(message) /* QMGR_MESSAGE *qmgr_message_realloc(message)
/* QMGR_MESSAGE *message; /* QMGR_MESSAGE *message;
@@ -49,6 +50,7 @@
/* run through the resolver, and are assigned to destination /* run through the resolver, and are assigned to destination
/* queues. Recipients that cannot be assigned are deferred or /* queues. Recipients that cannot be assigned are deferred or
/* bounced. Mail that has bounced twice is silently absorbed. /* bounced. Mail that has bounced twice is silently absorbed.
/* A non-zero mode means change the queue file permissions.
/* /*
/* qmgr_message_realloc() resumes reading recipients from the queue /* qmgr_message_realloc() resumes reading recipients from the queue
/* file, and updates the recipient list and \fIrcpt_offset\fR message /* file, and updates the recipient list and \fIrcpt_offset\fR message
@@ -752,7 +754,8 @@ static int qmgr_message_read(QMGR_MESSAGE *message)
/* /*
* Remember when we have read the last recipient batch. Note that we do * Remember when we have read the last recipient batch. Note that we do
* it here after reading as reading might have used considerable amount of time. * it here after reading as reading might have used considerable amount
* of time.
*/ */
message->refill_time = sane_time(); message->refill_time = sane_time();
@@ -985,16 +988,15 @@ static void qmgr_message_resolve(QMGR_MESSAGE *message)
for (recipient = list.info; recipient < list.info + list.len; recipient++) { for (recipient = list.info; recipient < list.info + list.len; recipient++) {
/* /*
* Redirect overrides all else. But only once (per entire * Redirect overrides all else. But only once (per entire message).
* message). For consistency with the remainder of Postfix, * For consistency with the remainder of Postfix, rewrite the address
* rewrite the address to canonical form before resolving it. * to canonical form before resolving it.
*/ */
if (message->redirect_addr) { if (message->redirect_addr) {
if (recipient > list.info) { if (recipient > list.info) {
recipient->u.queue = 0; recipient->u.queue = 0;
continue; continue;
} }
message->rcpt_offset = 0; message->rcpt_offset = 0;
message->rcpt_unread = 0; message->rcpt_unread = 0;
@@ -1099,7 +1101,7 @@ static void qmgr_message_resolve(QMGR_MESSAGE *message)
* Optionally defer deliveries over specific transports, unless the * Optionally defer deliveries over specific transports, unless the
* restriction is lifted temporarily. * restriction is lifted temporarily.
*/ */
if (*var_defer_xports && (message->qflags & QMGR_FLUSH_DEAD) == 0) { if (*var_defer_xports && (message->qflags & QMGR_FLUSH_DFXP) == 0) {
if (defer_xport_argv == 0) if (defer_xport_argv == 0)
defer_xport_argv = argv_split(var_defer_xports, " \t\r\n,"); defer_xport_argv = argv_split(var_defer_xports, " \t\r\n,");
for (cpp = defer_xport_argv->argv; *cpp; cpp++) for (cpp = defer_xport_argv->argv; *cpp; cpp++)
@@ -1120,6 +1122,14 @@ static void qmgr_message_resolve(QMGR_MESSAGE *message)
queue = 0; queue = 0;
} }
/*
* This message is being flushed. If need-be unthrottle the
* transport.
*/
if ((message->qflags & QMGR_FLUSH_EACH) != 0
&& QMGR_TRANSPORT_THROTTLED(transport))
qmgr_transport_unthrottle(transport);
/* /*
* This transport is dead. Defer delivery to this recipient. * This transport is dead. Defer delivery to this recipient.
*/ */
@@ -1196,6 +1206,13 @@ static void qmgr_message_resolve(QMGR_MESSAGE *message)
STR(reply.nexthop)); STR(reply.nexthop));
} }
/*
* This message is being flushed. If need-be unthrottle the queue.
*/
if ((message->qflags & QMGR_FLUSH_EACH) != 0
&& QMGR_QUEUE_THROTTLED(queue))
qmgr_queue_unthrottle(queue);
/* /*
* This queue is dead. Defer delivery to this recipient. * This queue is dead. Defer delivery to this recipient.
*/ */
@@ -1260,8 +1277,8 @@ static void qmgr_message_assign(QMGR_MESSAGE *message)
peer = qmgr_peer_obtain(job, queue); peer = qmgr_peer_obtain(job, queue);
/* /*
* Lookup old or instantiate new recipient entry. We try to reuse * Lookup old or instantiate new recipient entry. We try to reuse the
* the last existing entry whenever the recipient limit permits. * last existing entry whenever the recipient limit permits.
*/ */
entry = peer->entry_list.prev; entry = peer->entry_list.prev;
if (message->single_rcpt || entry == 0 if (message->single_rcpt || entry == 0
@@ -1363,7 +1380,7 @@ void qmgr_message_free(QMGR_MESSAGE *message)
/* qmgr_message_alloc - create in-core message structure */ /* qmgr_message_alloc - create in-core message structure */
QMGR_MESSAGE *qmgr_message_alloc(const char *queue_name, const char *queue_id, QMGR_MESSAGE *qmgr_message_alloc(const char *queue_name, const char *queue_id,
int qflags) int qflags, mode_t mode)
{ {
const char *myname = "qmgr_message_alloc"; const char *myname = "qmgr_message_alloc";
QMGR_MESSAGE *message; QMGR_MESSAGE *message;
@@ -1398,6 +1415,13 @@ QMGR_MESSAGE *qmgr_message_alloc(const char *queue_name, const char *queue_id,
return (0); return (0);
} else { } else {
/*
* We have validated the queue file content, so it is safe to modify
* the file properties now.
*/
if (mode != 0 && fchmod(vstream_fileno(message->fp), mode) < 0)
msg_fatal("fchmod %s: %m", VSTREAM_PATH(message->fp));
/* /*
* Reset the defer log. This code should not be here, but we must * Reset the defer log. This code should not be here, but we must
* reset the defer log *after* acquiring the exclusive lock on the * reset the defer log *after* acquiring the exclusive lock on the

View File

@@ -31,12 +31,17 @@
/* qmgr_scan_request() records a request for the next queue scan. The /* qmgr_scan_request() records a request for the next queue scan. The
/* flags argument is the bit-wise OR of zero or more of the following, /* flags argument is the bit-wise OR of zero or more of the following,
/* unrecognized flags being ignored: /* unrecognized flags being ignored:
/* .IP QMGR_FLUSH_DEAD /* .IP QMGR_FLUSH_ONCE
/* Forget state information about dead hosts or transports. This /* Forget state information about dead hosts or transports.
/* request takes effect upon the next queue scan. /* This request takes effect immediately.
/* .IP QMGR_FLUSH_DFXP
/* Override the defer_transports setting. This takes effect
/* immediately when a queue scan is in progress, and affects
/* the next queue scan.
/* .IP QMGR_SCAN_ALL /* .IP QMGR_SCAN_ALL
/* Ignore queue file time stamps. /* Ignore queue file time stamps. This takes effect immediately
/* This flag is passed on to the qmgr_active_feed() routine. /* when a queue scan is in progress, and affects the next queue
/* scan.
/* .IP QMGR_SCAN_START /* .IP QMGR_SCAN_START
/* Start a queue scan when none is in progress, or restart the /* Start a queue scan when none is in progress, or restart the
/* current scan upon completion. /* current scan upon completion.
@@ -94,12 +99,6 @@ static void qmgr_scan_start(QMGR_SCAN *scan_info)
scan_info->nflags & QMGR_SCAN_START ? "re" : "", scan_info->nflags & QMGR_SCAN_START ? "re" : "",
scan_info->queue); scan_info->queue);
/*
* Optionally forget all dead host information.
*/
if (scan_info->nflags & QMGR_FLUSH_DEAD)
qmgr_enable_all();
/* /*
* Start or restart the scan. * Start or restart the scan.
*/ */
@@ -113,6 +112,33 @@ static void qmgr_scan_start(QMGR_SCAN *scan_info)
void qmgr_scan_request(QMGR_SCAN *scan_info, int flags) void qmgr_scan_request(QMGR_SCAN *scan_info, int flags)
{ {
/*
* Apply "forget all dead destinations" requests immediately. Throttle
* dead transports and queues at the earliest opportunity: preferably
* during an already ongoing queue scan, otherwise the throttling will
* have to wait until a "start scan" trigger arrives.
*
* The QMGR_FLUSH_ONCE request always comes with QMGR_FLUSH_DFXP, and
* sometimes it also comes with QMGR_SCAN_ALL. It becomes a completely
* different story when a flush request is encoded in file permissions.
*/
if (flags & QMGR_FLUSH_ONCE)
qmgr_enable_all();
/*
* Apply "ignore time stamp" requests also towards the scan that is
* already in progress.
*/
if (scan_info->handle != 0 && (flags & QMGR_SCAN_ALL))
scan_info->flags |= QMGR_SCAN_ALL;
/*
* Apply "override defer_transports" requests also towards the scan that
* is already in progress.
*/
if (scan_info->handle != 0 && (flags & QMGR_FLUSH_DFXP))
scan_info->flags |= QMGR_FLUSH_DFXP;
/* /*
* If a scan is in progress, just record the request. * If a scan is in progress, just record the request.
*/ */

View File

@@ -168,6 +168,11 @@
/* .IP "\fB-q\fIinterval\fR (ignored)" /* .IP "\fB-q\fIinterval\fR (ignored)"
/* The interval between queue runs. Use the \fBqueue_run_delay\fR /* The interval between queue runs. Use the \fBqueue_run_delay\fR
/* configuration parameter instead. /* configuration parameter instead.
/* .IP \fB-qI\fIqueueid\fR
/* Schedule immediate delivery of mail with the specified queue
/* ID. This option is implemented by executing the
/* \fBpostqueue\fR(1) command, and is available with Postfix
/* version 2.4 and later.
/* .IP \fB-qR\fIsite\fR /* .IP \fB-qR\fIsite\fR
/* Schedule immediate delivery of all mail that is queued for the named /* Schedule immediate delivery of all mail that is queued for the named
/* \fIsite\fR. This option accepts only \fIsite\fR names that are /* \fIsite\fR. This option accepts only \fIsite\fR names that are
@@ -885,6 +890,13 @@ static void enqueue(const int flags, const char *encoding,
myfree(saved_sender); myfree(saved_sender);
} }
/* tempfail - sanitize exit status after library run-time error */
static void tempfail(void)
{
exit(EX_TEMPFAIL);
}
/* main - the main program */ /* main - the main program */
int main(int argc, char **argv) int main(int argc, char **argv)
@@ -902,6 +914,7 @@ int main(int argc, char **argv)
int n; int n;
int flags = SM_FLAG_DEFAULT; int flags = SM_FLAG_DEFAULT;
char *site_to_flush = 0; char *site_to_flush = 0;
char *id_to_flush = 0;
char *encoding = 0; char *encoding = 0;
char *qtime = 0; char *qtime = 0;
const char *errstr; const char *errstr;
@@ -952,6 +965,7 @@ int main(int argc, char **argv)
if ((slash = strrchr(argv[0], '/')) != 0 && slash[1]) if ((slash = strrchr(argv[0], '/')) != 0 && slash[1])
argv[0] = slash + 1; argv[0] = slash + 1;
msg_vstream_init(argv[0], VSTREAM_ERR); msg_vstream_init(argv[0], VSTREAM_ERR);
msg_cleanup(tempfail);
msg_syslog_init(mail_task("sendmail"), LOG_PID, LOG_FACILITY); msg_syslog_init(mail_task("sendmail"), LOG_PID, LOG_FACILITY);
set_mail_conf_str(VAR_PROCNAME, var_procname = mystrdup(argv[0])); set_mail_conf_str(VAR_PROCNAME, var_procname = mystrdup(argv[0]));
@@ -1175,6 +1189,10 @@ int main(int argc, char **argv)
site_to_flush = optarg + 1; site_to_flush = optarg + 1;
if (*site_to_flush == 0) if (*site_to_flush == 0)
msg_fatal_status(EX_USAGE, "specify: -qRsitename"); msg_fatal_status(EX_USAGE, "specify: -qRsitename");
} else if (optarg[0] == 'I') {
id_to_flush = optarg + 1;
if (*id_to_flush == 0)
msg_fatal_status(EX_USAGE, "specify: -qIqueueid");
} else { } else {
msg_fatal_status(EX_USAGE, "-q%c is not implemented", msg_fatal_status(EX_USAGE, "-q%c is not implemented",
optarg[0]); optarg[0]);
@@ -1200,6 +1218,9 @@ int main(int argc, char **argv)
if (site_to_flush && mode != SM_MODE_ENQUEUE) if (site_to_flush && mode != SM_MODE_ENQUEUE)
msg_fatal_status(EX_USAGE, "-qR can be used only in delivery mode"); msg_fatal_status(EX_USAGE, "-qR can be used only in delivery mode");
if (id_to_flush && mode != SM_MODE_ENQUEUE)
msg_fatal_status(EX_USAGE, "-qI can be used only in delivery mode");
if (flags & DEL_REQ_FLAG_USR_VRFY) { if (flags & DEL_REQ_FLAG_USR_VRFY) {
if (flags & SM_FLAG_XRCPT) if (flags & SM_FLAG_XRCPT)
msg_fatal_status(EX_USAGE, "-t option cannot be used with -bv"); msg_fatal_status(EX_USAGE, "-t option cannot be used with -bv");
@@ -1228,11 +1249,7 @@ int main(int argc, char **argv)
msg_panic("unknown operation mode: %d", mode); msg_panic("unknown operation mode: %d", mode);
/* NOTREACHED */ /* NOTREACHED */
case SM_MODE_ENQUEUE: case SM_MODE_ENQUEUE:
if (site_to_flush == 0) { if (site_to_flush) {
enqueue(flags, encoding, dsn_envid, dsn_notify,
rewrite_context, sender, full_name, argv + OPTIND);
exit(0);
}
if (argv[OPTIND]) if (argv[OPTIND])
msg_fatal_status(EX_USAGE, "flush site requires no recipient"); msg_fatal_status(EX_USAGE, "flush site requires no recipient");
ext_argv = argv_alloc(2); ext_argv = argv_alloc(2);
@@ -1242,6 +1259,22 @@ int main(int argc, char **argv)
argv_terminate(ext_argv); argv_terminate(ext_argv);
mail_run_replace(var_command_dir, ext_argv->argv); mail_run_replace(var_command_dir, ext_argv->argv);
/* NOTREACHED */ /* NOTREACHED */
} else if (id_to_flush) {
if (argv[OPTIND])
msg_fatal_status(EX_USAGE, "flush queue_id requires no recipient");
ext_argv = argv_alloc(2);
argv_add(ext_argv, "postqueue", "-i", id_to_flush, (char *) 0);
for (n = 0; n < msg_verbose; n++)
argv_add(ext_argv, "-v", (char *) 0);
argv_terminate(ext_argv);
mail_run_replace(var_command_dir, ext_argv->argv);
/* NOTREACHED */
} else {
enqueue(flags, encoding, dsn_envid, dsn_notify,
rewrite_context, sender, full_name, argv + OPTIND);
exit(0);
/* NOTREACHED */
}
break; break;
case SM_MODE_MAILQ: case SM_MODE_MAILQ:
if (argv[OPTIND]) if (argv[OPTIND])

View File

@@ -1708,6 +1708,11 @@ static int mail_open_stream(SMTPD_STATE *state)
MAIL_ATTR_ACT_HELO_NAME, state->helo_name); MAIL_ATTR_ACT_HELO_NAME, state->helo_name);
rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%u", rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%u",
MAIL_ATTR_ACT_CLIENT_AF, state->addr_family); MAIL_ATTR_ACT_CLIENT_AF, state->addr_family);
/*
* Don't send client certificate down the pipeline unless it is
* a) verified or b) just a fingerprint.
*/
} }
if (state->verp_delims) if (state->verp_delims)
rec_fputs(state->cleanup, REC_TYPE_VERP, state->verp_delims); rec_fputs(state->cleanup, REC_TYPE_VERP, state->verp_delims);
@@ -2994,7 +2999,7 @@ static int etrn_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
smtpd_chat_reply(state, "%s", err); smtpd_chat_reply(state, "%s", err);
return (-1); return (-1);
} }
switch (flush_send(argv[1].strval)) { switch (flush_send_site(argv[1].strval)) {
case FLUSH_STAT_OK: case FLUSH_STAT_OK:
smtpd_chat_reply(state, "250 Queuing started"); smtpd_chat_reply(state, "250 Queuing started");
return (0); return (0);

View File

@@ -1221,8 +1221,12 @@ static int permit_tls_clientcerts(SMTPD_STATE *state, int permit_all_certs)
msg_info("Relaying allowed for all verified client certificates"); msg_info("Relaying allowed for all verified client certificates");
return (SMTPD_CHECK_OK); return (SMTPD_CHECK_OK);
} }
if (state->tls_context->peer_verified
&& state->tls_context->peer_fingerprint) { /*
* When directly checking the fingerprint, it is OK if the issuing CA is
* not trusted.
*/
if (state->tls_context->peer_fingerprint) {
found = maps_find(relay_ccerts, state->tls_context->peer_fingerprint, found = maps_find(relay_ccerts, state->tls_context->peer_fingerprint,
DICT_FLAG_NONE); DICT_FLAG_NONE);
if (found) { if (found) {
@@ -2578,8 +2582,11 @@ static int check_ccert_access(SMTPD_STATE *state, const char *table,
if (!state->tls_context) if (!state->tls_context)
return SMTPD_CHECK_DUNNO; return SMTPD_CHECK_DUNNO;
if (state->tls_context->peer_verified /*
&& state->tls_context->peer_fingerprint) { * When directly checking the fingerprint, it is OK if the issuing CA is
* not trusted.
*/
if (state->tls_context->peer_fingerprint) {
if (msg_verbose) if (msg_verbose)
msg_info("%s: %s", myname, state->tls_context->peer_fingerprint); msg_info("%s: %s", myname, state->tls_context->peer_fingerprint);
@@ -3335,11 +3342,18 @@ static int check_policy_service(SMTPD_STATE *state, const char *server,
#define IF_VERIFIED(x) \ #define IF_VERIFIED(x) \
((state->tls_context && \ ((state->tls_context && \
state->tls_context->peer_verified && ((x) != 0)) ? (x) : "") state->tls_context->peer_verified && ((x) != 0)) ? (x) : "")
ATTR_TYPE_STR, MAIL_ATTR_CCERT_SUBJECT, subject,
ATTR_TYPE_STR, MAIL_ATTR_CCERT_ISSUER, issuer,
ATTR_TYPE_STR, MAIL_ATTR_CCERT_FINGERPRINT,
IF_VERIFIED(state->tls_context->peer_fingerprint),
#define IF_ENCRYPTED(x, y) ((state->tls_context && ((x) != 0)) ? (x) : (y)) #define IF_ENCRYPTED(x, y) ((state->tls_context && ((x) != 0)) ? (x) : (y))
ATTR_TYPE_STR, MAIL_ATTR_CCERT_SUBJECT,
IF_VERIFIED(subject),
ATTR_TYPE_STR, MAIL_ATTR_CCERT_ISSUER,
IF_VERIFIED(issuer),
/*
* When directly checking the fingerprint, it is OK if the issuing CA is
* not trusted.
*/
ATTR_TYPE_STR, MAIL_ATTR_CCERT_FINGERPRINT,
IF_ENCRYPTED(state->tls_context->peer_fingerprint, ""),
ATTR_TYPE_STR, MAIL_ATTR_CRYPTO_PROTOCOL, ATTR_TYPE_STR, MAIL_ATTR_CRYPTO_PROTOCOL,
IF_ENCRYPTED(state->tls_context->protocol, ""), IF_ENCRYPTED(state->tls_context->protocol, ""),
ATTR_TYPE_STR, MAIL_ATTR_CRYPTO_CIPHER, ATTR_TYPE_STR, MAIL_ATTR_CRYPTO_CIPHER,