-TBOUNCE_INFO
-TBOUNCE_LOG
-TBOUNCE_LOG_DSN_BUF
+-TBOUNCE_LOG_FORGE
-TBOUNCE_LOG_RCPT_BUF
-TBOUNCE_STAT
-TBOUNCE_TEMPLATE
-TDSN
-TDSN_BUF
-TDSN_SPLIT
--TDSN_VAR
+-TDSN_STAT
-TEXPAND_ATTR
-TFILE
-TFORWARD_INFO
-TRCPT_BUF
-TRECIPIENT
-TRECIPIENT_LIST
--TRECIPIENT_VAR
-TREC_TYPE_NAME
-TRESOLVE_REPLY
-TRESPONSE
20051229
+ The following workaround was removed 20060103.
+
Workaround: when mail is still queued after 3000 seconds,
the SMTP client no longer pipelines the DOT+QUIT commands.
The 20050929 paranoia about malformed server replies
The administrator still has the option to turn off pipelining
by hand if loss of mail is a concern.
+20060103
+
+ Bugfix: the 20051217 fix (when a SASL client password is
+ found, defer delivery when no server-announced mechanism
+ survives the smtp_sasl_mechanism_filter) did the mechanism
+ test too early, so that it could trip up with deliveries
+ to servers that we don't have a SASL password for. Files:
+ smtp/smtp_sasl_proto.c, smtp/smtp_proto.c.
+
+20060104
+
+ Safety: new "smtp_cname_overrides_servername" parameter.
+ The default value ("no") is NOT backwards compatible. This
+ avoids surprises with the hostname that is used for logging,
+ SASL password lookup, TLS policy decisions, or TLS certificate
+ verification. The change makes the 20051221 behavior more
+ configurable. Files: smtp/smtp_addr.c, smtp/smtp_connect.c,
+ proto/postconf.proto.
+
+20060105
+
+ Cleanup: removed the unused DSN "code" attribute; removed
+ surrogate SMTP replies for errors that were not reported
+ by a remote SMTP server, making several DSN-related functions
+ and macros redundant; cleaned up some bizarre code for DSN
+ attribute memory management in the SMTP client.
+
+20060106
+
+ Cleanup: eliminated the global smtp_errno variable, which
+ had become redundant after introducing DSN support. Files:
+ smtp/smtp_addr.c, smtp/smtp_connect.c.
+
+20060107
+
+ Cleanup: removed more bizarre code for DSN attribute memory
+ management in the queue manager, bounce server, and in
+ delivery agents.
+
+20060109
+
+ Bugfix: smtp_sasl_tls_opts was unimplemented. File:
+ smtp/smtp_sasl_proto.c.
+
+ Cleanup: more bounce logfile code cleanup. Files:
+ global/bounce_log.c, bounce/bounce_notify_util.c,
+ bounce/bounce.c, bounce/bounce_notify_verp.c,
+ bounce/bounce_one_service.c, showq/showq.c
+
+20060110
+
+ Cleanup: more bounce logfile code cleanup. Files:
+ global/bounce_log.c, bounce/bounce_notify_util.c.
+
+ Bugfix: the VERP bouncer never handled the case of a missing
+ bounce logfile. Found while doing more logfile code cleanup.
+ File: bounce/bounce_notify_verp.c.
+
+ Feature: smtp_sasl_tls_verified_security_options for
+ connections where the server certificate passed verification.
+ The default value is $smtp_sasl_tls_security_options, which
+ in turn defaults to $smtp_sasl_security_options.
+
+20060111
+
+ Optimization: mystrdup() and mystrndup() now return a pointer
+ to a fixed read-only memory location instead of allocating
+ memory for zero-length null-terminated strings. This saves
+ lots of memory for unused recipient attributes. If this
+ change causes problems (for example, you have an ancient
+ sscanf() implementation that writes to its input) then
+ compile Postfix with -DNO_SHARED_EMPTY_STRINGS.
+
+ Cleanup: eliminated null pointer members in DSN structures.
+ Instead we now use the optimized mystrdup() for empty
+ strings. For safety sake we keep the tests for null pointers
+ in input, but we always produce empty strings on output.
+ Files: global/dsn.c, global/dsn.h, global/dsn_buf.h,
+ global/dsn_print.c.
+
+ Cleanup: eliminated ad-hoc code for passing recipients in
+ the queue manager delivery request protocol. Postfix now
+ uses proper object activation/passivation instead. Files:
+ *qmgr/qmgr_deliver.c, global/deliver_request.c,
+ global/deliver_pass.c.
+
+20060112
+
+ Feature: to simplify debugging the bounce server logs the
+ old and new queue ID when notifying the sender or postmaster.
+ Files: global/post_mail.c, bounce/bounce_notify_service.c,
+ bounce/bounce_one_service.c, bounce/bounce_notify_verp.c,
+ bounce/bounce_warn_service.c, bounce/bounce_trace_service.c.
+
Open problems:
+ Privacy: remove local command/pathname details from remote
+ delivery status reports, and log them via local msg_warn().
+
+ Remove defer(8) and trace(8) references and man pages. These
+ are services not program names.
+
"postsuper -r" no longer resets the message arrival time,
because pickup(8) no longer overrides queue file time stamp
information. This can be a problem when mail "on hold" is
Network -> smtpd(8) <-> anvil(8)
- * The bounce(8), defer(8) and trace(8) servers each maintain their own queue
- directory trees with per-message logfiles. This information is used to send
- delivery or non-delivery notifications to the sender.
+ * The bounce(8) server implements the bounce, defer and trace services, which
+ maintain separate directory trees with per-message logfiles. This
+ information is used to send delivery or non-delivery notifications to the
+ sender.
- The trace(8) service implements support for the Postfix "sendmail -bv" and
+ The trace service implements support for the Postfix "sendmail -bv" and
"sendmail -v" commands which produce reports about how Postfix delivers
mail, and is available with Postfix version 2.1 and later. See DEBUG_README
for examples.
| v v
(Non-) bounce(8) Queue id,
- delivery <- defer(8) <- recipient,
- notice trace(8) status
+ delivery <- defer <- recipient,
+ notice trace status
^ |
| v
If you upgrade from Postfix 2.1 or earlier, read RELEASE_NOTES-2.2
before proceeding.
+Incompatibility with snapshot 20060112
+======================================
+
+The queue manager delivery request protocol has changed. You must
+reload Postfix when upgrading. If you omit this step, delivery
+agents complain with "warning: unexpected attribute original_recipient"
+and mail will not be delivered.
+
+The Postfix SMTP/LMTP client by default no longer allows DNS CNAME
+records to override the server hostname that is used for logging,
+SASL password lookup, TLS policy selection and TLS server certificate
+verification. Specify "smtp_cname_overrides_servername = no" to get
+the old behavior.
+
+Postfix DSN reports no longer make up their own surrogate SMTP
+replies for errors that were not reported by a remote SMTP server.
+
Incompatibility with snapshot 20060103
======================================
appear in the delayed mail notification text.
Once the result is satisfactory, copy the template to the
- Postfix configuration directory and specify in main.cf
+ Postfix configuration directory and specify in <a href="postconf.5.html">main.cf</a>
something like:
- /etc/postfix/main.cf:
+ /etc/postfix/<a href="postconf.5.html">main.cf</a>:
<a href="postconf.5.html#bounce_template_file">bounce_template_file</a> = /etc/postfix/bounce.cf
<b>TEMPLATE FILE FORMAT</b>
with the shell or with Perl (<i>template</i><b>_</b><i>name</i> <b>=</b> <<<b>'EOF'</b>).
Here is an example:
- # The failure template is used for undeliverable mail.
+ # The failure template is used for undeliverable mail.
- failure_template = <<EOF
- Charset: us-ascii
- From: MAILER-DAEMON (Mail Delivery System)
- Subject: Undelivered Mail Returned to Sender
- Postmaster-Subject: Postmaster Copy: Undelivered Mail
+ failure_template = <<EOF
+ Charset: us-ascii
+ From: MAILER-DAEMON (Mail Delivery System)
+ Subject: Undelivered Mail Returned to Sender
+ Postmaster-Subject: Postmaster Copy: Undelivered Mail
- This is the $<a href="postconf.5.html#mail_name">mail_name</a> program at host $<a href="postconf.5.html#myhostname">myhostname</a>.
+ This is the $<a href="postconf.5.html#mail_name">mail_name</a> program at host $<a href="postconf.5.html#myhostname">myhostname</a>.
- I'm sorry to have to inform you that your message could not
- be delivered to one or more recipients. It's attached below.
+ I'm sorry to have to inform you that your message could not
+ be delivered to one or more recipients. It's attached below.
- For further assistance, please send mail to <postmaster>
+ For further assistance, please send mail to <postmaster>
- If you do so, please include this problem report. You can
- delete your own text from the attached returned message.
+ If you do so, please include this problem report. You can
+ delete your own text from the attached returned message.
- The $<a href="postconf.5.html#mail_name">mail_name</a> program
- EOF
+ The $<a href="postconf.5.html#mail_name">mail_name</a> program
+ EOF
- No special meaning is given to the backslash character or
- to leading whitespace; these are always taken literally.
+ The usage and specification of bounce templates is subject
+ to the following restrictions:
- Outside the << context, lines beginning with "#" are
- ignored, as are empty lines, and lines consisting of
- whitespace only.
+ <b>o</b> No special meaning is given to the backslash char-
+ acter or to leading whitespace; these are always
+ taken literally.
+
+ <b>o</b> Inside the << context, the "$" character is spe-
+ cial. To produce a "$" character as output, specify
+ "$$".
+
+ <b>o</b> Outside the << context, lines beginning with "#"
+ are ignored, as are empty lines, and lines consist-
+ ing of whitespace only.
Examples of all templates can be found in the file
<b>bounce.cf.default</b> in the Postfix configuration directory.
<b>o</b> Template message headers must not span multiple
lines.
- <b>o</b> Template message headers must not contain main.cf
+ <b>o</b> Template message headers must not contain <a href="postconf.5.html">main.cf</a>
$parameters.
<b>o</b> Template message headers must contain ASCII charac-
<b>TEMPLATE MESSAGE TEXT FORMAT</b>
The second portion of a bounce template consists of mes-
sage text. As the above example shows, template message
- text may contain main.cf $parameters. Besides the parame-
- ters that are defined in main.cf, the following parameters
+ text may contain <a href="postconf.5.html">main.cf</a> $parameters. Besides the parame-
+ ters that are defined in <a href="postconf.5.html">main.cf</a>, the following parameters
are treated specially depending on the suffix that is
appended to their name.
delivery status information. Each log file is named after
the queue file that it corresponds to, and is kept in a
queue subdirectory named after the service name in the
- <b>master.cf</b> file (either <b>bounce</b>, <b>defer</b> or <b>trace</b>). This pro-
+ <a href="master.5.html"><b>master.cf</b></a> file (either <b>bounce</b>, <b>defer</b> or <b>trace</b>). This pro-
gram expects to be run from the <a href="master.8.html"><b>master</b>(8)</a> process manager.
The <a href="bounce.8.html"><b>bounce</b>(8)</a> daemon processes two types of service
Problems and transactions are logged to <b>syslogd</b>(8).
<b>CONFIGURATION PARAMETERS</b>
- Changes to <b>main.cf</b> are picked up automatically, as
+ Changes to <a href="postconf.5.html"><b>main.cf</b></a> are picked up automatically, as
<a href="bounce.8.html"><b>bounce</b>(8)</a> processes run for only a limited amount of time.
Use the command "<b>postfix reload</b>" to speed up a change.
sage templates.
<b><a href="postconf.5.html#config_directory">config_directory</a> (see 'postconf -d' output)</b>
- The default location of the Postfix main.cf and
- master.cf configuration files.
+ The default location of the Postfix <a href="postconf.5.html">main.cf</a> and
+ <a href="master.5.html">master.cf</a> configuration files.
<b><a href="postconf.5.html#daemon_timeout">daemon_timeout</a> (18000s)</b>
How much time a Postfix daemon process may take to
ble.
<b>CONFIGURATION PARAMETERS</b>
- Changes to <b>main.cf</b> are picked up automatically as <a href="discard.8.html"><b>dis-</b></a>
+ Changes to <a href="postconf.5.html"><b>main.cf</b></a> are picked up automatically as <a href="discard.8.html"><b>dis-</b></a>
<a href="discard.8.html"><b>card</b>(8)</a> processes run for only a limited amount of time.
Use the command "<b>postfix reload</b>" to speed up a change.
<a href="postconf.5.html"><b>postconf</b>(5)</a> for more details including examples.
<b><a href="postconf.5.html#config_directory">config_directory</a> (see 'postconf -d' output)</b>
- The default location of the Postfix main.cf and
- master.cf configuration files.
+ The default location of the Postfix <a href="postconf.5.html">main.cf</a> and
+ <a href="master.5.html">master.cf</a> configuration files.
<b><a href="postconf.5.html#daemon_timeout">daemon_timeout</a> (18000s)</b>
How much time a Postfix daemon process may take to
ble.
<b>CONFIGURATION PARAMETERS</b>
- Changes to <b>main.cf</b> are picked up automatically as <a href="error.8.html"><b>error</b>(8)</a>
+ Changes to <a href="postconf.5.html"><b>main.cf</b></a> are picked up automatically as <a href="error.8.html"><b>error</b>(8)</a>
processes run for only a limited amount of time. Use the
command "<b>postfix reload</b>" to speed up a change.
mail that Postfix did not receive.
<b><a href="postconf.5.html#config_directory">config_directory</a> (see 'postconf -d' output)</b>
- The default location of the Postfix main.cf and
- master.cf configuration files.
+ The default location of the Postfix <a href="postconf.5.html">main.cf</a> and
+ <a href="master.5.html">master.cf</a> configuration files.
<b><a href="postconf.5.html#daemon_timeout">daemon_timeout</a> (18000s)</b>
How much time a Postfix daemon process may take to
<b>SINGLE-RECIPIENT DELIVERY</b>
Some external commands cannot handle more than one recipi-
ent per delivery request. Examples of such transports are
- pagers, fax machines, and so on.
+ pagers or fax machines.
To prevent Postfix from sending multiple recipients per
delivery request, specify
<i>transport</i><b>_destination_recipient_limit = 1</b>
- in the Postfix <b>main.cf</b> file, where <i>transport</i> is the name
- in the first column of the Postfix <b>master.cf</b> entry for the
+ in the Postfix <a href="postconf.5.html"><b>main.cf</b></a> file, where <i>transport</i> is the name
+ in the first column of the Postfix <a href="master.5.html"><b>master.cf</b></a> entry for the
pipe-based delivery transport.
<b>COMMAND ATTRIBUTE SYNTAX</b>
- The external command attributes are given in the <b>master.cf</b>
+ The external command attributes are given in the <a href="master.5.html"><b>master.cf</b></a>
file at the end of a service definition. The syntax is as
follows:
fore security sensitive.
<b>CONFIGURATION PARAMETERS</b>
- Changes to <b>main.cf</b> are picked up automatically as <a href="pipe.8.html"><b>pipe</b>(8)</a>
+ Changes to <a href="postconf.5.html"><b>main.cf</b></a> are picked up automatically as <a href="pipe.8.html"><b>pipe</b>(8)</a>
processes run for only a limited amount of time. Use the
command "<b>postfix reload</b>" to speed up a change.
<b>MISCELLANEOUS CONTROLS</b>
<b><a href="postconf.5.html#config_directory">config_directory</a> (see 'postconf -d' output)</b>
- The default location of the Postfix main.cf and
- master.cf configuration files.
+ The default location of the Postfix <a href="postconf.5.html">main.cf</a> and
+ <a href="master.5.html">master.cf</a> configuration files.
<b><a href="postconf.5.html#daemon_timeout">daemon_timeout</a> (18000s)</b>
How much time a Postfix daemon process may take to
</p>
+</DD>
+
+<DT><b><a name="lmtp_cname_overrides_servername">lmtp_cname_overrides_servername</a>
+(default: yes)</b></DT><DD>
+
+<p> The LMTP-specific version of the <a href="postconf.5.html#smtp_cname_overrides_servername">smtp_cname_overrides_servername</a>
+configuration parameter. See there for details. </p>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
+
</DD>
<DT><b><a name="lmtp_connect_timeout">lmtp_connect_timeout</a>
</DD>
<DT><b><a name="lmtp_sasl_tls_security_options">lmtp_sasl_tls_security_options</a>
-(default: $var_lmtp_sasl_opts)</b></DT><DD>
+(default: $<a href="postconf.5.html#lmtp_sasl_security_options">lmtp_sasl_security_options</a>)</b></DT><DD>
<p> The LMTP-specific version of the <a href="postconf.5.html#smtp_sasl_tls_security_options">smtp_sasl_tls_security_options</a>
configuration parameter. See there for details. </p>
<p> This feature is available in Postfix 2.3 and later. </p>
+</DD>
+
+<DT><b><a name="lmtp_sasl_tls_verified_security_options">lmtp_sasl_tls_verified_security_options</a>
+(default: $<a href="postconf.5.html#lmtp_sasl_tls_security_options">lmtp_sasl_tls_security_options</a>)</b></DT><DD>
+
+<p> The LMTP-specific version of the
+<a href="postconf.5.html#smtp_sasl_tls_verified_security_options">smtp_sasl_tls_verified_security_options</a> configuration parameter.
+See there for details. </p>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
+
</DD>
<DT><b><a name="lmtp_sasl_type">lmtp_sasl_type</a>
but this form is not recommended here. </p>
+</DD>
+
+<DT><b><a name="smtp_cname_overrides_servername">smtp_cname_overrides_servername</a>
+(default: version dependent)</b></DT><DD>
+
+<p> Allow DNS CNAME records to override the servername that the
+Postfix SMTP client uses for logging, SASL password lookup, TLS
+policy decisions, or TLS certificate verification. The value "no"
+hardens Postfix <a href="postconf.5.html#smtp_tls_per_site">smtp_tls_per_site</a> hostname-based policies against
+false hostname information in DNS CNAME records, and makes SASL
+password file lookups more predictable. This is the default setting
+as of Postfix 2.3. </p>
+
+<p> This feature is available in Postfix 2.2.9 and later. </p>
+
+
</DD>
<DT><b><a name="smtp_connect_timeout">smtp_connect_timeout</a>
client uses for TLS encrypted SMTP sessions. </p>
+</DD>
+
+<DT><b><a name="smtp_sasl_tls_verified_security_options">smtp_sasl_tls_verified_security_options</a>
+(default: $<a href="postconf.5.html#smtp_sasl_tls_security_options">smtp_sasl_tls_security_options</a>)</b></DT><DD>
+
+<p> The SASL authentication security options that the Postfix SMTP
+client uses for TLS encrypted SMTP sessions with a verified server
+certificate. </p>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
+
</DD>
<DT><b><a name="smtp_sasl_type">smtp_sasl_type</a>
</dl>
-<p> Special hint for enforcement mode: since no secure DNS lookup
-mechanism is available, the recommended setup is: specify local
-<a href="transport.5.html">transport(5)</a> table entries for sensitive domains with explicit
-<a href="smtp.8.html">smtp</a>:[mailhost] destinations (since you can assure security of this
-table unlike DNS), then specify MUST for these mail hosts in the
-<a href="postconf.5.html#smtp_tls_per_site">smtp_tls_per_site</a> table. </p>
+<p> Special hints for enforcement mode: since no secure DNS lookup
+mechanism is available, the recommended setup is: </p>
+
+<dl>
+
+<dt> Postfix 2.2.9 </dt>
+
+<dd> <ul>
+
+<li> Specify "<a href="postconf.5.html#smtp_cname_overrides_servername">smtp_cname_overrides_servername</a> = no". This avoids
+false hostname information in DNS CNAME records that could bypass
+a hostname-based TLS usage policy.
+
+<li> Specify local <a href="transport.5.html">transport(5)</a> table entries for sensitive domains
+with explicit <a href="smtp.8.html">smtp</a>:[mailhost] destinations. This avoids false
+hostname information in DNS MX records that could bypass a
+hostname-based TLS usage policy.
+
+<li> Specify MUST for these mail hosts in the <a href="postconf.5.html#smtp_tls_per_site">smtp_tls_per_site</a>
+table.
+
+</ul> </dd>
+
+<dt> Postfix < 2.2.9 </dt>
+
+<dd> <ul>
+
+<li> Specify local <a href="transport.5.html">transport(5)</a> table entries for sensitive domains
+with explicit <a href="smtp.8.html">smtp</a>:[mailhost] destinations. This avoids false
+hostname information in DNS MX records that could bypass a
+hostname-based TLS usage policy, but cannot avoid false hostname
+information in DNS CNAME records.
+
+<li> Specify MUST for these mail hosts in the <a href="postconf.5.html#smtp_tls_per_site">smtp_tls_per_site</a>
+table.
+
+</ul> </dd>
+
+</dl>
+
+<p> </p>
</DD>
the <b>maildrop</b> directory.
<b>CONFIGURATION PARAMETERS</b>
- Changes to <b>main.cf</b> are picked up automatically as <a href="showq.8.html"><b>showq</b>(8)</a>
+ Changes to <a href="postconf.5.html"><b>main.cf</b></a> are picked up automatically as <a href="showq.8.html"><b>showq</b>(8)</a>
processes run for only a limited amount of time. Use the
command "<b>postfix reload</b>" to speed up a change.
<a href="postconf.5.html"><b>postconf</b>(5)</a> for more details including examples.
<b><a href="postconf.5.html#config_directory">config_directory</a> (see 'postconf -d' output)</b>
- The default location of the Postfix main.cf and
- master.cf configuration files.
+ The default location of the Postfix <a href="postconf.5.html">main.cf</a> and
+ <a href="master.5.html">master.cf</a> configuration files.
<b><a href="postconf.5.html#daemon_timeout">daemon_timeout</a> (18000s)</b>
How much time a Postfix daemon process may take to
<a href="http://www.faqs.org/rfcs/rfc1652.html">RFC 1652</a> (8bit-MIME transport)
<a href="http://www.faqs.org/rfcs/rfc1870.html">RFC 1870</a> (Message Size Declaration)
<a href="http://www.faqs.org/rfcs/rfc2033.html">RFC 2033</a> (LMTP protocol)
- <a href="http://www.faqs.org/rfcs/rfc2034.html">RFC 2034</a> (Enhanced Status Codes)
<a href="http://www.faqs.org/rfcs/rfc2045.html">RFC 2045</a> (MIME: Format of Internet Message Bodies)
<a href="http://www.faqs.org/rfcs/rfc2046.html">RFC 2046</a> (MIME: Media Types)
<a href="http://www.faqs.org/rfcs/rfc2554.html">RFC 2554</a> (AUTH command)
locally valid address into a globally valid address
when sending mail across the Internet.
+ Available in Postfix version 2.2.9 and later:
+
+ <b><a href="postconf.5.html#smtp_cname_overrides_servername">smtp_cname_overrides_servername</a> (version dependent)</b>
+ Allow DNS CNAME records to override the servername
+ that the Postfix SMTP client uses for logging, SASL
+ password lookup, TLS policy decisions, or TLS cer-
+ tificate verification.
+
Available in Postfix version 2.3 and later:
<b><a href="postconf.5.html#lmtp_discard_lhlo_keyword_address_maps">lmtp_discard_lhlo_keyword_address_maps</a> (empty)</b>
- Lookup tables, indexed by the remote LMTP server
- address, with case insensitive lists of LHLO key-
- words (pipelining, starttls, auth, etc.) that the
+ Lookup tables, indexed by the remote LMTP server
+ address, with case insensitive lists of LHLO key-
+ words (pipelining, starttls, auth, etc.) that the
LMTP client will ignore in the LHLO response from a
remote LMTP server.
<b><a href="postconf.5.html#lmtp_discard_lhlo_keywords">lmtp_discard_lhlo_keywords</a> ($<a href="postconf.5.html#myhostname">myhostname</a>)</b>
- A case insensitive list of LHLO keywords (pipelin-
- ing, starttls, auth, etc.) that the LMTP client
+ A case insensitive list of LHLO keywords (pipelin-
+ ing, starttls, auth, etc.) that the LMTP client
will ignore in the LHLO response from a remote LMTP
server.
Available in Postfix version 2.0 and later:
<b><a href="postconf.5.html#disable_mime_output_conversion">disable_mime_output_conversion</a> (no)</b>
- Disable the conversion of 8BITMIME format to 7BIT
+ Disable the conversion of 8BITMIME format to 7BIT
format.
<b><a href="postconf.5.html#mime_boundary_length_limit">mime_boundary_length_limit</a> (2048)</b>
Available in Postfix version 2.1 and later:
<b><a href="postconf.5.html#smtp_send_xforward_command">smtp_send_xforward_command</a> (no)</b>
- Send the non-standard XFORWARD command when the
- Postfix SMTP server EHLO response announces XFOR-
+ Send the non-standard XFORWARD command when the
+ Postfix SMTP server EHLO response announces XFOR-
WARD support.
<b>SASL AUTHENTICATION CONTROLS</b>
<b><a href="postconf.5.html#smtp_sasl_auth_enable">smtp_sasl_auth_enable</a> (no)</b>
- Enable SASL authentication in the Postfix SMTP
+ Enable SASL authentication in the Postfix SMTP
client.
<b><a href="postconf.5.html#smtp_sasl_password_maps">smtp_sasl_password_maps</a> (empty)</b>
- Optional SMTP client lookup tables with one user-
- name:password entry per remote hostname or domain,
+ Optional SMTP client lookup tables with one user-
+ name:password entry per remote hostname or domain,
or sender address when sender-dependent authentica-
tion is enabled.
<b><a href="postconf.5.html#smtp_sasl_security_options">smtp_sasl_security_options</a> (noplaintext, noanonymous)</b>
- SASL security options; as of Postfix 2.3 the list
- of available features depends on the SASL client
- implementation that is selected with
+ SASL security options; as of Postfix 2.3 the list
+ of available features depends on the SASL client
+ implementation that is selected with
<b><a href="postconf.5.html#smtp_sasl_type">smtp_sasl_type</a></b>.
Available in Postfix version 2.2 and later:
<b><a href="postconf.5.html#smtp_sasl_mechanism_filter">smtp_sasl_mechanism_filter</a> (empty)</b>
- If non-empty, a Postfix SMTP client filter for the
- remote SMTP server's list of offered SASL mecha-
+ If non-empty, a Postfix SMTP client filter for the
+ remote SMTP server's list of offered SASL mecha-
nisms.
Available in Postfix version 2.3 and later:
<b><a href="postconf.5.html#smtp_sender_dependent_authentication">smtp_sender_dependent_authentication</a> (no)</b>
- Enable sender-dependent authentication in the SMTP
- client; this is available only with SASL authenti-
- cation, and disables SMTP connection caching to
- ensure that mail from different senders will use
+ Enable sender-dependent authentication in the SMTP
+ client; this is available only with SASL authenti-
+ cation, and disables SMTP connection caching to
+ ensure that mail from different senders will use
the appropriate credentials.
<b><a href="postconf.5.html#smtp_sasl_path">smtp_sasl_path</a> (empty)</b>
- Implementation-specific information that is passed
- through to the SASL plug-in implementation that is
+ Implementation-specific information that is passed
+ through to the SASL plug-in implementation that is
selected with <b><a href="postconf.5.html#smtp_sasl_type">smtp_sasl_type</a></b>.
<b><a href="postconf.5.html#smtp_sasl_type">smtp_sasl_type</a> (cyrus)</b>
- The SASL plug-in type that the Postfix SMTP client
+ The SASL plug-in type that the Postfix SMTP client
should use for authentication.
<b>STARTTLS SUPPORT CONTROLS</b>
- Detailed information about STARTTLS configuration may be
+ Detailed information about STARTTLS configuration may be
found in the <a href="TLS_README.html">TLS_README</a> document.
<b><a href="postconf.5.html#smtp_use_tls">smtp_use_tls</a> (no)</b>
- Opportunistic mode: use TLS when a remote SMTP
- server announces STARTTLS support, otherwise send
+ Opportunistic mode: use TLS when a remote SMTP
+ server announces STARTTLS support, otherwise send
the mail in the clear.
<b><a href="postconf.5.html#smtp_enforce_tls">smtp_enforce_tls</a> (no)</b>
- Enforcement mode: require that remote SMTP servers
- use TLS encryption, and never send mail in the
+ Enforcement mode: require that remote SMTP servers
+ use TLS encryption, and never send mail in the
clear.
<b><a href="postconf.5.html#smtp_sasl_tls_security_options">smtp_sasl_tls_security_options</a> ($<a href="postconf.5.html#smtp_sasl_security_options">smtp_sasl_secu</a>-</b>
<b><a href="postconf.5.html#smtp_sasl_security_options">rity_options</a>)</b>
- The SASL authentication security options that the
- Postfix SMTP client uses for TLS encrypted SMTP
+ The SASL authentication security options that the
+ Postfix SMTP client uses for TLS encrypted SMTP
sessions.
<b><a href="postconf.5.html#smtp_starttls_timeout">smtp_starttls_timeout</a> (300s)</b>
- Time limit for Postfix SMTP client write and read
- operations during TLS startup and shutdown hand-
+ Time limit for Postfix SMTP client write and read
+ operations during TLS startup and shutdown hand-
shake procedures.
<b><a href="postconf.5.html#smtp_tls_CAfile">smtp_tls_CAfile</a> (empty)</b>
- The file with the certificate of the certification
- authority (CA) that issued the Postfix SMTP client
+ The file with the certificate of the certification
+ authority (CA) that issued the Postfix SMTP client
certificate.
<b><a href="postconf.5.html#smtp_tls_CApath">smtp_tls_CApath</a> (empty)</b>
- Directory with PEM format certificate authority
- certificates that the Postfix SMTP client uses to
+ Directory with PEM format certificate authority
+ certificates that the Postfix SMTP client uses to
verify a remote SMTP server certificate.
<b><a href="postconf.5.html#smtp_tls_cert_file">smtp_tls_cert_file</a> (empty)</b>
- File with the Postfix SMTP client RSA certificate
+ File with the Postfix SMTP client RSA certificate
in PEM format.
<b><a href="postconf.5.html#smtp_tls_cipherlist">smtp_tls_cipherlist</a> (empty)</b>
- Controls the Postfix SMTP client TLS cipher selec-
+ Controls the Postfix SMTP client TLS cipher selec-
tion scheme.
<b><a href="postconf.5.html#smtp_tls_dcert_file">smtp_tls_dcert_file</a> (empty)</b>
- File with the Postfix SMTP client DSA certificate
+ File with the Postfix SMTP client DSA certificate
in PEM format.
<b><a href="postconf.5.html#smtp_tls_dkey_file">smtp_tls_dkey_file</a> ($<a href="postconf.5.html#smtp_tls_dcert_file">smtp_tls_dcert_file</a>)</b>
- File with the Postfix SMTP client DSA private key
+ File with the Postfix SMTP client DSA private key
in PEM format.
<b><a href="postconf.5.html#smtp_tls_enforce_peername">smtp_tls_enforce_peername</a> (yes)</b>
- When TLS encryption is enforced, require that the
+ When TLS encryption is enforced, require that the
remote SMTP server hostname matches the information
in the remote SMTP server certificate.
<b><a href="postconf.5.html#smtp_tls_key_file">smtp_tls_key_file</a> ($<a href="postconf.5.html#smtp_tls_cert_file">smtp_tls_cert_file</a>)</b>
- File with the Postfix SMTP client RSA private key
+ File with the Postfix SMTP client RSA private key
in PEM format.
<b><a href="postconf.5.html#smtp_tls_loglevel">smtp_tls_loglevel</a> (0)</b>
- Enable additional Postfix SMTP client logging of
+ Enable additional Postfix SMTP client logging of
TLS activity.
<b><a href="postconf.5.html#smtp_tls_note_starttls_offer">smtp_tls_note_starttls_offer</a> (no)</b>
- Log the hostname of a remote SMTP server that
- offers STARTTLS, when TLS is not already enabled
+ Log the hostname of a remote SMTP server that
+ offers STARTTLS, when TLS is not already enabled
for that server.
<b><a href="postconf.5.html#smtp_tls_per_site">smtp_tls_per_site</a> (empty)</b>
Optional lookup tables with the Postfix SMTP client
- TLS usage policy by next-hop domain name and by
+ TLS usage policy by next-hop domain name and by
remote SMTP server hostname.
<b><a href="postconf.5.html#smtp_tls_scert_verifydepth">smtp_tls_scert_verifydepth</a> (5)</b>
- The verification depth for remote SMTP server cer-
+ The verification depth for remote SMTP server cer-
tificates.
<b><a href="postconf.5.html#smtp_tls_session_cache_database">smtp_tls_session_cache_database</a> (empty)</b>
- Name of the file containing the optional Postfix
+ Name of the file containing the optional Postfix
SMTP client TLS session cache.
<b><a href="postconf.5.html#smtp_tls_session_cache_timeout">smtp_tls_session_cache_timeout</a> (3600s)</b>
sion cache information.
<b><a href="postconf.5.html#tls_daemon_random_bytes">tls_daemon_random_bytes</a> (32)</b>
- The number of pseudo-random bytes that an <a href="smtp.8.html"><b>smtp</b>(8)</a>
- or <a href="smtpd.8.html"><b>smtpd</b>(8)</a> process requests from the <a href="tlsmgr.8.html"><b>tlsmgr</b>(8)</a>
- server in order to seed its internal pseudo random
+ The number of pseudo-random bytes that an <a href="smtp.8.html"><b>smtp</b>(8)</a>
+ or <a href="smtpd.8.html"><b>smtpd</b>(8)</a> process requests from the <a href="tlsmgr.8.html"><b>tlsmgr</b>(8)</a>
+ server in order to seed its internal pseudo random
number generator (PRNG).
+ Available in Postfix version 2.3 and later:
+
+ <b><a href="postconf.5.html#smtp_sasl_tls_verified_security_options">smtp_sasl_tls_verified_security_options</a></b>
+ <b>($<a href="postconf.5.html#smtp_sasl_tls_security_options">smtp_sasl_tls_security_options</a>)</b>
+ The SASL authentication security options that the
+ Postfix SMTP client uses for TLS encrypted SMTP
+ sessions with a verified server certificate.
+
<b>RESOURCE AND RATE CONTROLS</b>
<b><a href="postconf.5.html#smtp_destination_concurrency_limit">smtp_destination_concurrency_limit</a> ($<a href="postconf.5.html#default_destination_concurrency_limit">default_destina</a>-</b>
<b><a href="postconf.5.html#default_destination_concurrency_limit">tion_concurrency_limit</a>)</b>
- The maximal number of parallel deliveries to the
- same destination via the smtp message delivery
+ The maximal number of parallel deliveries to the
+ same destination via the smtp message delivery
transport.
<b><a href="postconf.5.html#smtp_destination_recipient_limit">smtp_destination_recipient_limit</a> ($<a href="postconf.5.html#default_destination_recipient_limit">default_destina</a>-</b>
<b><a href="postconf.5.html#default_destination_recipient_limit">tion_recipient_limit</a>)</b>
- The maximal number of recipients per delivery via
+ The maximal number of recipients per delivery via
the smtp message delivery transport.
<b><a href="postconf.5.html#smtp_connect_timeout">smtp_connect_timeout</a> (30s)</b>
- The SMTP client time limit for completing a TCP
+ The SMTP client time limit for completing a TCP
connection, or zero (use the operating system
built-in time limit).
<b><a href="postconf.5.html#smtp_helo_timeout">smtp_helo_timeout</a> (300s)</b>
- The SMTP client time limit for sending the HELO or
- EHLO command, and for receiving the initial server
+ The SMTP client time limit for sending the HELO or
+ EHLO command, and for receiving the initial server
response.
<b><a href="postconf.5.html#lmtp_lhlo_timeout">lmtp_lhlo_timeout</a> (300s)</b>
- The LMTP client time limit for sending the LHLO
+ The LMTP client time limit for sending the LHLO
command, and for receiving the initial server
response.
command, and for receiving the server response.
<b><a href="postconf.5.html#smtp_mail_timeout">smtp_mail_timeout</a> (300s)</b>
- The SMTP client time limit for sending the MAIL
- FROM command, and for receiving the server
+ The SMTP client time limit for sending the MAIL
+ FROM command, and for receiving the server
response.
<b><a href="postconf.5.html#smtp_rcpt_timeout">smtp_rcpt_timeout</a> (300s)</b>
- The SMTP client time limit for sending the SMTP
- RCPT TO command, and for receiving the server
+ The SMTP client time limit for sending the SMTP
+ RCPT TO command, and for receiving the server
response.
<b><a href="postconf.5.html#smtp_data_init_timeout">smtp_data_init_timeout</a> (120s)</b>
- The SMTP client time limit for sending the SMTP
- DATA command, and for receiving the server
+ The SMTP client time limit for sending the SMTP
+ DATA command, and for receiving the server
response.
<b><a href="postconf.5.html#smtp_data_xfer_timeout">smtp_data_xfer_timeout</a> (180s)</b>
- The SMTP client time limit for sending the SMTP
+ The SMTP client time limit for sending the SMTP
message content.
<b><a href="postconf.5.html#smtp_data_done_timeout">smtp_data_done_timeout</a> (600s)</b>
- The SMTP client time limit for sending the SMTP
+ The SMTP client time limit for sending the SMTP
".", and for receiving the server response.
<b><a href="postconf.5.html#smtp_quit_timeout">smtp_quit_timeout</a> (300s)</b>
- The SMTP client time limit for sending the QUIT
+ The SMTP client time limit for sending the QUIT
command, and for receiving the server response.
Available in Postfix version 2.1 and later:
lookups, or zero (no limit).
<b><a href="postconf.5.html#smtp_mx_session_limit">smtp_mx_session_limit</a> (2)</b>
- The maximal number of SMTP sessions per delivery
- request before giving up or delivering to a fall-
+ The maximal number of SMTP sessions per delivery
+ request before giving up or delivering to a fall-
back <a href="postconf.5.html#relayhost">relay host</a>, or zero (no limit).
<b><a href="postconf.5.html#smtp_rset_timeout">smtp_rset_timeout</a> (20s)</b>
- The SMTP client time limit for sending the RSET
+ The SMTP client time limit for sending the RSET
command, and for receiving the server response.
Available in Postfix version 2.2 and earlier:
Available in Postfix version 2.2 and later:
<b><a href="postconf.5.html#smtp_connection_cache_destinations">smtp_connection_cache_destinations</a> (empty)</b>
- Permanently enable SMTP connection caching for the
+ Permanently enable SMTP connection caching for the
specified destinations.
<b><a href="postconf.5.html#smtp_connection_cache_on_demand">smtp_connection_cache_on_demand</a> (yes)</b>
- Temporarily enable SMTP connection caching while a
+ Temporarily enable SMTP connection caching while a
destination has a high volume of mail in the active
queue.
<b><a href="postconf.5.html#smtp_connection_cache_time_limit">smtp_connection_cache_time_limit</a> (2s)</b>
When SMTP connection caching is enabled, the amount
- of time that an unused SMTP client socket is kept
+ of time that an unused SMTP client socket is kept
open before it is closed.
Available in Postfix version 2.3 and later:
<b><a href="postconf.5.html#connection_cache_protocol_timeout">connection_cache_protocol_timeout</a> (5s)</b>
- Time limit for connection cache connect, send or
+ Time limit for connection cache connect, send or
receive operations.
<b>TROUBLE SHOOTING CONTROLS</b>
<b><a href="postconf.5.html#debug_peer_level">debug_peer_level</a> (2)</b>
- The increment in verbose logging level when a
- remote client or server matches a pattern in the
+ The increment in verbose logging level when a
+ remote client or server matches a pattern in the
<a href="postconf.5.html#debug_peer_list">debug_peer_list</a> parameter.
<b><a href="postconf.5.html#debug_peer_list">debug_peer_list</a> (empty)</b>
- Optional list of remote client or server hostname
- or network address patterns that cause the verbose
- logging level to increase by the amount specified
+ Optional list of remote client or server hostname
+ or network address patterns that cause the verbose
+ logging level to increase by the amount specified
in $<a href="postconf.5.html#debug_peer_level">debug_peer_level</a>.
<b><a href="postconf.5.html#error_notice_recipient">error_notice_recipient</a> (postmaster)</b>
- The recipient of postmaster notifications about
- mail delivery problems that are caused by policy,
+ The recipient of postmaster notifications about
+ mail delivery problems that are caused by policy,
resource, software or protocol errors.
<b><a href="postconf.5.html#notify_classes">notify_classes</a> (resource, software)</b>
- The list of error classes that are reported to the
+ The list of error classes that are reported to the
postmaster.
<b>MISCELLANEOUS CONTROLS</b>
<b><a href="postconf.5.html#best_mx_transport">best_mx_transport</a> (empty)</b>
- Where the Postfix SMTP client should deliver mail
+ Where the Postfix SMTP client should deliver mail
when it detects a "mail loops back to myself" error
condition.
<b><a href="postconf.5.html#config_directory">config_directory</a> (see 'postconf -d' output)</b>
- The default location of the Postfix <a href="postconf.5.html">main.cf</a> and
+ The default location of the Postfix <a href="postconf.5.html">main.cf</a> and
<a href="master.5.html">master.cf</a> configuration files.
<b><a href="postconf.5.html#daemon_timeout">daemon_timeout</a> (18000s)</b>
- How much time a Postfix daemon process may take to
- handle a request before it is terminated by a
+ How much time a Postfix daemon process may take to
+ handle a request before it is terminated by a
built-in watchdog timer.
<b><a href="postconf.5.html#delay_logging_resolution_limit">delay_logging_resolution_limit</a> (2)</b>
- The maximal number of digits after the decimal
+ The maximal number of digits after the decimal
point when logging sub-second delay values.
<b><a href="postconf.5.html#disable_dns_lookups">disable_dns_lookups</a> (no)</b>
- Disable DNS lookups in the Postfix SMTP and LMTP
+ Disable DNS lookups in the Postfix SMTP and LMTP
clients.
<b><a href="postconf.5.html#inet_interfaces">inet_interfaces</a> (all)</b>
tem receives mail on.
<b><a href="postconf.5.html#inet_protocols">inet_protocols</a> (ipv4)</b>
- The Internet protocols Postfix will attempt to use
+ The Internet protocols Postfix will attempt to use
when making or accepting connections.
<b><a href="postconf.5.html#ipc_timeout">ipc_timeout</a> (3600s)</b>
over an internal communication channel.
<b><a href="postconf.5.html#lmtp_tcp_port">lmtp_tcp_port</a> (24)</b>
- The default TCP port that the Postfix LMTP client
+ The default TCP port that the Postfix LMTP client
connects to.
<b><a href="postconf.5.html#max_idle">max_idle</a> (100s)</b>
- The maximum amount of time that an idle Postfix
- daemon process waits for the next service request
+ The maximum amount of time that an idle Postfix
+ daemon process waits for the next service request
before exiting.
<b><a href="postconf.5.html#max_use">max_use</a> (100)</b>
- The maximal number of connection requests before a
+ The maximal number of connection requests before a
Postfix daemon process terminates.
<b><a href="postconf.5.html#process_id">process_id</a> (read-only)</b>
- The process ID of a Postfix command or daemon
+ The process ID of a Postfix command or daemon
process.
<b><a href="postconf.5.html#process_name">process_name</a> (read-only)</b>
- The process name of a Postfix command or daemon
+ The process name of a Postfix command or daemon
process.
<b><a href="postconf.5.html#proxy_interfaces">proxy_interfaces</a> (empty)</b>
The network interface addresses that this mail sys-
- tem receives mail on by way of a proxy or network
+ tem receives mail on by way of a proxy or network
address translation unit.
<b><a href="postconf.5.html#smtp_bind_address">smtp_bind_address</a> (empty)</b>
An optional numerical network address that the SMTP
- client should bind to when making an IPv4 connec-
+ client should bind to when making an IPv4 connec-
tion.
<b><a href="postconf.5.html#smtp_bind_address6">smtp_bind_address6</a> (empty)</b>
An optional numerical network address that the SMTP
- client should bind to when making an IPv6 connec-
+ client should bind to when making an IPv6 connec-
tion.
<b><a href="postconf.5.html#smtp_helo_name">smtp_helo_name</a> ($<a href="postconf.5.html#myhostname">myhostname</a>)</b>
- The hostname to send in the SMTP EHLO or HELO com-
+ The hostname to send in the SMTP EHLO or HELO com-
mand.
<b><a href="postconf.5.html#lmtp_lhloname">lmtp_lhlo_name</a> ($<a href="postconf.5.html#myhostname">myhostname</a>)</b>
The hostname to send in the LMTP LHLO command.
<b><a href="postconf.5.html#smtp_host_lookup">smtp_host_lookup</a> (dns)</b>
- What mechanisms when the SMTP client uses to look
+ What mechanisms when the SMTP client uses to look
up a host's IP address.
<b><a href="postconf.5.html#smtp_randomize_addresses">smtp_randomize_addresses</a> (yes)</b>
- Randomize the order of equal-preference MX host
+ Randomize the order of equal-preference MX host
addresses.
<b><a href="postconf.5.html#syslog_facility">syslog_facility</a> (mail)</b>
The syslog facility of Postfix logging.
<b><a href="postconf.5.html#syslog_name">syslog_name</a> (postfix)</b>
- The mail system name that is prepended to the
- process name in syslog records, so that "smtpd"
+ The mail system name that is prepended to the
+ process name in syslog records, so that "smtpd"
becomes, for example, "postfix/smtpd".
Available with Postfix 2.2 and earlier:
<b><a href="postconf.5.html#fallback_relay">fallback_relay</a> (empty)</b>
- Optional list of relay hosts for SMTP destinations
+ Optional list of relay hosts for SMTP destinations
that can't be found or that are unreachable.
Available with Postfix 2.3 and later:
<b><a href="postconf.5.html#smtp_fallback_relay">smtp_fallback_relay</a> ($<a href="postconf.5.html#fallback_relay">fallback_relay</a>)</b>
- Optional list of relay hosts for SMTP destinations
+ Optional list of relay hosts for SMTP destinations
that can't be found or that are unreachable.
<b>SEE ALSO</b>
<a href="TLS_README.html">TLS_README</a>, Postfix STARTTLS howto
<b>LICENSE</b>
- The Secure Mailer license must be distributed with this
+ The Secure Mailer license must be distributed with this
software.
<b>AUTHOR(S)</b>
The SMTP server implements a variety of policies for con-
nection requests, and for parameters given to <b>HELO, ETRN,</b>
<b>MAIL FROM, VRFY</b> and <b>RCPT TO</b> commands. They are detailed
- below and in the <b>main.cf</b> configuration file.
+ below and in the <a href="postconf.5.html"><b>main.cf</b></a> configuration file.
<b>SECURITY</b>
The SMTP server is moderately security-sensitive. It talks
<a href="http://www.faqs.org/rfcs/rfc1869.html">RFC 1869</a> (SMTP service extensions)
<a href="http://www.faqs.org/rfcs/rfc1870.html">RFC 1870</a> (Message Size Declaration)
<a href="http://www.faqs.org/rfcs/rfc1985.html">RFC 1985</a> (ETRN command)
- <a href="http://www.faqs.org/rfcs/rfc2034.html">RFC 2034</a> (Enhanced Status Codes)
<a href="http://www.faqs.org/rfcs/rfc2554.html">RFC 2554</a> (AUTH command)
<a href="http://www.faqs.org/rfcs/rfc2821.html">RFC 2821</a> (SMTP protocol)
<a href="http://www.faqs.org/rfcs/rfc2920.html">RFC 2920</a> (SMTP Pipelining)
policy violations, and of other trouble.
<b>CONFIGURATION PARAMETERS</b>
- Changes to <b>main.cf</b> are picked up automatically, as
+ Changes to <a href="postconf.5.html"><b>main.cf</b></a> are picked up automatically, as
<a href="smtpd.8.html"><b>smtpd</b>(8)</a> processes run for only a limited amount of time.
Use the command "<b>postfix reload</b>" to speed up a change.
<b>MISCELLANEOUS CONTROLS</b>
<b><a href="postconf.5.html#config_directory">config_directory</a> (see 'postconf -d' output)</b>
- The default location of the Postfix main.cf and
- master.cf configuration files.
+ The default location of the Postfix <a href="postconf.5.html">main.cf</a> and
+ <a href="master.5.html">master.cf</a> configuration files.
<b><a href="postconf.5.html#daemon_timeout">daemon_timeout</a> (18000s)</b>
How much time a Postfix daemon process may take to
This violates a basic Postfix principle.
<b>CONFIGURATION PARAMETERS</b>
- Changes to <b>main.cf</b> are not picked up automatically, as
+ Changes to <a href="postconf.5.html"><b>main.cf</b></a> are not picked up automatically, as
<a href="verify.8.html"><b>verify</b>(8)</a> processes are persistent. Use the command "<b>post-</b>
<b>fix reload</b>" after a configuration change.
<b>MISCELLANEOUS CONTROLS</b>
<b><a href="postconf.5.html#config_directory">config_directory</a> (see 'postconf -d' output)</b>
- The default location of the Postfix main.cf and
- master.cf configuration files.
+ The default location of the Postfix <a href="postconf.5.html">main.cf</a> and
+ <a href="master.5.html">master.cf</a> configuration files.
<b><a href="postconf.5.html#daemon_timeout">daemon_timeout</a> (18000s)</b>
How much time a Postfix daemon process may take to
it in quotes as with the shell or with Perl (\fItemplate_name\fB
= <<'EOF'\fR). Here is an example:
-.in +2
+.in +4
.nf
.na
# The failure template is used for undeliverable mail.
.ti +12
The $mail_name program
EOF
-.in -2
+.in -4
.ad
.fi
-
+.PP
+The usage and specification of bounce templates is
+subject to the following restrictions:
+.IP \(bu
No special meaning is given to the backslash character or
to leading whitespace; these are always taken literally.
-
+.IP \(bu
+Inside the << context, the "$" character is special. To
+produce a "$" character as output, specify "$$".
+.IP \(bu
Outside the << context, lines beginning with "#" are ignored,
as are empty lines, and lines consisting of whitespace only.
-
+.PP
Examples of all templates can be found in the file
\fBbounce.cf.default\fR in the Postfix configuration
directory.
Most of these limitations will be removed after Postfix implements
a connection cache that is shared among multiple LMTP client
programs.
+.SH lmtp_cname_overrides_servername (default: yes)
+The LMTP-specific version of the smtp_cname_overrides_servername
+configuration parameter. See there for details.
+.PP
+This feature is available in Postfix 2.3 and later.
.SH lmtp_connect_timeout (default: 0s)
The LMTP client time limit for completing a TCP connection, or
zero (use the operating system built-in time limit). When no
.fi
.ad
.ft R
-.SH lmtp_sasl_tls_security_options (default: $var_lmtp_sasl_opts)
+.SH lmtp_sasl_tls_security_options (default: $lmtp_sasl_security_options)
The LMTP-specific version of the smtp_sasl_tls_security_options
configuration parameter. See there for details.
.PP
This feature is available in Postfix 2.3 and later.
+.SH lmtp_sasl_tls_verified_security_options (default: $lmtp_sasl_tls_security_options)
+The LMTP-specific version of the
+smtp_sasl_tls_verified_security_options configuration parameter.
+See there for details.
+.PP
+This feature is available in Postfix 2.3 and later.
.SH lmtp_sasl_type (default: cyrus)
The SASL plug-in type that the Postfix LMTP client should use
for authentication. The available types are listed with the
.PP
Note 2: address information may be enclosed inside [],
but this form is not recommended here.
+.SH smtp_cname_overrides_servername (default: version dependent)
+Allow DNS CNAME records to override the servername that the
+Postfix SMTP client uses for logging, SASL password lookup, TLS
+policy decisions, or TLS certificate verification. The value "no"
+hardens Postfix smtp_tls_per_site hostname-based policies against
+false hostname information in DNS CNAME records, and makes SASL
+password file lookups more predictable. This is the default setting
+as of Postfix 2.3.
+.PP
+This feature is available in Postfix 2.2.9 and later.
.SH smtp_connect_timeout (default: 30s)
The SMTP client time limit for completing a TCP connection, or
zero (use the operating system built-in time limit).
.SH smtp_sasl_tls_security_options (default: $smtp_sasl_security_options)
The SASL authentication security options that the Postfix SMTP
client uses for TLS encrypted SMTP sessions.
+.SH smtp_sasl_tls_verified_security_options (default: $smtp_sasl_tls_security_options)
+The SASL authentication security options that the Postfix SMTP
+client uses for TLS encrypted SMTP sessions with a verified server
+certificate.
+.PP
+This feature is available in Postfix 2.3 and later.
.SH smtp_sasl_type (default: cyrus)
The SASL plug-in type that the Postfix SMTP client should use
for authentication. The available types are listed with the
information in the remote SMTP server certificate, or that the
server certificate was issued by a trusted CA.
.PP
-Special hint for enforcement mode: since no secure DNS lookup
-mechanism is available, the recommended setup is: specify local
-\fBtransport\fR(5) table entries for sensitive domains with explicit
-smtp:[mailhost] destinations (since you can assure security of this
-table unlike DNS), then specify MUST for these mail hosts in the
-smtp_tls_per_site table.
+Special hints for enforcement mode: since no secure DNS lookup
+mechanism is available, the recommended setup is:
+.IP "Postfix 2.2.9"
+.IP \(bu
+Specify "smtp_cname_overrides_servername = no". This avoids
+false hostname information in DNS CNAME records that could bypass
+a hostname-based TLS usage policy.
+.IP \(bu
+Specify local \fBtransport\fR(5) table entries for sensitive domains
+with explicit smtp:[mailhost] destinations. This avoids false
+hostname information in DNS MX records that could bypass a
+hostname-based TLS usage policy.
+.IP \(bu
+Specify MUST for these mail hosts in the smtp_tls_per_site
+table.
+.IP "Postfix < 2.2.9"
+.IP \(bu
+Specify local \fBtransport\fR(5) table entries for sensitive domains
+with explicit smtp:[mailhost] destinations. This avoids false
+hostname information in DNS MX records that could bypass a
+hostname-based TLS usage policy, but cannot avoid false hostname
+information in DNS CNAME records.
+.IP \(bu
+Specify MUST for these mail hosts in the smtp_tls_per_site
+table.
+.PP
.SH smtp_tls_scert_verifydepth (default: 5)
The verification depth for remote SMTP server certificates. A
depth of 1 is sufficient, if the certificate is directly issued by
.ad
.fi
Some external commands cannot handle more than one recipient
-per delivery request. Examples of such transports are pagers,
-fax machines, and so on.
+per delivery request. Examples of such transports are pagers
+or fax machines.
To prevent Postfix from sending multiple recipients per delivery
request, specify
RFC 1652 (8bit-MIME transport)
RFC 1870 (Message Size Declaration)
RFC 2033 (LMTP protocol)
-RFC 2034 (Enhanced Status Codes)
RFC 2045 (MIME: Format of Internet Message Bodies)
RFC 2046 (MIME: Media Types)
RFC 2554 (AUTH command)
SMTP client, typically to transform a locally valid address into
a globally valid address when sending mail across the Internet.
.PP
+Available in Postfix version 2.2.9 and later:
+.IP "\fBsmtp_cname_overrides_servername (version dependent)\fR"
+Allow DNS CNAME records to override the servername that the
+Postfix SMTP client uses for logging, SASL password lookup, TLS
+policy decisions, or TLS certificate verification.
+.PP
Available in Postfix version 2.3 and later:
.IP "\fBlmtp_discard_lhlo_keyword_address_maps (empty)\fR"
Lookup tables, indexed by the remote LMTP server address, with
The number of pseudo-random bytes that an \fBsmtp\fR(8) or \fBsmtpd\fR(8)
process requests from the \fBtlsmgr\fR(8) server in order to seed its
internal pseudo random number generator (PRNG).
+.PP
+Available in Postfix version 2.3 and later:
+.IP "\fBsmtp_sasl_tls_verified_security_options ($smtp_sasl_tls_security_options)\fR"
+The SASL authentication security options that the Postfix SMTP
+client uses for TLS encrypted SMTP sessions with a verified server
+certificate.
.SH "RESOURCE AND RATE CONTROLS"
.na
.nf
RFC 1869 (SMTP service extensions)
RFC 1870 (Message Size Declaration)
RFC 1985 (ETRN command)
-RFC 2034 (Enhanced Status Codes)
RFC 2554 (AUTH command)
RFC 2821 (SMTP protocol)
RFC 2920 (SMTP Pipelining)
Available in Postfix version 2.2 and later:
.IP "\fBsmtpd_discard_ehlo_keyword_address_maps (empty)\fR"
Lookup tables, indexed by the remote SMTP client address, with
-case insensitive lists of EHLO keywords (pipelining, starttls,
-auth, etc.) that the SMTP server will not send in the EHLO response
-to a remote SMTP client.
+case insensitive lists of EHLO keywords (pipelining, starttls, auth,
+etc.) that the SMTP server will not send in the EHLO response to a
+remote SMTP client.
.IP "\fBsmtpd_discard_ehlo_keywords (empty)\fR"
A case insensitive list of EHLO keywords (pipelining, starttls,
auth, etc.) that the SMTP server will not send in the EHLO response
s;\blmtp_discard_lhlo_keyword_address_maps\b;<a href="postconf.5.html#lmtp_discard_lhlo_keyword_address_maps">$&</a>;g;
s;\blmtp_discard_lhlo_keywords\b;<a href="postconf.5.html#lmtp_discard_lhlo_keywords">$&</a>;g;
s;\blmtp_sasl_tls_security_options\b;<a href="postconf.5.html#lmtp_sasl_tls_security_options">$&</a>;g;
+ s;\blmtp_sasl_tls_verified_security_options\b;<a href="postconf.5.html#lmtp_sasl_tls_verified_security_options">$&</a>;g;
s;\blmtp_sasl_mechanism_filter\b;<a href="postconf.5.html#lmtp_sasl_mechanism_filter">$&</a>;g;
s;\blmtp_host_lookup\b;<a href="postconf.5.html#lmtp_host_lookup">$&</a>;g;
s;\blmtp_connection_cache_destinations\b;<a href="postconf.5.html#lmtp_connection_cache_destinations">$&</a>;g;
s;\bsmtp_always_send_ehlo\b;<a href="postconf.5.html#smtp_always_send_ehlo">$&</a>;g;
s;\bsmtp_bind_address\b;<a href="postconf.5.html#smtp_bind_address">$&</a>;g;
s;\bsmtp_bind_address6\b;<a href="postconf.5.html#smtp_bind_address6">$&</a>;g;
+ s;\bsmtp_cname_overrides_servername\b;<a href="postconf.5.html#smtp_cname_overrides_servername">$&</a>;g;
s;\bsmtp_connect_timeout\b;<a href="postconf.5.html#smtp_connect_timeout">$&</a>;g;
s;\bsmtp_connection_cache_on_demand\b;<a href="postconf.5.html#smtp_connection_cache_on_demand">$&</a>;g;
s;\bsmtp_enforce_tls\b;<a href="postconf.5.html#smtp_enforce_tls">$&</a>;g;
s;\bsmtp_fallback_relay\b;<a href="postconf.5.html#smtp_fallback_relay">$&</a>;g;
- s;\bsmtp_sasl_tls_security_options\b;<a href="postconf.5.html#smtp_sasl_tls_security_options">$&</a>;g;
- s;\bsmtp_sasl_tls_verified_security_options\b;<a href="postconf.5.html#smtp_sasl_tls_verified_security_options">$&</a>;g;
+ s;\bsmtp_[-</Bb>]*\n* *[<Bb>]*sasl_[-</Bb>]*\n* *[<Bb>]*tls_[-</Bb>]*\n* *[<Bb>]*secu[-</Bb>]*\n* *[<Bb>]*rity_options\b;<a href="postconf.5.html#smtp_sasl_tls_security_options">$&</a>;g;
+ s;\bsmtp_sasl_tls_verified_secu[-</Bb>]*\n* *[<Bb>]*rity_options\b;<a href="postconf.5.html#smtp_sasl_tls_verified_security_options">$&</a>;g;
s;\bsmtp_sasl_type\b;<a href="postconf.5.html#smtp_sasl_type">$&</a>;g;
s;\bsmtp_starttls_timeout\b;<a href="postconf.5.html#smtp_starttls_timeout">$&</a>;g;
s;\bsmtp_tls_CAfile\b;<a href="postconf.5.html#smtp_tls_CAfile">$&</a>;g;
# it in quotes as with the shell or with Perl (\fItemplate_name\fB
# = <<'EOF'\fR). Here is an example:
#
-# .in +2
+# .in +4
# .nf
# .na
# # The failure template is used for undeliverable mail.
# .ti +12
# The $mail_name program
# EOF
-# .in -2
+# .in -4
# .ad
# .fi
-#
+# .PP
+# The usage and specification of bounce templates is
+# subject to the following restrictions:
+# .IP \(bu
# No special meaning is given to the backslash character or
# to leading whitespace; these are always taken literally.
-#
+# .IP \(bu
+# Inside the << context, the "$" character is special. To
+# produce a "$" character as output, specify "$$".
+# .IP \(bu
# Outside the << context, lines beginning with "#" are ignored,
# as are empty lines, and lines consisting of whitespace only.
-#
+# .PP
# Examples of all templates can be found in the file
# \fBbounce.cf.default\fR in the Postfix configuration
# directory.
</dl>
-<p> Special hint for enforcement mode: since no secure DNS lookup
-mechanism is available, the recommended setup is: specify local
-transport(5) table entries for sensitive domains with explicit
-smtp:[mailhost] destinations (since you can assure security of this
-table unlike DNS), then specify MUST for these mail hosts in the
-smtp_tls_per_site table. </p>
+<p> Special hints for enforcement mode: since no secure DNS lookup
+mechanism is available, the recommended setup is: </p>
+
+<dl>
+
+<dt> Postfix 2.2.9 </dt>
+
+<dd> <ul>
+
+<li> Specify "smtp_cname_overrides_servername = no". This avoids
+false hostname information in DNS CNAME records that could bypass
+a hostname-based TLS usage policy.
+
+<li> Specify local transport(5) table entries for sensitive domains
+with explicit smtp:[mailhost] destinations. This avoids false
+hostname information in DNS MX records that could bypass a
+hostname-based TLS usage policy.
+
+<li> Specify MUST for these mail hosts in the smtp_tls_per_site
+table.
+
+</ul> </dd>
+
+<dt> Postfix < 2.2.9 </dt>
+
+<dd> <ul>
+
+<li> Specify local transport(5) table entries for sensitive domains
+with explicit smtp:[mailhost] destinations. This avoids false
+hostname information in DNS MX records that could bypass a
+hostname-based TLS usage policy, but cannot avoid false hostname
+information in DNS CNAME records.
+
+<li> Specify MUST for these mail hosts in the smtp_tls_per_site
+table.
+
+</ul> </dd>
+
+</dl>
+
+<p> </p>
%PARAM smtp_tls_scert_verifydepth 5
<p> Time units: s (seconds), m (minutes), h (hours), d (days), w
(weeks). The default time unit is s (seconds). </p>
-%PARAM lmtp_sasl_tls_security_options $var_lmtp_sasl_opts
+%PARAM lmtp_sasl_tls_security_options $lmtp_sasl_security_options
<p> The LMTP-specific version of the smtp_sasl_tls_security_options
configuration parameter. See there for details. </p>
fallback_transport_maps, fallback_transport and luser_relay. </p>
<p> This feature is available in Postfix 2.3 and later. </p>
+
+%PARAM smtp_cname_overrides_servername version dependent
+
+<p> Allow DNS CNAME records to override the servername that the
+Postfix SMTP client uses for logging, SASL password lookup, TLS
+policy decisions, or TLS certificate verification. The value "no"
+hardens Postfix smtp_tls_per_site hostname-based policies against
+false hostname information in DNS CNAME records, and makes SASL
+password file lookups more predictable. This is the default setting
+as of Postfix 2.3. </p>
+
+<p> This feature is available in Postfix 2.2.9 and later. </p>
+
+%PARAM lmtp_cname_overrides_servername yes
+
+<p> The LMTP-specific version of the smtp_cname_overrides_servername
+configuration parameter. See there for details. </p>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
+%PARAM smtp_sasl_tls_verified_security_options $smtp_sasl_tls_security_options
+
+<p> The SASL authentication security options that the Postfix SMTP
+client uses for TLS encrypted SMTP sessions with a verified server
+certificate. </p>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
+%PARAM lmtp_sasl_tls_verified_security_options $lmtp_sasl_tls_security_options
+
+<p> The LMTP-specific version of the
+smtp_sasl_tls_verified_security_options configuration parameter.
+See there for details. </p>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
#define STR vstring_str
+#define VS_NEUTER(s) printable(vstring_str(s), '?')
+
/* bounce_append_proto - bounce_append server protocol */
static int bounce_append_proto(char *service_name, VSTREAM *client)
{
char *myname = "bounce_append_proto";
int flags;
- RECIPIENT_VAR rcpt;
- DSN_VAR dsn;
/*
* Read and validate the client request.
msg_warn("malformed request");
return (-1);
}
+
+ /*
+ * Sanitize input.
+ */
if (mail_queue_id_ok(STR(queue_id)) == 0) {
msg_warn("malformed queue id: %s", printable(STR(queue_id), '?'));
return (-1);
}
- (void) RECIPIENT_FROM_RCPT_BUF(&rcpt, rcpt_buf);
- (void) DSN_FROM_DSN_BUF(&dsn, dsn_buf);
+ VS_NEUTER(rcpt_buf->address);
+ VS_NEUTER(rcpt_buf->orig_addr);
+ VS_NEUTER(rcpt_buf->dsn_orcpt);
+ VS_NEUTER(dsn_buf->status);
+ VS_NEUTER(dsn_buf->action);
+ VS_NEUTER(dsn_buf->reason);
+ VS_NEUTER(dsn_buf->dtype);
+ VS_NEUTER(dsn_buf->dtext);
+ VS_NEUTER(dsn_buf->mtype);
+ VS_NEUTER(dsn_buf->mname);
+ (void) RECIPIENT_FROM_RCPT_BUF(rcpt_buf);
+ (void) DSN_FROM_DSN_BUF(dsn_buf);
/*
- * Beware: some dsn or rcpt fields may be null; access dsn_buf and
- * rcpt_buf instead. See DSN_FROM_DSN_BUF(), RECIPIENT_FROM_RCPT_BUF(),
- * and bounce_log(3).
+ * Beware: some DSN or RECIPIENT fields may be null; access dsn_buf and
+ * rcpt_buf buffers instead. See DSN_FROM_DSN_BUF() and
+ * RECIPIENT_FROM_RCPT_BUF().
*/
if (msg_verbose)
msg_info("%s: flags=0x%x service=%s id=%s org_to=%s to=%s off=%ld dsn_org=%s, notif=0x%x stat=%s act=%s why=%s",
* Execute the request.
*/
return (bounce_append_service(flags, service_name, STR(queue_id),
- &rcpt, &dsn));
+ &rcpt_buf->rcpt, &dsn_buf->dsn));
}
/* bounce_notify_proto - bounce_notify server protocol */
msg_warn("malformed request");
return (-1);
}
+
+ /*
+ * Sanitize input.
+ */
if (mail_queue_name_ok(STR(queue_name)) == 0) {
msg_warn("malformed queue name: %s", printable(STR(queue_name), '?'));
return (-1);
msg_warn("malformed request");
return (-1);
}
+
+ /*
+ * Sanitize input.
+ */
if (mail_queue_name_ok(STR(queue_name)) == 0) {
msg_warn("malformed queue name: %s", printable(STR(queue_name), '?'));
return (-1);
char *myname = "bounce_one_proto";
int flags;
int dsn_ret;
- RECIPIENT rcpt;
- DSN dsn;
/*
* Read and validate the client request.
msg_warn("malformed request");
return (-1);
}
+
+ /*
+ * Sanitize input.
+ */
if (strcmp(service_name, MAIL_SERVICE_BOUNCE) != 0) {
msg_warn("wrong service name \"%s\" for one-recipient bouncing",
service_name);
return (-1);
}
printable(STR(dsn_envid), '?');
- (void) RECIPIENT_FROM_RCPT_BUF(&rcpt, rcpt_buf);
- (void) DSN_FROM_DSN_BUF(&dsn, dsn_buf);
+ VS_NEUTER(rcpt_buf->address);
+ VS_NEUTER(rcpt_buf->orig_addr);
+ VS_NEUTER(rcpt_buf->dsn_orcpt);
+ VS_NEUTER(dsn_buf->status);
+ VS_NEUTER(dsn_buf->action);
+ VS_NEUTER(dsn_buf->reason);
+ VS_NEUTER(dsn_buf->dtype);
+ VS_NEUTER(dsn_buf->dtext);
+ VS_NEUTER(dsn_buf->mtype);
+ VS_NEUTER(dsn_buf->mname);
+ (void) RECIPIENT_FROM_RCPT_BUF(rcpt_buf);
+ (void) DSN_FROM_DSN_BUF(dsn_buf);
/*
- * Beware: some dsn or rcpt fields may be null; access dsn_buf and
- * rcpt_buf instead. See DSN_FROM_DSN_BUF(), RECIPIENT_FROM_RCPT_BUF(),
- * and bounce_log(3).
+ * Beware: some DSN or RECIPIENT fields may be null; access dsn_buf and
+ * rcpt_buf buffers instead. See DSN_FROM_DSN_BUF() and
+ * RECIPIENT_FROM_RCPT_BUF().
*/
if (msg_verbose)
msg_info("%s: flags=0x%x queue=%s id=%s encoding=%s sender=%s envid=%s dsn_ret=0x%x orig_to=%s to=%s off=%ld dsn_orig=%s notif=0x%x stat=%s act=%s why=%s",
*/
return (bounce_one_service(flags, STR(queue_name), STR(queue_id),
STR(encoding), STR(sender), STR(dsn_envid),
- dsn_ret, &rcpt, &dsn, bounce_templates));
+ dsn_ret, rcpt_buf, dsn_buf, bounce_templates));
}
/* bounce_service - parse bounce command type and delegate */
/*
* Special case: dump bounce templates. This is not part of the master(5)
- * public interface.
+ * public interface. This internal interface is used by the postconf
+ * command. It was implemented before bounce templates were isolated
+ * into modules that could have been called directly.
*/
if (strcmp(service_name, "dump_templates") == 0) {
bounce_templates_dump(VSTREAM_OUT, bounce_templates);
/* int flags;
/* char *service;
/* char *queue_id;
-/* RECIPIENT_VAR *rcpt;
-/* DSN_VAR *dsn;
+/* RECIPIENT *rcpt;
+/* DSN *dsn;
/* DESCRIPTION
/* This module implements the server side of the bounce_append()
/* (append bounce log) request. This routine either succeeds or
/* bounce_append_service - append bounce log */
int bounce_append_service(int unused_flags, char *service, char *queue_id,
- RECIPIENT_VAR *rcpt, DSN_VAR *dsn)
+ RECIPIENT *rcpt, DSN *dsn)
{
VSTRING *in_buf = vstring_alloc(100);
VSTREAM *log;
msg_fatal("seek file %s %s: %m", service, queue_id);
#define NOT_NULL_EMPTY(s) ((s) != 0 && *(s) != 0)
-#define ST_NEUTER(s) printable((s), '?')
-#define VS_NEUTER(s) printable(vstring_str(s), '?')
+#define STR(x) vstring_str(x)
vstream_fputs("\n", log);
if (var_oldlog_compat) {
vstream_fprintf(log, "<%s>: %s\n", *rcpt->address == 0 ? "" :
- VS_NEUTER(quote_822_local(in_buf, rcpt->address)),
- ST_NEUTER(dsn->reason));
+ STR(quote_822_local(in_buf, rcpt->address)),
+ dsn->reason);
}
vstream_fprintf(log, "%s=%s\n", MAIL_ATTR_RECIP, *rcpt->address ?
- VS_NEUTER(quote_822_local(in_buf, rcpt->address)) : "<>");
+ STR(quote_822_local(in_buf, rcpt->address)) : "<>");
if (NOT_NULL_EMPTY(rcpt->orig_addr)
&& strcasecmp(rcpt->address, rcpt->orig_addr) != 0)
vstream_fprintf(log, "%s=%s\n", MAIL_ATTR_ORCPT,
- VS_NEUTER(quote_822_local(in_buf, rcpt->orig_addr)));
+ STR(quote_822_local(in_buf, rcpt->orig_addr)));
if (rcpt->offset > 0)
vstream_fprintf(log, "%s=%ld\n", MAIL_ATTR_OFFSET, rcpt->offset);
if (NOT_NULL_EMPTY(rcpt->dsn_orcpt))
- vstream_fprintf(log, "%s=%s\n", MAIL_ATTR_DSN_ORCPT,
- ST_NEUTER(rcpt->dsn_orcpt));
+ vstream_fprintf(log, "%s=%s\n", MAIL_ATTR_DSN_ORCPT, rcpt->dsn_orcpt);
if (rcpt->dsn_notify != 0)
vstream_fprintf(log, "%s=%d\n", MAIL_ATTR_DSN_NOTIFY, rcpt->dsn_notify);
if (NOT_NULL_EMPTY(dsn->status))
- vstream_fprintf(log, "%s=%s\n", MAIL_ATTR_DSN_STATUS,
- ST_NEUTER(dsn->status));
+ vstream_fprintf(log, "%s=%s\n", MAIL_ATTR_DSN_STATUS, dsn->status);
if (NOT_NULL_EMPTY(dsn->action))
- vstream_fprintf(log, "%s=%s\n", MAIL_ATTR_DSN_ACTION,
- ST_NEUTER(dsn->action));
+ vstream_fprintf(log, "%s=%s\n", MAIL_ATTR_DSN_ACTION, dsn->action);
if (NOT_NULL_EMPTY(dsn->dtype) && NOT_NULL_EMPTY(dsn->dtext)) {
- vstream_fprintf(log, "%s=%s\n", MAIL_ATTR_DSN_DTYPE,
- ST_NEUTER(dsn->dtype));
- vstream_fprintf(log, "%s=%s\n", MAIL_ATTR_DSN_DTEXT,
- ST_NEUTER(dsn->dtext));
+ vstream_fprintf(log, "%s=%s\n", MAIL_ATTR_DSN_DTYPE, dsn->dtype);
+ vstream_fprintf(log, "%s=%s\n", MAIL_ATTR_DSN_DTEXT, dsn->dtext);
}
if (NOT_NULL_EMPTY(dsn->mtype) && NOT_NULL_EMPTY(dsn->mname)) {
- vstream_fprintf(log, "%s=%s\n", MAIL_ATTR_DSN_MTYPE,
- ST_NEUTER(dsn->mtype));
- vstream_fprintf(log, "%s=%s\n", MAIL_ATTR_DSN_MNAME,
- ST_NEUTER(dsn->mname));
+ vstream_fprintf(log, "%s=%s\n", MAIL_ATTR_DSN_MTYPE, dsn->mtype);
+ vstream_fprintf(log, "%s=%s\n", MAIL_ATTR_DSN_MNAME, dsn->mname);
}
if (NOT_NULL_EMPTY(dsn->reason))
- vstream_fprintf(log, "%s=%s\n", MAIL_ATTR_WHY, ST_NEUTER(dsn->reason));
+ vstream_fprintf(log, "%s=%s\n", MAIL_ATTR_WHY, dsn->reason);
vstream_fputs("\n", log);
if (vstream_fflush(log) != 0 || fsync(vstream_fileno(log)) < 0) {
VSTREAM *bounce;
int notify_mask = name_mask(VAR_NOTIFY_CLASSES, mail_error_masks,
var_notify_classes);
+ VSTRING *new_id = vstring_alloc(10);
char *postmaster;
int count;
if ((bounce = post_mail_fopen_nowait(mail_addr_double_bounce(),
postmaster,
CLEANUP_FLAG_MASK_INTERNAL,
- NULL_TRACE_FLAGS)) != 0) {
+ NULL_TRACE_FLAGS,
+ new_id)) != 0) {
/*
* Double bounce to Postmaster. This is the last opportunity
DSN_NOTIFY_OVERRIDE) > 0) {
bounce_original(bounce, bounce_info, DSN_RET_FULL);
bounce_status = post_mail_fclose(bounce);
+ if (bounce_status == 0)
+ msg_info("%s: postmaster non-delivery notification: %s",
+ queue_id, STR(new_id));
} else {
/* No applicable recipients found - cancel this notice. */
(void) vstream_fclose(bounce);
else {
if ((bounce = post_mail_fopen_nowait(NULL_SENDER, recipient,
CLEANUP_FLAG_MASK_INTERNAL,
- NULL_TRACE_FLAGS)) != 0) {
+ NULL_TRACE_FLAGS,
+ new_id)) != 0) {
/*
* Send the bounce message header, some boilerplate text that
bounce_original(bounce, bounce_info, dsn_ret ?
dsn_ret : DSN_RET_FULL);
bounce_status = post_mail_fclose(bounce);
+ if (bounce_status == 0)
+ msg_info("%s: sender non-delivery notification: %s",
+ queue_id, STR(new_id));
} else {
/* No applicable recipients found - cancel this notice. */
(void) vstream_fclose(bounce);
if ((bounce = post_mail_fopen_nowait(mail_addr_double_bounce(),
postmaster,
CLEANUP_FLAG_MASK_INTERNAL,
- NULL_TRACE_FLAGS)) != 0) {
+ NULL_TRACE_FLAGS,
+ new_id)) != 0) {
count = -1;
if (bounce_header(bounce, bounce_info, postmaster,
POSTMASTER_COPY) == 0
DSN_NOTIFY_OVERRIDE) > 0) {
bounce_original(bounce, bounce_info, DSN_RET_HDRS);
postmaster_status = post_mail_fclose(bounce);
+ if (postmaster_status == 0)
+ msg_info("%s: postmaster non-delivery notification: %s",
+ queue_id, STR(new_id));
} else {
/* No applicable recipients found - cancel this notice. */
(void) vstream_fclose(bounce);
}
}
if (postmaster_status)
- msg_warn("postmaster notice failed while bouncing to %s",
- recipient);
+ msg_warn("%s: postmaster notice failed while bouncing to %s",
+ queue_id, recipient);
}
}
* Cleanup.
*/
bounce_mail_free(bounce_info);
+ vstring_free(new_id);
return (bounce_status);
}
/* const BOUNCE_TEMPLATE *template;
/*
/* BOUNCE_INFO *bounce_mail_one_init(queue_name, queue_id, encoding,
-/* dsn_envid, dsn_notify, rcpt, dsn,
-/* template)
+/* dsn_envid, dsn_notify, rcpt_buf,
+/* dsn_buf, template)
/* const char *queue_name;
/* const char *queue_id;
/* const char *encoding;
/* int dsn_notify;
/* const char *dsn_envid;
-/* RECIPIENT *rcpt;
-/* DSN *dsn;
+/* RCPT_BUF *rcpt_buf;
+/* DSN_BUF *dsn_buf;
/* const BOUNCE_TEMPLATE *template;
/*
/* void bounce_mail_free(bounce_info)
#include <sys_defs.h>
#include <sys/stat.h>
#include <stdlib.h>
+#include <stdio.h> /* sscanf() */
#include <unistd.h>
#include <errno.h>
#include <string.h>
const char *queue_id,
const char *encoding,
const char *dsn_envid,
+ RCPT_BUF *rcpt_buf,
+ DSN_BUF *dsn_buf,
BOUNCE_TEMPLATE *template,
BOUNCE_LOG *log_handle)
{
int rec_type;
/*
- * Bundle up a bunch of parameters and initialize information. that will
+ * Bundle up a bunch of parameters and initialize information that will
* be discovered on the fly.
*/
bounce_info = (BOUNCE_INFO *) mymalloc(sizeof(*bounce_info));
bounce_info->arrival_time = 0;
bounce_info->orig_offs = 0;
bounce_info->message_size = 0;
+ bounce_info->rcpt_buf = rcpt_buf;
+ bounce_info->dsn_buf = dsn_buf;
bounce_info->log_handle = log_handle;
/*
msg_fatal("open %s %s: %m", service, queue_id);
/*
- * Skip over the original message envelope records. If the envelope is
- * corrupted just send whatever we can (remember this is a best effort,
- * it does not have to be perfect).
+ * Get time/size/sender information from the original message envelope
+ * records. If the envelope is corrupted just send whatever we can
+ * (remember this is a best effort, it does not have to be perfect).
*
* Lock the file for shared use, so that queue manager leaves it alone after
* restarting.
VSTREAM_PATH(bounce_info->orig_fp));
while ((rec_type = rec_get(bounce_info->orig_fp,
bounce_info->buf, 0)) > 0) {
+
+ /*
+ * Postfix version dependent: data offset in SIZE record.
+ */
if (rec_type == REC_TYPE_SIZE) {
- if (bounce_info->message_size == 0
- && (bounce_info->message_size = atol(STR(bounce_info->buf))) < 0)
+ if (bounce_info->message_size == 0)
+ sscanf(STR(bounce_info->buf), "%ld %ld",
+ &bounce_info->message_size,
+ &bounce_info->orig_offs);
+ if (bounce_info->message_size < 0)
bounce_info->message_size = 0;
- } else if (rec_type == REC_TYPE_TIME) {
+ if (bounce_info->orig_offs < 0)
+ bounce_info->orig_offs = 0;
+ }
+
+ /*
+ * Information for the Arrival-Date: attribute.
+ */
+ else if (rec_type == REC_TYPE_TIME) {
if (bounce_info->arrival_time == 0
&& (bounce_info->arrival_time = atol(STR(bounce_info->buf))) < 0)
bounce_info->arrival_time = 0;
- } else if (rec_type == REC_TYPE_FROM) {
+ }
+
+ /*
+ * Information for the X-Postfix-Sender: attribute.
+ */
+ else if (rec_type == REC_TYPE_FROM) {
quote_822_local_flags(bounce_info->sender,
VSTRING_LEN(bounce_info->buf) ?
STR(bounce_info->buf) :
mail_addr_mail_daemon(), 0);
- } else if (rec_type == REC_TYPE_MESG) {
+ }
+
+ /*
+ * Backwards compatibility: no data offset in SIZE record.
+ */
+ else if (rec_type == REC_TYPE_MESG) {
/* XXX Future: sender+recipient after message content. */
if (VSTRING_LEN(bounce_info->sender) == 0)
msg_warn("%s: no sender before message content record",
bounce_info->orig_offs = vstream_ftell(bounce_info->orig_fp);
break;
}
+ if (bounce_info->orig_offs > 0
+ && bounce_info->arrival_time > 0
+ && VSTRING_LEN(bounce_info->sender) > 0)
+ break;
}
}
return (bounce_info);
{
BOUNCE_INFO *bounce_info;
BOUNCE_LOG *log_handle;
+ RCPT_BUF *rcpt_buf;
+ DSN_BUF *dsn_buf;
/*
* Initialize the bounce_info structure. If the bounce log cannot be
* job. But if the system IS running out of resources, raise a fatal
* run-time error and force a backoff.
*/
- if ((log_handle = bounce_log_open(service, queue_id, O_RDONLY, 0)) == 0
- && errno != ENOENT)
- msg_fatal("open %s %s: %m", service, queue_id);
+ if ((log_handle = bounce_log_open(service, queue_id, O_RDONLY, 0)) == 0) {
+ if (errno != ENOENT)
+ msg_fatal("open %s %s: %m", service, queue_id);
+ rcpt_buf = 0;
+ dsn_buf = 0;
+ } else {
+ rcpt_buf = rcpb_create();
+ dsn_buf = dsb_create();
+ }
bounce_info = bounce_mail_alloc(service, queue_name, queue_id, encoding,
- dsn_envid, template, log_handle);
+ dsn_envid, rcpt_buf, dsn_buf,
+ template, log_handle);
return (bounce_info);
}
const char *queue_id,
const char *encoding,
const char *dsn_envid,
- RECIPIENT *rcpt,
- DSN *dsn,
+ RCPT_BUF *rcpt_buf,
+ DSN_BUF *dsn_buf,
BOUNCE_TEMPLATE *template)
{
BOUNCE_INFO *bounce_info;
- BOUNCE_LOG *log_handle;
/*
- * Initialize the bounce_info structure. Forge a logfile record for just
- * one recipient.
+ * Initialize the bounce_info structure for just one recipient.
*/
- log_handle = bounce_log_forge(rcpt, dsn);
bounce_info = bounce_mail_alloc("none", queue_name, queue_id, encoding,
- dsn_envid, template, log_handle);
+ dsn_envid, rcpt_buf, dsn_buf, template,
+ (BOUNCE_LOG *) 0);
return (bounce_info);
}
void bounce_mail_free(BOUNCE_INFO *bounce_info)
{
- if (bounce_info->log_handle && bounce_log_close(bounce_info->log_handle))
- msg_warn("%s: read bounce log %s: %m",
- bounce_info->queue_id, bounce_info->queue_id);
+ if (bounce_info->log_handle) {
+ if (bounce_log_close(bounce_info->log_handle))
+ msg_warn("%s: read bounce log %s: %m",
+ bounce_info->queue_id, bounce_info->queue_id);
+ rcpb_free(bounce_info->rcpt_buf);
+ dsb_free(bounce_info->dsn_buf);
+ }
if (bounce_info->orig_fp && vstream_fclose(bounce_info->orig_fp))
msg_warn("%s: read message file %s %s: %m",
bounce_info->queue_id, bounce_info->queue_name,
int bounce_recipient_log(VSTREAM *bounce, BOUNCE_INFO *bounce_info)
{
+ RECIPIENT *rcpt = &bounce_info->rcpt_buf->rcpt;
+ DSN *dsn = &bounce_info->dsn_buf->dsn;
/*
* Mask control and non-ASCII characters (done in bounce_log_read()),
* piped into other programs. Sort of like TCP Wrapper's safe_finger
* program.
*/
+#define NON_NULL_EMPTY(s) ((s) && *(s))
+
post_mail_fputs(bounce, "");
- if (bounce_info->log_handle->rcpt.orig_addr) {
+ if (NON_NULL_EMPTY(rcpt->orig_addr)) {
bounce_print_wrap(bounce, bounce_info, "<%s> (expanded from <%s>): %s",
- bounce_info->log_handle->rcpt.address,
- bounce_info->log_handle->rcpt.orig_addr,
- bounce_info->log_handle->dsn.reason);
+ rcpt->address, rcpt->orig_addr, dsn->reason);
} else {
bounce_print_wrap(bounce, bounce_info, "<%s>: %s",
- bounce_info->log_handle->rcpt.address,
- bounce_info->log_handle->dsn.reason);
+ rcpt->address, dsn->reason);
}
return (vstream_ferror(bounce));
}
int bounce_diagnostic_log(VSTREAM *bounce, BOUNCE_INFO *bounce_info,
int notify_filter)
{
+ RECIPIENT *rcpt = &bounce_info->rcpt_buf->rcpt;
int count = 0;
/*
if (bounce_info->log_handle == 0
|| bounce_log_rewind(bounce_info->log_handle)) {
if (IS_FAILURE_TEMPLATE(bounce_info->template)) {
+ post_mail_fputs(bounce, "");
post_mail_fputs(bounce, "\t--- Delivery report unavailable ---");
count = 1; /* XXX don't abort */
}
} else {
- while (bounce_log_read(bounce_info->log_handle) != 0) {
- if (bounce_info->log_handle->rcpt.dsn_notify == 0 /* compat */
- || (bounce_info->log_handle->rcpt.dsn_notify & notify_filter)) {
+ while (bounce_log_read(bounce_info->log_handle, bounce_info->rcpt_buf,
+ bounce_info->dsn_buf) != 0) {
+ if (rcpt->dsn_notify == 0 /* compat */
+ || (rcpt->dsn_notify & notify_filter)) {
count++;
if (bounce_recipient_log(bounce, bounce_info) != 0)
break;
#if 0
post_mail_fprintf(bounce, "Received-From-MTA: dns; %s", "whatever");
#endif
- if (bounce_info->dsn_envid) {
+ if (NON_NULL_EMPTY(bounce_info->dsn_envid)) {
post_mail_fprintf(bounce, "Original-Envelope-Id: %s",
bounce_info->dsn_envid);
}
int bounce_recipient_dsn(VSTREAM *bounce, BOUNCE_INFO *bounce_info)
{
+ RECIPIENT *rcpt = &bounce_info->rcpt_buf->rcpt;
+ DSN *dsn = &bounce_info->dsn_buf->dsn;
+
post_mail_fputs(bounce, "");
- post_mail_fprintf(bounce, "Final-Recipient: rfc822; %s",
- bounce_info->log_handle->rcpt.address);
+ post_mail_fprintf(bounce, "Final-Recipient: rfc822; %s", rcpt->address);
/*
* XXX DSN
* Postfix always reports an Original-Recipient field, because it is more
* more useful and more consistent.
*/
- if (bounce_info->log_handle->rcpt.dsn_orcpt) {
- post_mail_fprintf(bounce, "Original-Recipient: %s",
- bounce_info->log_handle->rcpt.dsn_orcpt);
- } else if (bounce_info->log_handle->rcpt.orig_addr) {
+ if (NON_NULL_EMPTY(rcpt->dsn_orcpt)) {
+ post_mail_fprintf(bounce, "Original-Recipient: %s", rcpt->dsn_orcpt);
+ } else if (NON_NULL_EMPTY(rcpt->orig_addr)) {
post_mail_fprintf(bounce, "Original-Recipient: rfc822; %s",
- bounce_info->log_handle->rcpt.orig_addr);
+ rcpt->orig_addr);
}
post_mail_fprintf(bounce, "Action: %s",
IS_FAILURE_TEMPLATE(bounce_info->template) ?
- "failed" : bounce_info->log_handle->dsn.action);
- post_mail_fprintf(bounce, "Status: %s",
- bounce_info->log_handle->dsn.status);
- if (bounce_info->log_handle->dsn.mtype
- && bounce_info->log_handle->dsn.mname)
+ "failed" : dsn->action);
+ post_mail_fprintf(bounce, "Status: %s", dsn->status);
+ if (NON_NULL_EMPTY(dsn->mtype) && NON_NULL_EMPTY(dsn->mname))
bounce_print_wrap(bounce, bounce_info, "Remote-MTA: %s; %s",
- bounce_info->log_handle->dsn.mtype,
- bounce_info->log_handle->dsn.mname);
- if (bounce_info->log_handle->dsn.dtype
- && bounce_info->log_handle->dsn.dtext)
+ dsn->mtype, dsn->mname);
+ if (NON_NULL_EMPTY(dsn->dtype) && NON_NULL_EMPTY(dsn->dtext))
bounce_print_wrap(bounce, bounce_info, "Diagnostic-Code: %s; %s",
- bounce_info->log_handle->dsn.dtype,
- bounce_info->log_handle->dsn.dtext);
+ dsn->dtype, dsn->dtext);
else
bounce_print_wrap(bounce, bounce_info, "Diagnostic-Code: X-%s; %s",
- bounce_info->mail_name,
- bounce_info->log_handle->dsn.reason);
+ bounce_info->mail_name, dsn->reason);
#if 0
- post_mail_fprintf(bounce, "Last-Attempt-Date: %s",
- bounce_info->log_handle->log_time);
+ if (dsn->time > 0)
+ post_mail_fprintf(bounce, "Last-Attempt-Date: %s",
+ mail_date(dsn->time));
#endif
if (IS_DELAY_TEMPLATE(bounce_info->template))
post_mail_fprintf(bounce, "Will-Retry-Until: %s",
int bounce_diagnostic_dsn(VSTREAM *bounce, BOUNCE_INFO *bounce_info,
int notify_filter)
{
+ RECIPIENT *rcpt = &bounce_info->rcpt_buf->rcpt;
int count = 0;
/*
if (IS_FAILURE_TEMPLATE(bounce_info->template))
count = 1; /* XXX don't abort */
} else {
- while (bounce_log_read(bounce_info->log_handle) != 0) {
- if (bounce_info->log_handle->rcpt.dsn_notify == 0 /* compat */
- || (bounce_info->log_handle->rcpt.dsn_notify & notify_filter)) {
+ while (bounce_log_read(bounce_info->log_handle, bounce_info->rcpt_buf,
+ bounce_info->dsn_buf) != 0) {
+ if (rcpt->dsn_notify == 0 /* compat */
+ || (rcpt->dsn_notify & notify_filter)) {
count++;
if (bounce_recipient_dsn(bounce, bounce_info) != 0)
break;
void bounce_delrcpt(BOUNCE_INFO *bounce_info)
{
+ RECIPIENT *rcpt = &bounce_info->rcpt_buf->rcpt;
+
if (bounce_info->orig_fp != 0
&& bounce_info->log_handle != 0
&& bounce_log_rewind(bounce_info->log_handle) == 0)
- while (bounce_log_read(bounce_info->log_handle) != 0)
- if (bounce_info->log_handle->rcpt.offset > 0)
- deliver_completed(bounce_info->orig_fp,
- bounce_info->log_handle->rcpt.offset);
+ while (bounce_log_read(bounce_info->log_handle, bounce_info->rcpt_buf,
+ bounce_info->dsn_buf) != 0)
+ if (rcpt->offset > 0)
+ deliver_completed(bounce_info->orig_fp, rcpt->offset);
}
/* bounce_delrcpt_one - delete one recipient from original queue file */
void bounce_delrcpt_one(BOUNCE_INFO *bounce_info)
{
- if (bounce_info->orig_fp != 0
- && bounce_info->log_handle != 0
- && bounce_info->log_handle->rcpt.offset > 0)
- deliver_completed(bounce_info->orig_fp,
- bounce_info->log_handle->rcpt.offset);
+ RECIPIENT *rcpt = &bounce_info->rcpt_buf->rcpt;
+
+ if (bounce_info->orig_fp != 0 && rcpt->offset > 0)
+ deliver_completed(bounce_info->orig_fp, rcpt->offset);
}
int notify_mask = name_mask(VAR_NOTIFY_CLASSES, mail_error_masks,
var_notify_classes);
char *postmaster;
- VSTRING *verp_buf = vstring_alloc(100);
+ VSTRING *verp_buf;
+ VSTRING *new_id;
/*
* Sanity checks. We must be called only for undeliverable non-bounce
bounce_info = bounce_mail_init(service, queue_name, queue_id,
encoding, dsn_envid, ts->failure);
+ /*
+ * If we have no recipient list then we can't send VERP replies. Send
+ * *something* anyway so that the mail is not lost in a black hole.
+ */
+ if (bounce_info->log_handle == 0) {
+ DSN_BUF *dsn_buf = dsb_create();
+ RCPT_BUF *rcpt_buf = rcpb_create();
+
+ dsb_simple(dsn_buf, "5.0.0", "(error report unavailable)");
+ (void) DSN_FROM_DSN_BUF(dsn_buf);
+ vstring_strcpy(rcpt_buf->address, "(recipient address unavailable)");
+ (void) RECIPIENT_FROM_RCPT_BUF(rcpt_buf);
+ bounce_status = bounce_one_service(flags, queue_name, queue_id,
+ encoding, recipient, dsn_envid,
+ dsn_ret, rcpt_buf, dsn_buf, ts);
+ rcpb_free(rcpt_buf);
+ dsb_free(dsn_buf);
+ bounce_mail_free(bounce_info);
+ return (bounce_status);
+ }
+
#define NULL_SENDER MAIL_ADDR_EMPTY /* special address */
#define NULL_TRACE_FLAGS 0
* A non-bounce message was returned. Send a single bounce, one per
* recipient.
*/
- while (bounce_log_read(bounce_info->log_handle) != 0) {
+ verp_buf = vstring_alloc(100);
+ new_id = vstring_alloc(10);
+ while (bounce_log_read(bounce_info->log_handle, bounce_info->rcpt_buf,
+ bounce_info->dsn_buf) != 0) {
+ RECIPIENT *rcpt = &bounce_info->rcpt_buf->rcpt;
/*
* Notify the originator, subject to DSN NOTIFY restrictions.
*/
- if (bounce_info->log_handle->rcpt.dsn_notify != 0 /* compat */
- && (bounce_info->log_handle->rcpt.dsn_notify & DSN_NOTIFY_FAILURE) == 0) {
+ if (rcpt->dsn_notify != 0 /* compat */
+ && (rcpt->dsn_notify & DSN_NOTIFY_FAILURE) == 0) {
bounce_status = 0;
} else {
- verp_sender(verp_buf, verp_delims, recipient,
- bounce_info->log_handle->rcpt.address);
+ verp_sender(verp_buf, verp_delims, recipient, rcpt->address);
if ((bounce = post_mail_fopen_nowait(NULL_SENDER, STR(verp_buf),
CLEANUP_FLAG_MASK_INTERNAL,
- NULL_TRACE_FLAGS)) != 0) {
+ NULL_TRACE_FLAGS,
+ new_id)) != 0) {
/*
* Send the bounce message header, some boilerplate text that
bounce_original(bounce, bounce_info, dsn_ret ?
dsn_ret : DSN_RET_FULL);
bounce_status = post_mail_fclose(bounce);
+ if (bounce_status == 0)
+ msg_info("%s: sender non-delivery notification: %s",
+ queue_id, STR(new_id));
} else
bounce_status = 1;
if ((bounce = post_mail_fopen_nowait(mail_addr_double_bounce(),
postmaster,
CLEANUP_FLAG_MASK_INTERNAL,
- NULL_TRACE_FLAGS)) != 0) {
+ NULL_TRACE_FLAGS,
+ new_id)) != 0) {
if (bounce_header(bounce, bounce_info, postmaster,
POSTMASTER_COPY) == 0
&& bounce_recipient_log(bounce, bounce_info) == 0
&& bounce_recipient_dsn(bounce, bounce_info) == 0)
bounce_original(bounce, bounce_info, DSN_RET_HDRS);
postmaster_status = post_mail_fclose(bounce);
+ if (postmaster_status == 0)
+ msg_info("%s: postmaster non-delivery notification: %s",
+ queue_id, STR(new_id));
} else
postmaster_status = 1;
if (postmaster_status)
- msg_warn("postmaster notice failed while bouncing to %s",
- recipient);
+ msg_warn("%s: postmaster notice failed while bouncing to %s",
+ queue_id, recipient);
}
}
*/
bounce_mail_free(bounce_info);
vstring_free(verp_buf);
+ vstring_free(new_id);
return (bounce_status);
}
/*
/* int bounce_one_service(flags, queue_name, queue_id, encoding,
/* orig_sender, envid, ret,
-/* rcpt, dsn, templates)
+/* rcpt_buf, dsn_buf, templates)
/* int flags;
/* char *queue_name;
/* char *queue_id;
/* char *orig_sender;
/* char *envid;
/* int ret;
-/* RECIPIENT *rcpt;
-/* DSN *dsn;
+/* RCPT_BUF *rcpt_buf;
+/* DSN_BUF *dsn_buf;
/* BOUNCE_TEMPLATES *templates;
/* DESCRIPTION
/* This module implements the server side of the bounce_one()
int bounce_one_service(int flags, char *queue_name, char *queue_id,
char *encoding, char *orig_sender,
char *dsn_envid, int dsn_ret,
- RECIPIENT *rcpt, DSN *dsn,
+ RCPT_BUF *rcpt_buf, DSN_BUF *dsn_buf,
BOUNCE_TEMPLATES *ts)
{
BOUNCE_INFO *bounce_info;
VSTREAM *bounce;
int notify_mask = name_mask(VAR_NOTIFY_CLASSES, mail_error_masks,
var_notify_classes);
+ VSTRING *new_id = vstring_alloc(10);
/*
* Initialize. Open queue file, bounce log, etc.
*/
bounce_info = bounce_mail_one_init(queue_name, queue_id, encoding,
- dsn_envid, rcpt, dsn, ts->failure);
+ dsn_envid, rcpt_buf, dsn_buf,
+ ts->failure);
#define NULL_SENDER MAIL_ADDR_EMPTY /* special address */
#define NULL_TRACE_FLAGS 0
if ((bounce = post_mail_fopen_nowait(mail_addr_double_bounce(),
var_2bounce_rcpt,
CLEANUP_FLAG_MASK_INTERNAL,
- NULL_TRACE_FLAGS)) != 0) {
+ NULL_TRACE_FLAGS,
+ new_id)) != 0) {
/*
* Double bounce to Postmaster. This is the last opportunity
&& bounce_recipient_dsn(bounce, bounce_info) == 0)
bounce_original(bounce, bounce_info, DSN_RET_FULL);
bounce_status = post_mail_fclose(bounce);
+ if (bounce_status == 0)
+ msg_info("%s: postmaster non-delivery notification: %s",
+ queue_id, STR(new_id));
}
}
}
* restrictions.
*/
else {
- if (bounce_info->log_handle->rcpt.dsn_notify != 0 /* compat */
- && (bounce_info->log_handle->rcpt.dsn_notify & DSN_NOTIFY_FAILURE) == 0) {
+ RECIPIENT *rcpt = &bounce_info->rcpt_buf->rcpt;
+
+ if (rcpt->dsn_notify != 0 /* compat */
+ && (rcpt->dsn_notify & DSN_NOTIFY_FAILURE) == 0) {
bounce_status = 0;
} else {
if ((bounce = post_mail_fopen_nowait(NULL_SENDER, orig_sender,
CLEANUP_FLAG_MASK_INTERNAL,
- NULL_TRACE_FLAGS)) != 0) {
+ NULL_TRACE_FLAGS,
+ new_id)) != 0) {
/*
* Send the bounce message header, some boilerplate text that
bounce_original(bounce, bounce_info, dsn_ret ?
dsn_ret : DSN_RET_FULL);
bounce_status = post_mail_fclose(bounce);
+ if (bounce_status == 0)
+ msg_info("%s: sender non-delivery notification: %s",
+ queue_id, STR(new_id));
}
}
if ((bounce = post_mail_fopen_nowait(mail_addr_double_bounce(),
var_bounce_rcpt,
CLEANUP_FLAG_MASK_INTERNAL,
- NULL_TRACE_FLAGS)) != 0) {
+ NULL_TRACE_FLAGS,
+ new_id)) != 0) {
if (bounce_header(bounce, bounce_info, var_bounce_rcpt,
POSTMASTER_COPY) == 0
&& bounce_recipient_log(bounce, bounce_info) == 0
&& bounce_recipient_dsn(bounce, bounce_info) == 0)
bounce_original(bounce, bounce_info, DSN_RET_HDRS);
postmaster_status = post_mail_fclose(bounce);
+ if (postmaster_status == 0)
+ msg_info("%s: postmaster non-delivery notification: %s",
+ queue_id, STR(new_id));
}
if (postmaster_status)
- msg_warn("postmaster notice failed while bouncing to %s",
- orig_sender);
+ msg_warn("%s: postmaster notice failed while bouncing to %s",
+ queue_id, orig_sender);
}
}
* Cleanup.
*/
bounce_mail_free(bounce_info);
+ vstring_free(new_id);
return (bounce_status);
}
/*
* bounce_append_service.c
*/
-extern int bounce_append_service(int, char *, char *, RECIPIENT_VAR *, DSN_VAR *);
+extern int bounce_append_service(int, char *, char *, RECIPIENT *, DSN *);
/*
* bounce_notify_service.c
/*
* bounce_one_service.c
*/
-extern int bounce_one_service(int, char *, char *, char *, char *, char *, int, RECIPIENT *, DSN *, BOUNCE_TEMPLATES *);
+extern int bounce_one_service(int, char *, char *, char *, char *, char *, int, RCPT_BUF *, DSN_BUF *, BOUNCE_TEMPLATES *);
/*
* bounce_cleanup.c
long orig_offs; /* start of content */
time_t arrival_time; /* time of arrival */
long message_size; /* size of content */
+ RCPT_BUF *rcpt_buf; /* recipient info */
+ DSN_BUF *dsn_buf; /* delivery status info */
BOUNCE_LOG *log_handle; /* open logfile */
char *mail_name; /* $mail_name, cooked */
} BOUNCE_INFO;
/* */
extern BOUNCE_INFO *bounce_mail_init(const char *, const char *, const char *, const char *, const char *, BOUNCE_TEMPLATE *);
-extern BOUNCE_INFO *bounce_mail_one_init(const char *, const char *, const char *, const char *, RECIPIENT *, DSN *, BOUNCE_TEMPLATE *);
+extern BOUNCE_INFO *bounce_mail_one_init(const char *, const char *, const char *, const char *, RCPT_BUF *, DSN_BUF *, BOUNCE_TEMPLATE *);
extern void bounce_mail_free(BOUNCE_INFO *);
extern int bounce_header(VSTREAM *, BOUNCE_INFO *, const char *, int);
extern int bounce_boilerplate(VSTREAM *, BOUNCE_INFO *);
"# THIS IS A WARNING ONLY. YOU DO NOT NEED TO RESEND YOUR MESSAGE. #",
"####################################################################",
"",
- "Your message could not be delivered for $delay_warning_time_hours hour(s)."
+ "Your message could not be delivered for more than $delay_warning_time_hours hour(s)."
,
"It will be retried until it is $maximal_queue_lifetime_days day(s) old.",
"",
BOUNCE_INFO *bounce_info;
int bounce_status = 1;
VSTREAM *bounce;
+ VSTRING *new_id = vstring_alloc(10);
int count;
/*
*/
if ((bounce = post_mail_fopen_nowait(NULL_SENDER, recipient,
CLEANUP_FLAG_MASK_INTERNAL,
- NULL_TRACE_FLAGS)) != 0) {
+ NULL_TRACE_FLAGS,
+ new_id)) != 0) {
count = -1;
if (bounce_header(bounce, bounce_info, recipient,
NO_POSTMASTER_COPY) == 0
DSN_NOTIFY_OVERRIDE) > 0) {
bounce_original(bounce, bounce_info, DSN_RET_HDRS);
bounce_status = post_mail_fclose(bounce);
+ if (bounce_status == 0)
+ msg_info("%s: sender delivery status notification: %s",
+ queue_id, STR(new_id));
} else {
(void) vstream_fclose(bounce);
if (count == 0)
* Cleanup.
*/
bounce_mail_free(bounce_info);
+ vstring_free(new_id);
return (bounce_status);
}
VSTREAM *bounce;
int notify_mask = name_mask(VAR_NOTIFY_CLASSES, mail_error_masks,
var_notify_classes);
+ VSTRING *new_id = vstring_alloc(10);
char *postmaster;
int count;
if ((bounce = post_mail_fopen_nowait(mail_addr_double_bounce(),
postmaster,
CLEANUP_FLAG_MASK_INTERNAL,
- NULL_TRACE_FLAGS)) != 0) {
+ NULL_TRACE_FLAGS,
+ new_id)) != 0) {
/*
* Double bounce to Postmaster. This is the last opportunity
DSN_NOTIFY_OVERRIDE) > 0) {
bounce_original(bounce, bounce_info, DSN_RET_FULL);
bounce_status = post_mail_fclose(bounce);
+ if (bounce_status == 0)
+ msg_info("%s: postmaster delay notification: %s",
+ queue_id, STR(new_id));
} else {
(void) vstream_fclose(bounce);
if (count == 0)
else {
if ((bounce = post_mail_fopen_nowait(NULL_SENDER, recipient,
CLEANUP_FLAG_MASK_INTERNAL,
- NULL_TRACE_FLAGS)) != 0) {
+ NULL_TRACE_FLAGS,
+ new_id)) != 0) {
/*
* Send the bounce message header, some boilerplate text that
DSN_NOTIFY_DELAY) > 0) {
bounce_original(bounce, bounce_info, DSN_RET_HDRS);
bounce_status = post_mail_fclose(bounce);
+ if (bounce_status == 0)
+ msg_info("%s: sender delay notification: %s",
+ queue_id, STR(new_id));
} else {
(void) vstream_fclose(bounce);
if (count == 0)
if ((bounce = post_mail_fopen_nowait(mail_addr_double_bounce(),
postmaster,
CLEANUP_FLAG_MASK_INTERNAL,
- NULL_TRACE_FLAGS)) != 0) {
+ NULL_TRACE_FLAGS,
+ new_id)) != 0) {
count = -1;
if (bounce_header(bounce, bounce_info, postmaster,
POSTMASTER_COPY) == 0
DSN_NOTIFY_OVERRIDE) > 0) {
bounce_original(bounce, bounce_info, DSN_RET_HDRS);
postmaster_status = post_mail_fclose(bounce);
+ if (postmaster_status == 0)
+ msg_info("%s: postmaster delay notification: %s",
+ queue_id, STR(new_id));
} else {
(void) vstream_fclose(bounce);
if (count == 0)
}
}
if (postmaster_status)
- msg_warn("postmaster notice failed while bouncing to %s",
- recipient);
+ msg_warn("%s: postmaster notice failed while warning %s",
+ queue_id, recipient);
}
}
* Cleanup.
*/
bounce_mail_free(bounce_info);
+ vstring_free(new_id);
return (bounce_status);
}
* named after the things one can expect to find in a DNS resource record.
*/
typedef struct DNS_RR {
- char *name; /* name, mystrdup()ed */
+ char *qname; /* query name, mystrdup()ed */
char *rname; /* reply name, mystrdup()ed */
unsigned short type; /* T_A, T_CNAME, etc. */
unsigned short class; /* C_IN, etc. */
/* SYNOPSIS
/* #include <dns.h>
/*
-/* DNS_RR *dns_rr_create(name, rname, type, class, ttl, preference,
+/* DNS_RR *dns_rr_create(qname, rname, type, class, ttl, preference,
/* data, data_len)
-/* const char *name;
+/* const char *qname;
/* const char *rname;
/* unsigned short type;
/* unsigned short class;
/* information, and maintain lists of DNS resource records.
/*
/* dns_rr_create() creates and initializes one resource record.
-/* The \fIname\fR field specifies the query name.
+/* The \fIqname\fR field specifies the query name.
/* The \fIrname\fR field specifies the reply name.
/* \fIpreference\fR is used for MX records; \fIdata\fR is a null
/* pointer or specifies optional resource-specific data;
/*
/* dns_rr_append() appends a resource record to a (list of) resource
/* record(s).
+/* A null input list is explicitly allowed.
/*
/* dns_rr_sort() sorts a list of resource records into ascending
/* order according to a user-specified criterion. The result is the
/*
/* dns_rr_remove() removes the specified record from the specified list.
/* The updated list is the result value.
+/* The record MUST be a list member.
/* LICENSE
/* .ad
/* .fi
/* dns_rr_create - fill in resource record structure */
-DNS_RR *dns_rr_create(const char *name, const char *rname,
+DNS_RR *dns_rr_create(const char *qname, const char *rname,
ushort type, ushort class,
unsigned int ttl, unsigned pref,
const char *data, size_t data_len)
DNS_RR *rr;
rr = (DNS_RR *) mymalloc(sizeof(*rr) + data_len - 1);
- rr->name = mystrdup(name);
+ rr->qname = mystrdup(qname);
rr->rname = mystrdup(rname);
rr->type = type;
rr->class = class;
if (rr) {
if (rr->next)
dns_rr_free(rr->next);
- myfree(rr->name);
+ myfree(rr->qname);
myfree(rr->rname);
myfree((char *) rr);
}
*/
dst = (DNS_RR *) mymalloc(len);
memcpy((char *) dst, (char *) src, len);
- dst->name = mystrdup(src->name);
+ dst->qname = mystrdup(src->qname);
dst->rname = mystrdup(src->rname);
dst->next = 0;
return (dst);
rcpt = request->rcpt_list.info + nrcpt;
if (rcpt->offset >= 0) {
status = bounce_append(BOUNCE_FLAGS(request), request->queue_id,
- &request->msg_stats, rcpt, "none",
- &dsn);
+ &request->msg_stats, rcpt, "none", &dsn);
if (status == 0)
deliver_completed(src, rcpt->offset);
result |= status;
deliver_pass.o: mail_params.h
deliver_pass.o: mail_proto.h
deliver_pass.o: msg_stats.h
+deliver_pass.o: rcpt_print.h
deliver_pass.o: recipient_list.h
deliver_request.o: ../../include/attr.h
deliver_request.o: ../../include/iostuff.h
deliver_request.o: deliver_request.c
deliver_request.o: deliver_request.h
deliver_request.o: dsn.h
-deliver_request.o: dsn_buf.h
deliver_request.o: dsn_print.h
deliver_request.o: mail_open_ok.h
deliver_request.o: mail_proto.h
deliver_request.o: mail_queue.h
deliver_request.o: msg_stats.h
+deliver_request.o: rcpt_buf.h
deliver_request.o: recipient_list.h
dict_ldap.o: ../../include/argv.h
dict_ldap.o: ../../include/binhash.h
dsb_scan.o: ../../include/vstring.h
dsb_scan.o: dsb_scan.c
dsb_scan.o: dsb_scan.h
+dsb_scan.o: dsn.h
dsb_scan.o: dsn_buf.h
dsb_scan.o: mail_proto.h
dsn.o: ../../include/msg.h
dsn.o: ../../include/mymalloc.h
dsn.o: ../../include/sys_defs.h
-dsn.o: ../../include/vbuf.h
-dsn.o: ../../include/vstring.h
dsn.o: dsn.c
dsn.o: dsn.h
-dsn.o: dsn_buf.h
dsn_attr_map.o: ../../include/attr.h
dsn_attr_map.o: ../../include/iostuff.h
dsn_attr_map.o: ../../include/sys_defs.h
dsn_buf.o: ../../include/sys_defs.h
dsn_buf.o: ../../include/vbuf.h
dsn_buf.o: ../../include/vstring.h
+dsn_buf.o: dsn.h
dsn_buf.o: dsn_buf.c
dsn_buf.o: dsn_buf.h
dsn_mask.o: ../../include/msg.h
dsn_print.o: ../../include/sys_defs.h
dsn_print.o: ../../include/vbuf.h
dsn_print.o: ../../include/vstream.h
-dsn_print.o: ../../include/vstring.h
dsn_print.o: dsn.h
-dsn_print.o: dsn_buf.h
dsn_print.o: dsn_print.c
dsn_print.o: dsn_print.h
dsn_print.o: mail_proto.h
log_adhoc.o: ../../include/vstream.h
log_adhoc.o: ../../include/vstring.h
log_adhoc.o: dsn.h
-log_adhoc.o: dsn_buf.h
log_adhoc.o: log_adhoc.c
log_adhoc.o: log_adhoc.h
log_adhoc.o: mail_params.h
mail_copy.o: ../../include/vstream.h
mail_copy.o: ../../include/vstring.h
mail_copy.o: ../../include/vstring_vstream.h
+mail_copy.o: dsn.h
mail_copy.o: dsn_buf.h
mail_copy.o: mail_addr.h
mail_copy.o: mail_copy.c
mark_corrupt.o: ../../include/vstring.h
mark_corrupt.o: deliver_request.h
mark_corrupt.o: dsn.h
-mark_corrupt.o: dsn_buf.h
mark_corrupt.o: mail_params.h
mark_corrupt.o: mail_queue.h
mark_corrupt.o: mark_corrupt.c
mbox_open.o: ../../include/vstring.h
mbox_open.o: deliver_flock.h
mbox_open.o: dot_lockfile.h
+mbox_open.o: dsn.h
mbox_open.o: dsn_buf.h
mbox_open.o: mbox_conf.h
mbox_open.o: mbox_open.c
mime_state.o: mime_state.c
mime_state.o: mime_state.h
mime_state.o: rec_type.h
+mkmap_cdb.o: ../../include/argv.h
+mkmap_cdb.o: ../../include/dict.h
+mkmap_cdb.o: ../../include/dict_cdb.h
+mkmap_cdb.o: ../../include/mymalloc.h
mkmap_cdb.o: ../../include/sys_defs.h
+mkmap_cdb.o: ../../include/vbuf.h
+mkmap_cdb.o: ../../include/vstream.h
+mkmap_cdb.o: mkmap.h
mkmap_cdb.o: mkmap_cdb.c
mkmap_db.o: ../../include/argv.h
mkmap_db.o: ../../include/dict.h
pipe_command.o: ../../include/vbuf.h
pipe_command.o: ../../include/vstream.h
pipe_command.o: ../../include/vstring.h
+pipe_command.o: dsn.h
pipe_command.o: dsn_buf.h
pipe_command.o: dsn_util.h
pipe_command.o: mail_copy.h
rcpt_buf.o: mail_proto.h
rcpt_buf.o: rcpt_buf.c
rcpt_buf.o: rcpt_buf.h
+rcpt_buf.o: recipient_list.h
rcpt_print.o: ../../include/attr.h
rcpt_print.o: ../../include/iostuff.h
rcpt_print.o: ../../include/sys_defs.h
verify.o: ../../include/vstring.h
verify.o: deliver_request.h
verify.o: dsn.h
-verify.o: dsn_buf.h
verify.o: log_adhoc.h
verify.o: mail_params.h
verify.o: mail_proto.h
verify_clnt.o: clnt_stream.h
verify_clnt.o: deliver_request.h
verify_clnt.o: dsn.h
-verify_clnt.o: dsn_buf.h
verify_clnt.o: mail_params.h
verify_clnt.o: mail_proto.h
verify_clnt.o: msg_stats.h
* Global library.
*/
#include <deliver_request.h>
+#include <dsn_buf.h>
/*
* Client interface.
/*
/* typedef struct {
/* .in +4
-/* /* Non-null: rcpt.address, dsn.{status,action,text} */
-/* RECIPIENT rcpt;
-/* DSN dsn;
+/* /* No public members. */
/* .in -4
/* } BOUNCE_LOG;
/*
/* int flags;
/* mode_t mode;
/*
-/* BOUNCE_LOG *bounce_log_read(bp)
+/* BOUNCE_LOG *bounce_log_read(bp, rcpt, dsn)
/* BOUNCE_LOG *bp;
+/* RCPT_BUF *rcpt;
+/* DSN_BUF *dsn;
/*
/* void bounce_log_rewind(bp)
/* BOUNCE_LOG *bp;
/*
-/* BOUNCE_LOG *bounce_log_forge(rcpt, dsn)
-/* RECIPIENT *rcpt;
-/* DSN *dsn;
-/*
/* void bounce_log_close(bp)
/* BOUNCE_LOG *bp;
/* DESCRIPTION
/* are marked as done). The result is 0 in case of success, -1 in case
/* of problems.
/*
-/* bounce_log_forge() forges one recipient status record
-/* without actually accessing a logfile. No copy is made
-/* of strings with recipient or status information.
-/* The result cannot be used for any logfile access operation
-/* and must be disposed of by passing it to bounce_log_close().
-/*
/* bounce_log_close() closes an open bounce or defer logfile and
/* releases memory for the specified handle. The result is non-zero
/* in case of I/O errors.
/* File open flags, as with open(2).
/* .IP mode
/* File permissions, as with open(2).
-/* .PP
-/* Recipient results:
-/* .IP address
-/* The final recipient address in RFC 822 external form, or <>
-/* in case of the null recipient address.
-/* .IP dsn_orcpt
-/* Null pointer or DSN original recipient.
-/* .IP orig_addr
-/* Null pointer or the original recipient address in RFC 822
-/* external form.
-/* .IP dsn_notify
-/* Zero or DSN notify flags.
-/* .IP offset
-/* Zero or queue file offset of recipient record.
-/* .PP
-/* Delivery status results:
-/* .IP dsn_status
-/* RFC 3463-compatible enhanced status code (digit.digits.digits).
-/* .IP dsn_action
-/* "delivered", "failed", "delayed" and so on.
-/* .IP reason
-/* The text that explains why the recipient was undeliverable.
-/* .IP dsn_dtype
-/* Null pointer or RFC 3464-compatible diagnostic type.
-/* .IP dsn_dtext
-/* Null pointer or RFC 3464-compatible diagnostic text.
-/* .IP dsn_mtype
-/* Null pointer or RFC 3464-compatible remote MTA type.
-/* .IP dsn_mname
-/* Null pointer or RFC 3464-compatible remote MTA name.
-/* .PP
-/* Other fields will be added as the code evolves.
+/* .IP rcpt
+/* Recipient buffer. The RECIPIENT member is updated.
+/* .IP dsn
+/* Delivery status information. The DSN member is updated.
/* LICENSE
/* .ad
/* .fi
#include <mail_proto.h>
#include <mail_queue.h>
#include <dsn_mask.h>
-#include <recipient_list.h>
-#include <dsn.h>
#include <bounce_log.h>
/* Application-specific. */
#define STREQ(x,y) (strcmp((x),(y)) == 0)
/*
- * TODO: peek at the first byte to see if this is an old-style log
- * (<recipient>: text) or a new-style extensible log with multiple
- * attributes per recipient. That would not help during a transition from
- * old to new style, where one can expect to find mixed format files.
+ * Logfiles may contain a mixture of old-style (<recipient>: text) and
+ * new-style entries with multiple attributes per recipient.
*
* Kluge up default DSN status and action for old-style logfiles.
*/
bp = (BOUNCE_LOG *) mymalloc(sizeof(*bp));
bp->fp = fp;
bp->buf = vstring_alloc(100);
- bp->rcpt_buf = rcpb_create();
- bp->dsn_buf = dsb_create();
if (STREQ(queue_name, MAIL_QUEUE_DEFER)) {
bp->compat_status = mystrdup("4.0.0");
bp->compat_action = mystrdup("delayed");
/* bounce_log_read - read one record from bounce log file */
-BOUNCE_LOG *bounce_log_read(BOUNCE_LOG *bp)
+BOUNCE_LOG *bounce_log_read(BOUNCE_LOG *bp, RCPT_BUF *rcpt_buf,
+ DSN_BUF *dsn_buf)
{
char *recipient;
char *text;
#define FOUND 1 /* in logfile entry */
/*
- * Initialize. See also DSN_FROM_DSN_BUF() and bounce_log_forge() for
- * null and non-null fields.
+ * Initialize.
*/
state = START;
- bp->rcpt.address = "(recipient address unavailable)";
- bp->dsn.status = bp->compat_status;
- bp->dsn.action = bp->compat_action;
- bp->dsn.reason = "(description unavailable)";
-
- bp->rcpt.orig_addr = 0;
- bp->rcpt.dsn_orcpt = 0;
- bp->rcpt.dsn_notify = 0;
- bp->rcpt.offset = 0;
-
- bp->dsn.dtype = 0;
- bp->dsn.dtext = 0;
- bp->dsn.mtype = 0;
- bp->dsn.mname = 0;
+ rcpb_reset(rcpt_buf);
+ dsb_reset(dsn_buf);
/*
* Support mixed logfile formats to make migration easier. The same file
* backwards compatibility, we even have old format followed by new
* format within the same logfile entry!
*/
- while ((vstring_get_nonl(bp->buf, bp->fp) != VSTREAM_EOF)) {
+ for (;;) {
+ if ((vstring_get_nonl(bp->buf, bp->fp) == VSTREAM_EOF))
+ return (0);
/*
* Logfile entries are separated by blank lines. Even the old ad-hoc
*/
if (STR(bp->buf)[0] == 0) {
if (state == FOUND)
- return (bp);
+ break;
state = START;
continue;
}
char *name;
char *value;
long offset;
- long notify;
+ int notify;
/*
* Split into name and value.
* Save attribute value.
*/
if (STREQ(name, MAIL_ATTR_RECIP)) {
- bp->rcpt.address =
- STR(vstring_strcpy(bp->rcpt_buf->address, *value ?
- value : "(MAILER-DAEMON)"));
+ vstring_strcpy(rcpt_buf->address, *value ?
+ value : "(MAILER-DAEMON)");
} else if (STREQ(name, MAIL_ATTR_ORCPT)) {
- bp->rcpt.orig_addr =
- STR(vstring_strcpy(bp->rcpt_buf->orig_addr, *value ?
- value : "(MAILER-DAEMON)"));
+ vstring_strcpy(rcpt_buf->orig_addr, *value ?
+ value : "(MAILER-DAEMON)");
} else if (STREQ(name, MAIL_ATTR_DSN_ORCPT)) {
- if (*value)
- bp->rcpt.dsn_orcpt =
- STR(vstring_strcpy(bp->rcpt_buf->dsn_orcpt, value));
+ vstring_strcpy(rcpt_buf->dsn_orcpt, value);
} else if (STREQ(name, MAIL_ATTR_DSN_NOTIFY)) {
if ((notify = atoi(value)) > 0 && DSN_NOTIFY_OK(notify))
- bp->rcpt.dsn_notify = notify;
+ rcpt_buf->dsn_notify = notify;
} else if (STREQ(name, MAIL_ATTR_OFFSET)) {
if ((offset = atol(value)) > 0)
- bp->rcpt.offset = offset;
+ rcpt_buf->offset = offset;
} else if (STREQ(name, MAIL_ATTR_DSN_STATUS)) {
- if (*value)
- bp->dsn.status =
- STR(vstring_strcpy(bp->dsn_buf->status, value));
+ vstring_strcpy(dsn_buf->status, value);
} else if (STREQ(name, MAIL_ATTR_DSN_ACTION)) {
- if (*value)
- bp->dsn.action =
- STR(vstring_strcpy(bp->dsn_buf->action, value));
+ vstring_strcpy(dsn_buf->action, value);
} else if (STREQ(name, MAIL_ATTR_DSN_DTYPE)) {
- if (*value)
- bp->dsn.dtype =
- STR(vstring_strcpy(bp->dsn_buf->dtype, value));
+ vstring_strcpy(dsn_buf->dtype, value);
} else if (STREQ(name, MAIL_ATTR_DSN_DTEXT)) {
- if (*value)
- bp->dsn.dtext =
- STR(vstring_strcpy(bp->dsn_buf->dtext, value));
+ vstring_strcpy(dsn_buf->dtext, value);
} else if (STREQ(name, MAIL_ATTR_DSN_MTYPE)) {
- if (*value)
- bp->dsn.mtype =
- STR(vstring_strcpy(bp->dsn_buf->mtype, value));
+ vstring_strcpy(dsn_buf->mtype, value);
} else if (STREQ(name, MAIL_ATTR_DSN_MNAME)) {
- if (*value)
- bp->dsn.mname =
- STR(vstring_strcpy(bp->dsn_buf->mname, value));
+ vstring_strcpy(dsn_buf->mname, value);
} else if (STREQ(name, MAIL_ATTR_WHY)) {
- if (*value)
- bp->dsn.reason =
- STR(vstring_strcpy(bp->dsn_buf->reason, value));
+ vstring_strcpy(dsn_buf->reason, value);
} else {
msg_warn("%s: unknown attribute name: %s, ignored",
VSTREAM_PATH(bp->fp), name);
continue;
}
*cp = 0;
- vstring_strcpy(bp->rcpt_buf->address, *recipient ?
+ vstring_strcpy(rcpt_buf->address, *recipient ?
recipient : "(MAILER-DAEMON)");
- bp->rcpt.address = STR(bp->rcpt_buf->address);
/*
* Find the text that explains why mail was not deliverable.
text = cp + 2;
while (*text && ISSPACE(*text))
text++;
- vstring_strcpy(bp->dsn_buf->reason, text);
- if (*text)
- bp->dsn.reason = STR(bp->dsn_buf->reason);
+ vstring_strcpy(dsn_buf->reason, text);
}
- return (0);
-}
-
-/* bounce_log_forge - forge one recipient status record */
-
-BOUNCE_LOG *bounce_log_forge(RECIPIENT *rcpt, DSN *dsn)
-{
- BOUNCE_LOG *bp;
-
- /*
- * Create a partial record. No point copying information that doesn't
- * need to be.
- */
- bp = (BOUNCE_LOG *) mymalloc(sizeof(*bp));
- bp->fp = 0;
- bp->buf = 0;
- bp->rcpt_buf = 0;
- bp->dsn_buf = 0;
- bp->compat_status = 0;
- bp->compat_action = 0;
-
- bp->rcpt = *rcpt;
- bp->dsn = *dsn;
-
- /*
- * Finalize. See also DSN_FROM_DSN_BUF() and bounce_log_read() for null
- * and non-null fields.
- */
-#define NULL_OR_EMPTY(s) ((s) == 0 || *(s) == 0)
-#define EMPTY_STRING(s) ((s) != 0 && *(s) == 0)
/*
- * Replace null pointers and empty strings by place holders.
+ * Specify place holders for missing fields. See also DSN_FROM_DSN_BUF()
+ * and RECIPIENT_FROM_RCPT_BUF() for null and non-null fields.
*/
- if (bp->rcpt.address == 0)
- bp->rcpt.address = "(recipient address unavailable)";
- if (NULL_OR_EMPTY(bp->dsn.status))
- bp->dsn.status = "(unavailable)";
- if (NULL_OR_EMPTY(bp->dsn.action))
- bp->dsn.action = "(unavailable)";
- if (NULL_OR_EMPTY(bp->dsn.reason))
- bp->dsn.reason = "(description unavailable)";
-
- /*
- * Replace empty strings by null pointers.
- */
- if (EMPTY_STRING(bp->rcpt.orig_addr))
- bp->rcpt.orig_addr = 0;
- if (EMPTY_STRING(bp->rcpt.dsn_orcpt))
- bp->rcpt.dsn_orcpt = 0;
-
- if (EMPTY_STRING(bp->dsn.dtype))
- bp->dsn.dtype = 0;
- if (EMPTY_STRING(bp->dsn.dtext))
- bp->dsn.dtext = 0;
- if (EMPTY_STRING(bp->dsn.mtype))
- bp->dsn.mtype = 0;
- if (EMPTY_STRING(bp->dsn.mname))
- bp->dsn.mname = 0;
+#define BUF_NODATA(buf) (STR(buf)[0] == 0)
+#define BUF_ASSIGN(buf, text) vstring_strcpy((buf), (text))
+
+ if (BUF_NODATA(rcpt_buf->address))
+ BUF_ASSIGN(rcpt_buf->address, "(recipient address unavailable)");
+ if (BUF_NODATA(dsn_buf->status))
+ BUF_ASSIGN(dsn_buf->status, bp->compat_status);
+ if (BUF_NODATA(dsn_buf->action))
+ BUF_ASSIGN(dsn_buf->action, bp->compat_action);
+ if (BUF_NODATA(dsn_buf->reason))
+ BUF_ASSIGN(dsn_buf->reason, "(description unavailable)");
+ (void) RECIPIENT_FROM_RCPT_BUF(rcpt_buf);
+ (void) DSN_FROM_DSN_BUF(dsn_buf);
return (bp);
}
{
int ret;
- if (bp->fp)
- ret = vstream_fclose(bp->fp);
- else
- ret = 0;
- if (bp->buf)
- vstring_free(bp->buf);
- if (bp->rcpt_buf)
- rcpb_free(bp->rcpt_buf);
- if (bp->dsn_buf)
- dsb_free(bp->dsn_buf);
- if (bp->compat_status)
- myfree(bp->compat_status);
- if (bp->compat_action)
- myfree(bp->compat_action);
+ ret = vstream_fclose(bp->fp);
+ vstring_free(bp->buf);
+ myfree(bp->compat_status);
+ myfree(bp->compat_action);
myfree((char *) bp);
return (ret);
#include <recipient_list.h>
#include <rcpt_buf.h>
#include <dsn_buf.h>
-#include <dsn.h>
/*
* External interface.
*/
typedef struct {
- /* Private. */
VSTREAM *fp; /* open file */
VSTRING *buf; /* I/O buffer */
- RCPT_BUF *rcpt_buf; /* recipient info */
- DSN_BUF *dsn_buf; /* delivery status */
char *compat_status; /* old logfile compatibility */
char *compat_action; /* old logfile compatibility */
- /* Public. */
- RECIPIENT rcpt; /* recipient info */
- DSN dsn; /* delivery status */
} BOUNCE_LOG;
extern BOUNCE_LOG *bounce_log_open(const char *, const char *, int, mode_t);
-extern BOUNCE_LOG *bounce_log_read(BOUNCE_LOG *);
+extern BOUNCE_LOG *bounce_log_read(BOUNCE_LOG *, RCPT_BUF *, DSN_BUF *);
extern BOUNCE_LOG *bounce_log_delrcpt(BOUNCE_LOG *);
-extern BOUNCE_LOG *bounce_log_forge(RECIPIENT *, DSN *);
extern int bounce_log_close(BOUNCE_LOG *);
#define bounce_log_rewind(bp) vstream_fseek((bp)->fp, 0L, SEEK_SET)
#include <deliver_pass.h>
#include <dsb_scan.h>
#include <defer.h>
+#include <rcpt_print.h>
#define DELIVER_PASS_DEFER 1
#define DELIVER_PASS_UNKNOWN 2
ATTR_TYPE_STR, MAIL_ATTR_SASL_USERNAME, request->sasl_username,
ATTR_TYPE_STR, MAIL_ATTR_SASL_SENDER, request->sasl_sender,
ATTR_TYPE_STR, MAIL_ATTR_RWR_CONTEXT, request->rewrite_context,
- ATTR_TYPE_LONG, MAIL_ATTR_OFFSET, rcpt->offset,
- ATTR_TYPE_STR, MAIL_ATTR_DSN_ORCPT, rcpt->dsn_orcpt,
- ATTR_TYPE_NUM, MAIL_ATTR_DSN_NOTIFY, rcpt->dsn_notify,
- ATTR_TYPE_STR, MAIL_ATTR_ORCPT, rcpt->orig_addr,
- ATTR_TYPE_STR, MAIL_ATTR_RECIP, rcpt->address,
- ATTR_TYPE_NUM, MAIL_ATTR_OFFSET, 0,
+ ATTR_TYPE_NUM, MAIL_ATTR_RCPT_COUNT, 1,
+ ATTR_TYPE_END);
+ attr_print(stream, ATTR_FLAG_NONE,
+ ATTR_TYPE_FUNC, rcpt_print, (void *) rcpt,
ATTR_TYPE_END);
if (vstream_fflush(stream)) {
*/
if (deliver_pass_initial_reply(stream) != 0
|| deliver_pass_send_request(stream, request, nexthop, rcpt) != 0) {
- DSN_SMTP(&dsn, "4.3.0",
- "451 mail transport unavailable",
- "mail transport unavailable");
+ (void) DSN_SIMPLE(&dsn, "4.3.0", "mail transport unavailable");
status = defer_append(DEL_REQ_TRACE_FLAGS(request->flags),
request->queue_id, &request->msg_stats,
rcpt, "none", &dsn);
} else if ((status = deliver_pass_final_reply(stream, dsb))
== DELIVER_PASS_UNKNOWN) {
- DSN_SMTP(&dsn, "4.3.0",
- "451 unknown mail transport error",
- "unknown mail transport error");
+ (void) DSN_SIMPLE(&dsn, "4.3.0", "unknown mail transport error");
status = defer_append(DEL_REQ_TRACE_FLAGS(request->flags),
request->queue_id, &request->msg_stats,
rcpt, "none", &dsn);
RECIPIENT *rcpt;
int status = 0;
+ /*
+ * XXX We should find out if the target transport can handle
+ * multi-recipient requests. Unfortunately such code is hard to test,
+ * rarely used, and therefore will be buggy.
+ */
list = &request->rcpt_list;
for (rcpt = list->info; rcpt < list->info + list->len; rcpt++)
status |= deliver_pass(class, service, request, rcpt);
#include "dsn.h"
#include "dsn_print.h"
#include "deliver_request.h"
+#include "rcpt_buf.h"
/* deliver_request_initial - send initial status code */
{
DSN *hop_status;
int err;
+ /* XXX This DSN structure initialization bypasses integrity checks. */
static DSN dummy_dsn = {"", "", "", "", "", "", ""};
/*
* Send the status and the optional reason.
*/
-#define STRING_OR_EMPTY(s) ((s) ? (s) : "")
-
if ((hop_status = request->hop_status) == 0)
hop_status = &dummy_dsn;
if (msg_verbose)
static VSTRING *queue_id;
static VSTRING *nexthop;
static VSTRING *encoding;
- static VSTRING *dsn_orcpt;
- static VSTRING *orig_addr;
static VSTRING *address;
static VSTRING *client_name;
static VSTRING *client_addr;
static VSTRING *sasl_sender;
static VSTRING *rewrite_context;
static VSTRING *dsn_envid;
- long offset;
+ static RCPT_BUF *rcpt_buf;
+ int rcpt_count;
int dsn_ret;
- int dsn_notify;
/*
* Initialize. For some reason I wanted to allow for multiple instances
queue_id = vstring_alloc(10);
nexthop = vstring_alloc(10);
encoding = vstring_alloc(10);
- dsn_orcpt = vstring_alloc(10);
- orig_addr = vstring_alloc(10);
- address = vstring_alloc(10);
+ address = vstring_alloc(10);
client_name = vstring_alloc(10);
client_addr = vstring_alloc(10);
client_proto = vstring_alloc(10);
sasl_sender = vstring_alloc(10);
rewrite_context = vstring_alloc(10);
dsn_envid = vstring_alloc(10);
+ rcpt_buf = rcpb_create();
}
/*
* Extract the queue file name, data offset, and sender address. Abort
* the conversation when they send bad information.
*/
- if (attr_scan(stream, ATTR_FLAG_STRICT | ATTR_FLAG_MORE,
+ if (attr_scan(stream, ATTR_FLAG_STRICT,
ATTR_TYPE_NUM, MAIL_ATTR_FLAGS, &request->flags,
ATTR_TYPE_STR, MAIL_ATTR_QUEUE, queue_name,
ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, queue_id,
ATTR_TYPE_STR, MAIL_ATTR_SASL_USERNAME, sasl_username,
ATTR_TYPE_STR, MAIL_ATTR_SASL_SENDER, sasl_sender,
ATTR_TYPE_STR, MAIL_ATTR_RWR_CONTEXT, rewrite_context,
- ATTR_TYPE_END) != 19) {
+ ATTR_TYPE_NUM, MAIL_ATTR_RCPT_COUNT, &rcpt_count,
+ ATTR_TYPE_END) != 20) {
msg_warn("%s: error receiving common attributes", myname);
return (-1);
}
* Extract the recipient offset and address list. Skip over any
* attributes from the sender that we do not understand.
*/
- for (;;) {
- if (attr_scan(stream, ATTR_FLAG_MORE | ATTR_FLAG_STRICT,
- ATTR_TYPE_LONG, MAIL_ATTR_OFFSET, &offset,
+ while (rcpt_count-- > 0) {
+ if (attr_scan(stream, ATTR_FLAG_STRICT,
+ ATTR_TYPE_FUNC, rcpb_scan, (void *) rcpt_buf,
ATTR_TYPE_END) != 1) {
- msg_warn("%s: error receiving offset attribute", myname);
- return (-1);
- }
- if (offset == 0)
- break;
- if (attr_scan(stream, ATTR_FLAG_MORE | ATTR_FLAG_STRICT,
- ATTR_TYPE_STR, MAIL_ATTR_DSN_ORCPT, dsn_orcpt,
- ATTR_TYPE_NUM, MAIL_ATTR_DSN_NOTIFY, &dsn_notify,
- ATTR_TYPE_STR, MAIL_ATTR_ORCPT, orig_addr,
- ATTR_TYPE_STR, MAIL_ATTR_RECIP, address,
- ATTR_TYPE_END) != 4) {
msg_warn("%s: error receiving recipient attributes", myname);
return (-1);
}
- recipient_list_add(&request->rcpt_list, offset,
- vstring_str(dsn_orcpt), dsn_notify,
- vstring_str(orig_addr),
- vstring_str(address));
+ recipient_list_add(&request->rcpt_list, rcpt_buf->offset,
+ vstring_str(rcpt_buf->dsn_orcpt),
+ rcpt_buf->dsn_notify,
+ vstring_str(rcpt_buf->orig_addr),
+ vstring_str(rcpt_buf->address));
}
if (request->rcpt_list.len <= 0) {
msg_warn("%s: no recipients in delivery request for destination %s",
/*
/* typedef struct {
/* .in +4
-/* char *status; /* RFC 3463 status */
-/* char *action; /* null or RFC 3464 action */
-/* char *reason; /* human-readable text */
-/* char *dtype; /* null or diagnostic type */
-/* char *dtext; /* null or diagnostic code */
-/* char *mtype; /* null or MTA type */
-/* char *mname; /* null or remote MTA */
+/* const char *status; /* RFC 3463 status */
+/* const char *action; /* null or RFC 3464 action */
+/* const char *reason; /* human-readable text */
+/* const char *dtype; /* null or diagnostic type */
+/* const char *dtext; /* null or diagnostic code */
+/* const char *mtype; /* null or MTA type */
+/* const char *mname; /* null or remote MTA */
/* .in -4
/* } DSN;
/*
-/* DSN *create(status, action, reason, dtype, dtext, mtype, mname)
+/* DSN *create(status, action, reason, dtype, dtext, mtype, mname)
/* const char *status;
/* const char *action;
/* const char *reason;
/* const char *mtype;
/* const char *mname;
/*
-/* DSN *copy(dsn)
-/* DSN *dsn;
+/* DSN *DSN_COPY(dsn)
+/* DSN *dsn;
/*
-/* DSN *DSN_ASSIGN(dsn, status, action, reason, dtype, dtext, mtype, mname)
-/* DSN *dsn;
+/* void dsn_free(dsn)
+/* DSN *dsn;
+/*
+/* DSN *DSN_ASSIGN(dsn, status, action, reason, dtype, dtext,
+/* mtype, mname)
+/* DSN *dsn;
/* const char *status;
/* const char *action;
/* const char *reason;
/* const char *mtype;
/* const char *mname;
/*
-/* DSN *DSN_SIMPLE(dsn, status, action, reason)
-/* DSN *dsn;
+/* DSN *DSN_SIMPLE(dsn, status, action, reason)
+/* DSN *dsn;
/* const char *status;
/* const char *action;
/* const char *reason;
-/*
-/* DSN *DSN_SMTP(dsn, status, action, smtp_dtext, reason)
-/* DSN *dsn;
-/* const char *status;
-/* const char *action;
-/* const char *smtp_dtext;
-/* const char *reason;
-/*
-/* void dsn_free(dsn)
-/* DSN *dsn;
/* DESCRIPTION
/* This module maintains delivery error information. For a
/* description of structure field members see "Arguments"
/* for stack-based short-lived storage.
/*
/* DSN_SIMPLE() takes the minimally required subset of all the
-/* parameters and resets the rest to zero.
-/*
-/* DSN_SMTP() handles the common case of an SMTP-type
-/* diagnostic code that was generated by the local MTA.
+/* attributes and sets the rest to empty strings.
+/* This is a wrapper around the DSN_ASSIGN() macro.
/*
/* Arguments:
/* .IP reason
/* .IP status
/* Enhanced status code as specified in RFC 3463.
/* .IP action
-/* Action as defined in RFC 3464. If null, a default action
-/* is chosen.
+/* DSN_NO_ACTION, empty string, or action as defined in RFC 3464.
+/* If no action is specified, a default action is chosen.
/* .IP dtype
/* DSN_NO_DTYPE, empty string, or diagnostic code type as
/* specified in RFC 3464.
/* DSN_NO_MNAME, empty string, or remote MTA as specified in
/* RFC 3464.
/* DIAGNOSTICS
-/* Panic: null status or reason.
+/* Panic: null or empty status or reason.
/* Fatal: out of memory.
/* LICENSE
/* .ad
dsn = (DSN *) mymalloc(sizeof(*dsn));
/*
- * Status and reason must not be null. Other members may be null pointers
- * or empty strings. Null/empty members are represented as null pointers.
+ * Status and reason must not be empty. Other members may be empty
+ * strings.
+ *
+ * Early implementations represented unavailable information with null
+ * pointers. This resulted in code that was difficult to maintain. We now
+ * use empty strings instead. For safety sake we keep the null pointer
+ * test for input, but we always convert to empty string on output.
*/
#define NULL_OR_EMPTY(s) ((s) == 0 || *(s) == 0)
dsn->status = mystrdup(status);
if (NULL_OR_EMPTY(action))
- dsn->action = 0;
+ dsn->action = mystrdup("");
else
dsn->action = mystrdup(action);
dsn->reason = mystrdup(reason);
if (NULL_OR_EMPTY(dtype) || NULL_OR_EMPTY(dtext)) {
- dsn->dtype = 0;
- dsn->dtext = 0;
+ dsn->dtype = mystrdup("");
+ dsn->dtext = mystrdup("");
} else {
dsn->dtype = mystrdup(dtype);
dsn->dtext = mystrdup(dtext);
}
if (NULL_OR_EMPTY(mtype) || NULL_OR_EMPTY(mname)) {
- dsn->mtype = 0;
- dsn->mname = 0;
+ dsn->mtype = mystrdup("");
+ dsn->mname = mystrdup("");
} else {
dsn->mtype = mystrdup(mtype);
dsn->mname = mystrdup(mname);
void dsn_free(DSN *dsn)
{
myfree((char *) dsn->status);
- if (dsn->action)
- myfree((char *) dsn->action);
+ myfree((char *) dsn->action);
myfree((char *) dsn->reason);
- if (dsn->dtype)
- myfree((char *) dsn->dtype);
- if (dsn->dtext)
- myfree((char *) dsn->dtext);
- if (dsn->mtype)
- myfree((char *) dsn->mtype);
- if (dsn->mname)
- myfree((char *) dsn->mname);
+ myfree((char *) dsn->dtype);
+ myfree((char *) dsn->dtext);
+ myfree((char *) dsn->mtype);
+ myfree((char *) dsn->mname);
myfree((char *) dsn);
}
/* DESCRIPTION
/* .nf
- /*
- * Global library.
- */
-#include <dsn_buf.h>
-
/*
* External interface.
*/
const char *mname; /* Null / RFC 3464 remote MTA */
} DSN;
- /*
- * Ditto, without const poisoning.
- */
-typedef struct {
- char *status; /* RFC 3463 status */
- char *action; /* Null / RFC 3464 action */
- char *reason; /* descriptive reason */
- char *dtype; /* Null / RFC 3464 diagnostic type */
- char *dtext; /* Null / RFC 3464 diagnostic code */
- char *mtype; /* Null / RFC 3464 MTA type */
- char *mname; /* Null / RFC 3464 remote MTA */
-} DSN_VAR;
-
extern DSN *dsn_create(const char *, const char *, const char *, const char *,
const char *, const char *, const char *);
extern void dsn_free(DSN *);
#define DSN_SIMPLE(dsn, _status, _reason) \
(((dsn)->status = (_status)), \
- ((dsn)->action = 0), \
+ ((dsn)->action = DSN_NO_ACTION), \
((dsn)->reason = (_reason)), \
- ((dsn)->dtype = 0), \
- ((dsn)->dtext = 0), \
- ((dsn)->mtype = 0), \
- ((dsn)->mname = 0), \
+ ((dsn)->dtype = DSN_NO_DTYPE), \
+ ((dsn)->dtext = DSN_NO_DTEXT), \
+ ((dsn)->mtype = DSN_NO_MTYPE), \
+ ((dsn)->mname = DSN_NO_MNAME), \
(dsn))
-#define DSN_SMTP(dsn, _status, _dtext, _reason) \
- (((dsn)->status = (_status)), \
- ((dsn)->action = 0), \
- ((dsn)->reason = (_reason)), \
- ((dsn)->dtype = DSB_DTYPE_SMTP), \
- ((dsn)->dtext = _dtext), \
- ((dsn)->mtype = 0), \
- ((dsn)->mname = 0), \
- (dsn))
-
-#define DSN_NO_DTYPE 0
-#define DSN_NO_DTEXT 0
-#define DSN_NO_MTYPE 0
-#define DSN_NO_MNAME 0
+#define DSN_NO_ACTION ""
+#define DSN_NO_DTYPE ""
+#define DSN_NO_DTEXT ""
+#define DSN_NO_MTYPE ""
+#define DSN_NO_MNAME ""
/*
- * In order to save space in the queue manager, some DSN fields may be null
- * pointers so that we don't waste memory making copies of empty strings. In
- * addition, sanity requires that the status and reason are never null or
+ * Early implementations represented unavailable information with null
+ * pointers. This resulted in code that is hard to maintain. We now use
+ * empty strings instead. This does not waste precious memory as long as we
+ * can represent empty strings efficiently by collapsing them.
+ *
+ * The only restriction left is that the status and reason are never null or
* empty; this is enforced by dsn_create() which is invoked by DSN_COPY().
- * This complicates the bounce_log(3) and bounce(8) daemons, as well as the
- * server reply parsing code in the smtp(8) and lmtp(8) clients. They must
- * be able to cope with null pointers, and they must never supply empty
- * strings for the required fields.
+ * This complicates the server reply parsing code in the smtp(8) and lmtp(8)
+ * clients. they must never supply empty strings for these required fields.
*/
#define DSN_COPY(dsn) \
dsn_create((dsn)->status, (dsn)->action, (dsn)->reason, \
(dsn)->dtype, (dsn)->dtext, \
(dsn)->mtype, (dsn)->mname)
-#define DSN_STRING_OR_NULL(s) ((s)[0] ? (s) : 0)
-
-#define DSN_FROM_DSN_BUF(dsn, dsb) \
- DSN_ASSIGN((dsn), vstring_str((dsb)->status), \
- DSN_STRING_OR_NULL(vstring_str((dsb)->action)), \
- vstring_str((dsb)->reason), \
- DSN_STRING_OR_NULL(vstring_str((dsb)->dtype)), \
- DSN_STRING_OR_NULL(vstring_str((dsb)->dtext)), \
- DSN_STRING_OR_NULL(vstring_str((dsb)->mtype)), \
- DSN_STRING_OR_NULL(vstring_str((dsb)->mname)))
-
/* LICENSE
/* .ad
/* .fi
/*++
/* NAME
-/* dsbuf 3
+/* dsn_buf 3
/* SUMMARY
/* delivery status buffer
/* SYNOPSIS
/*
/* typedef struct {
/* .in +4
+/* /* Convenience member */
+/* DSN dsn; /* light-weight, dsn(3) */
+/* /* Formal members... */
/* VSTRING *status; /* RFC 3463 */
/* VSTRING *action; /* RFC 3464 */
/* VSTRING *mtype; /* dns */
/* VSTRING *mname; /* host or domain */
/* VSTRING *dtype; /* smtp, x-unix */
-/* int dcode; /* RFC 2821, sysexits.h */
/* VSTRING *dtext; /* RFC 2821, sysexits.h */
+/* /* Informal members... */
/* VSTRING *reason; /* informal text */
/* .in -4
/* } DSN_BUF;
/*
/* DSN_BUF *dsb_create(void)
/*
-/* DSN_BUF *dsb_update(dsb, status, action, mtype, mname, dtype, dcode,
+/* DSN_BUF *dsb_update(dsb, status, action, mtype, mname, dtype,
/* dtext, reason_fmt, ...)
/* DSN_BUF *dsb;
/* const char *status;
/* const char *mtype;
/* const char *mname;
/* const char *dtype;
-/* int dcode;
/* const char *dtext;
/* const char *reason_fmt;
/*
/* const char *status;
/* const char *reason_fmt;
/*
-/* DSN_BUF *dsb_unix(dsb, status, dcode, dtext, reason_fmt, ...)
+/* DSN_BUF *dsb_unix(dsb, status, dtext, reason_fmt, ...)
/* DSN_BUF *dsb;
/* const char *status;
/* const char *reason_fmt;
/*
-/* DSN_BUF *dsb_smtp(dsb, status, dcode, dtext, reason_fmt, ...)
-/* DSN_BUF *dsb;
-/* const char *status;
-/* const char *reason_fmt;
-/*
-/* DSN_BUF *dsb_formal(dsb, status, action, mtype, mname, dtype, dcode,
+/* DSN_BUF *dsb_formal(dsb, status, action, mtype, mname, dtype,
/* dtext)
/* DSN_BUF *dsb;
/* const char *status;
/* const char *mtype;
/* const char *mname;
/* const char *dtype;
-/* int dcode;
/* const char *dtext;
/*
/* DSN_BUF *dsb_status(dsb, status)
/* const char *status;
/*
/* void dsb_reset(dsb)
+/* DSN_BUF *dsb;
/*
/* void dsb_free(dsb)
+/* DSN_BUF *dsb;
+/*
+/* DSN *DSN_FROM_DSN_BUF(dsb)
+/* DSN_BUF *dsb;
/* DESCRIPTION
/* This module implements a simple to update delivery status
-/* buffer for Postfix-internal use. Typically it is populated
-/* in course of delivery attempt, and then formatted into a
+/* buffer for Postfix-internal use. Typically it is filled in
+/* the course of delivery attempt, and then formatted into a
/* DSN structure for external notification.
/*
/* dsb_create() creates initialized storage for formal RFC 3464
/* text, and informal text, sets the diagnostic type to UNIX,
/* and resets all other fields to defaults.
/*
-/* dsb_smtp() does the same for SMTP style diagnostics.
-/*
/* dsb_formal() updates all fields except the informal text.
/*
/* dsb_status() updates the status field, and resets all
/* dsb_free() recycles the storage that was allocated by
/* dsb_create(), and so on.
/*
+/* DSN_FROM_DSN_BUF() populates the DSN member with a shallow
+/* copy of the contents of the formal and informal fields, and
+/* returns a pointer to the DSN member. This is typically used
+/* for external reporting.
+/*
/* Arguments:
/* .IP dsb
/* Delivery status buffer.
/* matter here are "expanded" and "relayed"; all other values
/* are already implied by the context.
/* .IP mtype
+/* The remote MTA type.
/* The only valid type is DSB_MTYPE_DNS. The macro DSB_SKIP_RMTA
/* conveniently expands into a null argument list for the
/* remote MTA type and name.
/* .IP mname
/* Remote MTA name.
/* .IP dtype
+/* The reply type.
/* DSB_DTYPE_SMTP or DSB_DTYPE_UNIX. The macro DSB_SKIP_REPLY
/* conveniently expands into a null argument list for the reply
-/* type, code and text.
-/* .IP dcode
-/* Numerical reply code. The reply code is reset when dtype is
-/* DSB_SKIP_REPLY.
+/* type and text.
/* .IP dtext
/* The reply text. The reply text is reset when dtype is
/* DSB_SKIP_REPLY.
-/* .IP reply_fmt
-/* The reply text format. The reply text is reset when type
-/* is DSB_SKIP_REPLY.
/* .IP reason_fmt
/* The informal reason format.
/* SEE ALSO
dsb->mtype = vstring_alloc(10);
dsb->mname = vstring_alloc(100);
dsb->dtype = vstring_alloc(10);
- dsb->dcode = 0;
dsb->dtext = vstring_alloc(100);
dsb->reason = vstring_alloc(100);
myfree((char *) dsb);
}
+ /*
+ * Initial versions of this code represented unavailable inputs with null
+ * pointers, which produced fragile and hard to maintain code. The current
+ * code uses empty strings instead of null pointers.
+ *
+ * For safety we keep the test for null pointers in input. It's cheap.
+ */
#define DSB_TRUNCATE(s) (STR(s)[0] = 0)
+#define NULL_OR_EMPTY(s) ((s) == 0 || *(s) == 0)
+
#define DSB_ACTION(dsb, stat, act) \
- vstring_strcpy((dsb)->action, (act) && *(act) ? (act) : "")
+ vstring_strcpy((dsb)->action, !NULL_OR_EMPTY(act) ? (act) : "")
#define DSB_MTA(dsb, type, name) do { \
- if ((type) == 0) { \
+ if (NULL_OR_EMPTY(type) || NULL_OR_EMPTY(name)) { \
DSB_TRUNCATE((dsb)->mtype); \
DSB_TRUNCATE((dsb)->mname); \
} else { \
} \
} while (0)
+#define DSB_DIAG(dsb, type, text) do { \
+ if (NULL_OR_EMPTY(type) || NULL_OR_EMPTY(text)) { \
+ DSB_TRUNCATE((dsb)->dtype); \
+ DSB_TRUNCATE((dsb)->dtext); \
+ } else { \
+ vstring_strcpy((dsb)->dtype, (type)); \
+ vstring_strcpy((dsb)->dtext, (text)); \
+ } \
+} while (0)
+
/* dsb_update - update formal attributes and informal text */
DSN_BUF *dsb_update(DSN_BUF *dsb, const char *status, const char *action,
const char *mtype, const char *mname,
- const char *dtype, int dcode, const char *dtext,
+ const char *dtype, const char *dtext,
const char *format,...)
{
va_list ap;
vstring_strcpy(dsb->status, status);
DSB_ACTION(dsb, status, action);
DSB_MTA(dsb, mtype, mname);
- if (dtype == 0) {
- DSB_TRUNCATE(dsb->dtype);
- dsb->dcode = 0;
- DSB_TRUNCATE(dsb->dtext);
- } else {
- vstring_strcpy(dsb->dtype, dtype);
- dsb->dcode = dcode;
- vstring_strcpy(dsb->dtext, dtext);
- }
+ DSB_DIAG(dsb, dtype, dtext);
va_start(ap, format);
vstring_vsprintf(dsb->reason, format, ap);
va_end(ap);
return (dsb);
}
-/* dsb_simple - update status and text */
+/* dsb_simple - update status and informal text */
DSN_BUF *dsb_simple(DSN_BUF *dsb, const char *status, const char *format,...)
{
DSB_TRUNCATE(dsb->mtype);
DSB_TRUNCATE(dsb->mname);
DSB_TRUNCATE(dsb->dtype);
- dsb->dcode = 0;
DSB_TRUNCATE(dsb->dtext);
va_start(ap, format);
vstring_vsprintf(dsb->reason, format, ap);
return (dsb);
}
-/* dsb_unix - update status, UNIX diagnostic and text */
+/* dsb_unix - update status, UNIX diagnostic and informal text */
-DSN_BUF *dsb_unix(DSN_BUF *dsb, const char *status, int dcode,
+DSN_BUF *dsb_unix(DSN_BUF *dsb, const char *status,
const char *dtext, const char *format,...)
{
va_list ap;
DSB_TRUNCATE(dsb->mtype);
DSB_TRUNCATE(dsb->mname);
vstring_strcpy(dsb->dtype, DSB_DTYPE_UNIX);
- dsb->dcode = dcode;
- vstring_strcpy(dsb->dtext, dtext);
- va_start(ap, format);
- vstring_vsprintf(dsb->reason, format, ap);
- va_end(ap);
-
- return (dsb);
-}
-
-/* dsb_smtp - update status, SMTP diagnostic and text */
-
-DSN_BUF *dsb_smtp(DSN_BUF *dsb, const char *status, int dcode,
- const char *dtext, const char *format,...)
-{
- va_list ap;
-
- vstring_strcpy(dsb->status, status);
- DSB_TRUNCATE(dsb->action);
- DSB_TRUNCATE(dsb->mtype);
- DSB_TRUNCATE(dsb->mname);
- vstring_strcpy(dsb->dtype, DSB_DTYPE_SMTP);
- dsb->dcode = dcode;
vstring_strcpy(dsb->dtext, dtext);
va_start(ap, format);
vstring_vsprintf(dsb->reason, format, ap);
DSN_BUF *dsb_formal(DSN_BUF *dsb, const char *status, const char *action,
const char *mtype, const char *mname,
- const char *dtype, int dcode,
- const char *dtext)
+ const char *dtype, const char *dtext)
{
vstring_strcpy(dsb->status, status);
DSB_ACTION(dsb, status, action);
DSB_MTA(dsb, mtype, mname);
- if (dtype == 0) {
- DSB_TRUNCATE(dsb->dtype);
- dsb->dcode = 0;
- DSB_TRUNCATE(dsb->dtext);
- } else {
- vstring_strcpy(dsb->dtype, dtype);
- dsb->dcode = dcode;
- vstring_strcpy(dsb->dtext, dtext);
- }
+ DSB_DIAG(dsb, dtype, dtext);
return (dsb);
}
DSB_TRUNCATE(dsb->mtype);
DSB_TRUNCATE(dsb->mname);
DSB_TRUNCATE(dsb->dtype);
- dsb->dcode = 0;
DSB_TRUNCATE(dsb->dtext);
return (dsb);
}
DSB_TRUNCATE(dsb->mtype);
DSB_TRUNCATE(dsb->mname);
DSB_TRUNCATE(dsb->dtype);
- dsb->dcode = 0;
DSB_TRUNCATE(dsb->dtext);
DSB_TRUNCATE(dsb->reason);
}
/*++
/* NAME
-/* dsbuf 3h
+/* dsn_buf 3h
/* SUMMARY
-/* DSN support routines
+/* delivery status buffer
/* SYNOPSIS
/* #include <dsn_buf.h>
/* DESCRIPTION
*/
#include <vstring.h>
+ /*
+ * Global library.
+ */
+#include <dsn.h>
+
/*
* Delivery status buffer, Postfix-internal form.
*/
typedef struct {
+ DSN dsn; /* convenience */
+ /* Formal members. */
VSTRING *status; /* RFC 3463 */
VSTRING *action; /* RFC 3464 */
VSTRING *mtype; /* null or remote MTA type */
VSTRING *mname; /* null or remote MTA name */
- VSTRING *dtype; /* null, smtp, x-unix-command */
- int dcode; /* null, RFC 2821, sysexits.h */
+ VSTRING *dtype; /* null, smtp, x-unix */
VSTRING *dtext; /* null, RFC 2821, sysexits.h */
+ /* Informal free text. */
VSTRING *reason; /* free text */
} DSN_BUF;
#define DSB_MTYPE_NONE ((char *) 0)
#define DSB_MTYPE_DNS "dns" /* RFC 2821 */
-#define DSB_SKIP_REPLY (char *) 0, (int) 0, " " /* XXX Bogus? */
+#define DSB_SKIP_REPLY (char *) 0, " " /* XXX Bogus? */
#define DSB_DTYPE_NONE ((char *) 0)
#define DSB_DTYPE_SMTP "smtp" /* RFC 2821 */
#define DSB_DTYPE_UNIX "x-unix" /* sysexits.h */
#define DSB_DTYPE_SASL "x-sasl" /* libsasl */
extern DSN_BUF *dsb_create(void);
-extern DSN_BUF *PRINTFLIKE(9, 10) dsb_update(DSN_BUF *, const char *, const char *, const char *, const char *, const char *, int, const char *, const char *,...);
+extern DSN_BUF *PRINTFLIKE(8, 9) dsb_update(DSN_BUF *, const char *, const char *, const char *, const char *, const char *, const char *, const char *,...);
extern DSN_BUF *PRINTFLIKE(3, 4) dsb_simple(DSN_BUF *, const char *, const char *,...);
-extern DSN_BUF *PRINTFLIKE(5, 6) dsb_smtp(DSN_BUF *, const char *, int, const char *, const char *,...);
-extern DSN_BUF *PRINTFLIKE(5, 6) dsb_unix(DSN_BUF *, const char *, int, const char *, const char *,...);
-extern DSN_BUF *PRINTFLIKE(5, 6) dsb_smtp(DSN_BUF *, const char *, int, const char *, const char *,...);
-extern DSN_BUF *dsb_formal(DSN_BUF *, const char *, const char *, const char *, const char *, const char *, int, const char *);
+extern DSN_BUF *PRINTFLIKE(4, 5) dsb_unix(DSN_BUF *, const char *, const char *, const char *,...);
+extern DSN_BUF *PRINTFLIKE(4, 5) dsb_smtp(DSN_BUF *, const char *, const char *, const char *,...);
+extern DSN_BUF *dsb_formal(DSN_BUF *, const char *, const char *, const char *, const char *, const char *, const char *);
extern DSN_BUF *dsb_status(DSN_BUF *, const char *);
extern void dsb_reset(DSN_BUF *);
extern void dsb_free(DSN_BUF *);
+ /*
+ * Early implementations of the DSN structure represented unavailable
+ * information with null pointers. This resulted in hard to maintain code.
+ * We now use empty strings instead, so there is no need anymore to convert
+ * empty strings to null pointers in the macro below.
+ */
+#define DSN_FROM_DSN_BUF(dsb) \
+ DSN_ASSIGN(&(dsb)->dsn, \
+ vstring_str((dsb)->status), \
+ vstring_str((dsb)->action), \
+ vstring_str((dsb)->reason), \
+ vstring_str((dsb)->dtype), \
+ vstring_str((dsb)->dtext), \
+ vstring_str((dsb)->mtype), \
+ vstring_str((dsb)->mname))
+
/* LICENSE
/* .ad
/* .fi
DSN *dsn = (DSN *) ptr;
int ret;
-#define S(s) ((s) ? (s) : "")
-
/*
* The attribute order is determined by backwards compatibility. It can
* be sanitized after all the ad-hoc DSN read/write code is replaced.
*/
ret = print_fn(fp, flags | ATTR_FLAG_MORE,
ATTR_TYPE_STR, MAIL_ATTR_DSN_STATUS, dsn->status,
- ATTR_TYPE_STR, MAIL_ATTR_DSN_DTYPE, S(dsn->dtype),
- ATTR_TYPE_STR, MAIL_ATTR_DSN_DTEXT, S(dsn->dtext),
- ATTR_TYPE_STR, MAIL_ATTR_DSN_MTYPE, S(dsn->mtype),
- ATTR_TYPE_STR, MAIL_ATTR_DSN_MNAME, S(dsn->mname),
- ATTR_TYPE_STR, MAIL_ATTR_DSN_ACTION, S(dsn->action),
+ ATTR_TYPE_STR, MAIL_ATTR_DSN_DTYPE, dsn->dtype,
+ ATTR_TYPE_STR, MAIL_ATTR_DSN_DTEXT, dsn->dtext,
+ ATTR_TYPE_STR, MAIL_ATTR_DSN_MTYPE, dsn->mtype,
+ ATTR_TYPE_STR, MAIL_ATTR_DSN_MNAME, dsn->mname,
+ ATTR_TYPE_STR, MAIL_ATTR_DSN_ACTION, dsn->action,
ATTR_TYPE_STR, MAIL_ATTR_WHY, dsn->reason,
ATTR_TYPE_END);
return (ret);
/*
/* typedef struct {
/* .in +4
-/* DSN_BUF dsn; /* RFC 3463 status */
+/* DSN_STAT dsn; /* RFC 3463 status */
/* const char *text; /* Free text */
/* .in -4
/* } DSN_SPLIT;
*/
typedef struct {
char data[DSN_SIZE]; /* NOT a public interface */
-} DSN_BUFFER;
+} DSN_STAT;
#define DSN_UPDATE(dsn_buf, dsn, len) do { \
if (len >= sizeof((dsn_buf).data)) \
* Split flat text into detail code and free text.
*/
typedef struct {
- DSN_BUFFER dsn; /* RFC 3463 status */
+ DSN_STAT dsn; /* RFC 3463 status */
const char *text; /* free text */
} DSN_SPLIT;
if (why && read_error)
dsb_unix(why, TRY_AGAIN_ERROR(errno) ? "4.3.0" : "5.3.0",
- EX_IOERR, sys_exits_detail(EX_IOERR)->text,
+ sys_exits_detail(EX_IOERR)->text,
"error reading message: %m");
if (why && write_error)
dsb_unix(why, mbox_dsn(errno, "5.3.0"),
- EX_IOERR, sys_exits_detail(EX_IOERR)->text,
+ sys_exits_detail(EX_IOERR)->text,
"error writing message: %m");
/*
#define DEF_LMTP_SASL_TLS_OPTS "$" VAR_LMTP_SASL_OPTS
extern char *var_smtp_sasl_tls_opts;
+#define VAR_SMTP_SASL_TLSV_OPTS "smtp_sasl_tls_verified_security_options"
+#define DEF_SMTP_SASL_TLSV_OPTS "$" VAR_SMTP_SASL_TLS_OPTS
+#define VAR_LMTP_SASL_TLSV_OPTS "lmtp_sasl_tls_verified_security_options"
+#define DEF_LMTP_SASL_TLSV_OPTS "$" VAR_LMTP_SASL_TLS_OPTS
+extern char *var_smtp_sasl_tlsv_opts;
+
/*
* LMTP server. The soft error limit determines how many errors an LMTP
* client may make before we start to slow down; the hard error limit
#define DEF_LMTP_SENDER_AUTH 0
extern bool var_smtp_sender_auth;
+ /*
+ * Allow CNAME lookup result to override the server hostname.
+ */
+#define VAR_SMTP_CNAME_OVERR "smtp_cname_overrides_servername"
+#define DEF_SMTP_CNAME_OVERR 0
+#define VAR_LMTP_CNAME_OVERR "lmtp_cname_overrides_servername"
+#define DEF_LMTP_CNAME_OVERR 0
+extern bool var_smtp_cname_overr;
+
/* LICENSE
/* .ad
/* .fi
#define MAIL_ATTR_QUEUE "queue_name"
#define MAIL_ATTR_QUEUEID "queue_id"
#define MAIL_ATTR_SENDER "sender"
+#define MAIL_ATTR_RCPT_COUNT "recipient_count"
#define MAIL_ATTR_ORCPT "original_recipient"
#define MAIL_ATTR_RECIP "recipient"
#define MAIL_ATTR_WHY "reason"
* Patches change both the patchlevel and the release date. Snapshots have no
* patchlevel; they change the release date only.
*/
-#define MAIL_RELEASE_DATE "20060103"
+#define MAIL_RELEASE_DATE "20060112"
#define MAIL_VERSION_NUMBER "2.3"
#ifdef SNAPSHOT
/* msg_stats_print - write MSG_STATS to stream */
int msg_stats_print(ATTR_PRINT_MASTER_FN print_fn, VSTREAM *fp,
- int flags, void *ptr)
+ int flags, void *ptr)
{
int ret;
*/
case -1:
msg_warn("fork: %m");
- dsb_unix(why, "4.3.0", EX_OSERR, sys_exits_detail(EX_OSERR)->text,
+ dsb_unix(why, "4.3.0", sys_exits_detail(EX_OSERR)->text,
"Delivery failed: %m");
return (PIPE_STAT_DEFER);
args.uid, args.gid) < 0)
msg_fatal("wait: %m");
if (pipe_command_timeout) {
- dsb_unix(why, "5.3.0", EX_SOFTWARE, log_len ?
+ dsb_unix(why, "5.3.0", log_len ?
log_buf : sys_exits_detail(EX_SOFTWARE)->text,
"Command time limit exceeded: \"%s\"%s%s",
args.command,
*/
if (!NORMAL_EXIT_STATUS(wait_status)) {
if (WIFSIGNALED(wait_status)) {
- dsb_unix(why, "5.3.0", EX_SOFTWARE, log_len ?
+ dsb_unix(why, "5.3.0", log_len ?
log_buf : sys_exits_detail(EX_SOFTWARE)->text,
"Command died with signal %d: \"%s\"%s%s",
WTERMSIG(wait_status), args.command,
/* Use "D.S.N text" command output. XXX What diagnostic code? */
else if (dsn_valid(log_buf) > 0) {
dsn_split(&dp, "5.3.0", log_buf);
- dsb_unix(why, DSN_STATUS(dp.dsn), DSN_CLASS(dp.dsn) == '4' ?
- EX_TEMPFAIL : EX_UNAVAILABLE, dp.text, "%s", dp.text);
+ dsb_unix(why, DSN_STATUS(dp.dsn), dp.text, "%s", dp.text);
return (DSN_CLASS(dp.dsn) == '4' ?
PIPE_STAT_DEFER : PIPE_STAT_BOUNCE);
}
/* Use <sysexits.h> compatible exit status. */
else if (SYS_EXITS_CODE(WEXITSTATUS(wait_status))) {
sp = sys_exits_detail(WEXITSTATUS(wait_status));
- dsb_unix(why, sp->dsn, WEXITSTATUS(wait_status),
+ dsb_unix(why, sp->dsn,
log_len ? log_buf : sp->text, "%s%s%s", sp->text,
log_len ? ". Command output: " : "", log_buf);
return (sp->dsn[0] == '4' ?
/* No "D.S.N text" or <sysexits.h> compatible status. Fake it. */
else {
sp = sys_exits_detail(WEXITSTATUS(wait_status));
- dsb_unix(why, sp->dsn, WEXITSTATUS(wait_status),
+ dsb_unix(why, sp->dsn,
log_len ? log_buf : sp->text,
"Command died with status %d: \"%s\"%s%s",
WEXITSTATUS(wait_status), args.command,
/* SYNOPSIS
/* #include <post_mail.h>
/*
-/* VSTREAM *post_mail_fopen(sender, recipient, cleanup_flags, trace_flags)
+/* VSTREAM *post_mail_fopen(sender, recipient, cleanup_flags, trace_flags,
+/* queue_id)
/* const char *sender;
/* const char *recipient;
/* int cleanup_flags;
/* int trace_flags;
+/* VSTRING *queue_id;
/*
/* VSTREAM *post_mail_fopen_nowait(sender, recipient,
-/* cleanup_flags, trace_flags)
+/* cleanup_flags, trace_flags, queue_id)
/* const char *sender;
/* const char *recipient;
/* int cleanup_flags;
/* int trace_flags;
+/* VSTRING *queue_id;
/*
/* void post_mail_fopen_async(sender, recipient,
/* cleanup_flags, trace_flags,
-/* notify, context)
+/* queue_id, notify, context)
/* const char *sender;
/* const char *recipient;
/* int cleanup_flags;
/* int trace_flags;
+/* VSTRING *queue_id;
/* void (*notify)(VSTREAM *stream, char *context);
/* char *context;
/*
/* \fB<cleanup_user.h>\fR.
/* .IP trace_flags
/* Message tracing flags as specified in \fB<deliver_request.h>\fR.
-/* .IP via
-/* The name of the service responsible for posting this message.
+/* .IP queue_id
+/* Null pointer, or pointer to buffer that receives the queue
+/* ID of the new message.
/* .IP stream
/* A stream opened by mail_post_fopen().
/* .IP notify
POST_MAIL_NOTIFY notify;
void *context;
VSTREAM *stream;
+ VSTRING *queue_id;
} POST_MAIL_STATE;
/* post_mail_init - initial negotiations */
static void post_mail_init(VSTREAM *stream, const char *sender,
const char *recipient,
- int cleanup_flags, int trace_flags)
+ int cleanup_flags, int trace_flags,
+ VSTRING *queue_id)
{
- VSTRING *id = vstring_alloc(100);
+ VSTRING *id = queue_id ? queue_id : vstring_alloc(100);
struct timeval now;
const char *date;
var_myhostname, var_mail_name);
post_mail_fprintf(stream, "\tid %s; %s", vstring_str(id), date);
post_mail_fprintf(stream, "Date: %s", date);
- vstring_free(id);
+ if (queue_id == 0)
+ vstring_free(id);
}
/* post_mail_fopen - prepare for posting a message */
VSTREAM *post_mail_fopen(const char *sender, const char *recipient,
- int cleanup_flags, int trace_flags)
+ int cleanup_flags, int trace_flags,
+ VSTRING *queue_id)
{
VSTREAM *stream;
stream = mail_connect_wait(MAIL_CLASS_PUBLIC, var_cleanup_service);
- post_mail_init(stream, sender, recipient, cleanup_flags, trace_flags);
+ post_mail_init(stream, sender, recipient, cleanup_flags, trace_flags,
+ queue_id);
return (stream);
}
/* post_mail_fopen_nowait - prepare for posting a message */
VSTREAM *post_mail_fopen_nowait(const char *sender, const char *recipient,
- int cleanup_flags, int trace_flags)
+ int cleanup_flags, int trace_flags,
+ VSTRING *queue_id)
{
VSTREAM *stream;
if ((stream = mail_connect(MAIL_CLASS_PUBLIC, var_cleanup_service,
BLOCKING)) != 0)
- post_mail_init(stream, sender, recipient, cleanup_flags, trace_flags);
+ post_mail_init(stream, sender, recipient, cleanup_flags, trace_flags,
+ queue_id);
return (stream);
}
event_disable_readwrite(vstream_fileno(state->stream));
post_mail_init(state->stream, state->sender,
state->recipient, state->cleanup_flags,
- state->trace_flags);
+ state->trace_flags, state->queue_id);
myfree(state->sender);
myfree(state->recipient);
state->notify(state->stream, state->context);
void post_mail_fopen_async(const char *sender, const char *recipient,
int cleanup_flags, int trace_flags,
+ VSTRING *queue_id,
void (*notify) (VSTREAM *, void *),
void *context)
{
state->notify = notify;
state->context = context;
state->stream = stream;
+ state->queue_id = queue_id;
/*
* To keep interfaces as simple as possible we report all errors via the
* Utility library.
*/
#include <vstream.h>
+#include <vstring.h>
/*
* Global library.
* External interface.
*/
typedef void (*POST_MAIL_NOTIFY)(VSTREAM *, void *);
-extern VSTREAM *post_mail_fopen(const char *, const char *, int, int);
-extern VSTREAM *post_mail_fopen_nowait(const char *, const char *, int, int);
-extern void post_mail_fopen_async(const char *, const char *, int, int, POST_MAIL_NOTIFY, void *);
+extern VSTREAM *post_mail_fopen(const char *, const char *, int, int, VSTRING *);
+extern VSTREAM *post_mail_fopen_nowait(const char *, const char *, int, int, VSTRING *);
+extern void post_mail_fopen_async(const char *, const char *, int, int, VSTRING *, POST_MAIL_NOTIFY, void *);
extern int PRINTFLIKE(2, 3) post_mail_fprintf(VSTREAM *, const char *,...);
extern int post_mail_fputs(VSTREAM *, const char *);
extern int post_mail_buffer(VSTREAM *, const char *, int);
/* #include <rcpt_buf.h>
/*
/* typedef struct {
+/* RECIPIENT rcpt; /* convenience */
/* .in +4
/* VSTRING *address; /* final recipient */
/* VSTRING *orig_addr; /* original recipient */
/* .in -4
/* } RCPT_BUF;
/*
+/* RECIPIENT *RECIPIENT_FROM_RCPT_BUF(rcpb)
+/* RCPT_BUF *rcpb;
+/*
/* RCPT_BUF *rcpb_create(void)
/*
-/* void rcpb_free(rcpt)
-/* RCPT_BUF *rcpt;
+/* void rcpb_reset(rcpb)
+/* RCPT_BUF *rcpb;
+/*
+/* void rcpb_free(rcpb)
+/* RCPT_BUF *rcpb;
/*
/* int rcpb_scan(scan_fn, stream, flags, ptr)
/* ATTR_SCAN_MASTER_FN scan_fn;
/* int flags;
/* void *ptr;
/* DESCRIPTION
+/* RECIPIENT_FROM_RCPT_BUF() populates the rcpt member with
+/* a shallow copy of the contents of the other fields.
+/*
/* rcpb_scan() reads a recipient buffer from the named stream
/* using the specified attribute scan routine. rcpb_scan()
/* is meant to be passed as a call-back to attr_scan(), thusly:
/*
/* ... ATTR_SCAN_FUNC, rcpb_scan, (void *) rcpt_buf, ...
/*
-/* rcpb_create() and rcpb_free() create and destroy
-/* recipient buffer instances.
+/* rcpb_create(), rcpb_reset() and rcpb_free() create, wipe
+/* and destroy recipient buffer instances.
/* DIAGNOSTICS
/* Fatal: out of memory.
/* LICENSE
return (rcpt);
}
+/* rcpb_reset - reset recipient buffer */
+
+void rcpb_reset(RCPT_BUF *rcpt)
+{
+#define BUF_TRUNCATE(s) (vstring_str(s)[0] = 0)
+
+ rcpt->offset = 0;
+ BUF_TRUNCATE(rcpt->dsn_orcpt);
+ rcpt->dsn_notify = 0;
+ BUF_TRUNCATE(rcpt->orig_addr);
+ BUF_TRUNCATE(rcpt->address);
+}
+
/* rcpb_free - destroy recipient buffer */
void rcpb_free(RCPT_BUF *rcpt)
#include <vstring.h>
#include <attr.h>
+ /*
+ * Global library.
+ */
+#include <recipient_list.h>
+
/*
* External interface.
*/
typedef struct {
+ RECIPIENT rcpt; /* convenience */
VSTRING *address; /* final recipient */
VSTRING *orig_addr; /* original recipient */
VSTRING *dsn_orcpt; /* dsn original recipient */
} RCPT_BUF;
extern RCPT_BUF *rcpb_create(void);
+extern void rcpb_reset(RCPT_BUF *);
extern void rcpb_free(RCPT_BUF *);
extern int rcpb_scan(ATTR_SCAN_MASTER_FN, VSTREAM *, int, void *);
-#define RECIPIENT_FROM_RCPT_BUF(rcpt, buf) \
- ((rcpt)->address = vstring_str((buf)->address), \
- (rcpt)->orig_addr = vstring_str((buf)->orig_addr), \
- (rcpt)->dsn_orcpt = vstring_str((buf)->dsn_orcpt), \
- (rcpt)->dsn_notify = (buf)->dsn_notify, \
- (rcpt)->offset = (buf)->offset, \
- (rcpt))
+#define RECIPIENT_FROM_RCPT_BUF(buf) \
+ ((buf)->rcpt.address = vstring_str((buf)->address), \
+ (buf)->rcpt.orig_addr = vstring_str((buf)->orig_addr), \
+ (buf)->rcpt.dsn_orcpt = vstring_str((buf)->dsn_orcpt), \
+ (buf)->rcpt.dsn_notify = (buf)->dsn_notify, \
+ (buf)->rcpt.offset = (buf)->offset, \
+ &(buf)->rcpt)
/* LICENSE
/* .ad
RECIPIENT *rcpt = (RECIPIENT *) ptr;
int ret;
-#define S(s) ((s) ? (s) : "")
-
/*
* The attribute order is determined by backwards compatibility. It can
* be sanitized after all the ad-hoc recipient read/write code is
*/
ret =
print_fn(fp, flags | ATTR_FLAG_MORE,
- ATTR_TYPE_STR, MAIL_ATTR_ORCPT, S(rcpt->orig_addr),
+ ATTR_TYPE_STR, MAIL_ATTR_ORCPT, rcpt->orig_addr,
ATTR_TYPE_STR, MAIL_ATTR_RECIP, rcpt->address,
ATTR_TYPE_LONG, MAIL_ATTR_OFFSET, rcpt->offset,
- ATTR_TYPE_STR, MAIL_ATTR_DSN_ORCPT, S(rcpt->dsn_orcpt),
+ ATTR_TYPE_STR, MAIL_ATTR_DSN_ORCPT, rcpt->dsn_orcpt,
ATTR_TYPE_NUM, MAIL_ATTR_DSN_NOTIFY, rcpt->dsn_notify,
ATTR_TYPE_END);
return (ret);
/* char *address;
/* union {
/* .in +4
-/* int status;
-/* struct QMGR_QUEUE *queue;
-/* char *addr_type;
+/* int status;
+/* struct QMGR_QUEUE *queue;
+/* char *addr_type;
/* .in -4
/* }
/* .in -4
myfree((char *) (ptr)); (ptr) = mystrdup(new); \
} while (0)
- /*
- * Same without const poisning.
- */
-typedef struct RECIPIENT_VAR {
- long offset; /* REC_TYPE_RCPT byte */
- char *dsn_orcpt; /* DSN original recipient */
- int dsn_notify; /* DSN notify flags */
- char *orig_addr; /* null or original recipient */
- char *address; /* complete address */
- union { /* Application specific. */
- int status; /* SMTP client */
- struct QMGR_QUEUE *queue; /* Queue manager */
- char *addr_type; /* DSN */
- } u;
-} RECIPIENT_VAR;
-
typedef struct RECIPIENT_LIST {
RECIPIENT *info;
int len;
/* An I/O error happened, or the peer has disconnected unexpectedly.
/* .IP SMTP_ERR_TIME
/* The time limit specified to smtp_timeout_setup() was exceeded.
-/* .IP SMTP_ERR_PROTO
-/* A protocol error happened.
-/* This error is never generated by the smtp_stream(3) module, but
-/* is defined for application-specific use.
/* .IP SMTP_ERR_QUIET
/* Perform silent cleanup; the error was already reported by
/* the application.
*/
#define SMTP_ERR_EOF 1 /* unexpected client disconnect */
#define SMTP_ERR_TIME 2 /* time out */
-#define SMTP_ERR_PROTO 3 /* protocol (application) */
#define SMTP_ERR_QUIET 4 /* silent cleanup (application) */
extern void smtp_timeout_setup(VSTREAM *, int);
char *exp_from; /* expanded_from */
DELIVER_REQUEST *request; /* the kitchen sink */
DSN_BUF *why; /* delivery status */
- DSN dsn; /* delivery status */
} DELIVER_ATTR;
extern void deliver_attr_init(DELIVER_ATTR *);
#define BOUNCE_ATTR(attr) \
attr.queue_id, &attr.msg_stats, &attr.rcpt, attr.relay, \
- DSN_FROM_DSN_BUF(&attr.dsn, attr.why)
+ DSN_FROM_DSN_BUF(attr.why)
#define BOUNCE_ONE_ATTR(attr) \
attr.queue_name, attr.queue_id, attr.encoding, \
attr.sender, attr.dsn_envid, attr.dsn_ret, \
&attr.msg_stats, &attr.rcpt, attr.relay, \
- DSN_FROM_DSN_BUF(&attr.dsn, attr.why)
+ DSN_FROM_DSN_BUF(attr.why)
#define SENT_ATTR(attr) \
attr.queue_id, &attr.msg_stats, &attr.rcpt, attr.relay, \
- DSN_FROM_DSN_BUF(&attr.dsn, attr.why)
+ DSN_FROM_DSN_BUF(attr.why)
#define OPENED_ATTR(attr) \
attr.queue_id, attr.sender
#define COPY_ATTR(attr) \
/*
* Bounce the message when no luser relay is specified.
*/
- dsb_smtp(state.msg_attr.why, "5.1.1", 550, "550 user unknown",
- "unknown user: \"%s\"", state.msg_attr.local);
+ dsb_simple(state.msg_attr.why, "5.1.1",
+ "unknown user: \"%s\"", state.msg_attr.local);
return (bounce_append(BOUNCE_FLAGS(state.request),
BOUNCE_ATTR(state.msg_attr)));
}
master_ent.o: ../../include/mymalloc.h
master_ent.o: ../../include/own_inet_addr.h
master_ent.o: ../../include/readlline.h
+master_ent.o: ../../include/sock_addr.h
master_ent.o: ../../include/stringops.h
master_ent.o: ../../include/sys_defs.h
master_ent.o: ../../include/vbuf.h
qmgr.o: ../../include/attr.h
qmgr.o: ../../include/dict.h
qmgr.o: ../../include/dsn.h
-qmgr.o: ../../include/dsn_buf.h
qmgr.o: ../../include/events.h
qmgr.o: ../../include/flush_clnt.h
qmgr.o: ../../include/iostuff.h
qmgr.o: ../../include/mail_proto.h
qmgr.o: ../../include/mail_queue.h
qmgr.o: ../../include/mail_server.h
-qmgr.o: ../../include/maps.h
qmgr.o: ../../include/master_proto.h
qmgr.o: ../../include/msg.h
qmgr.o: ../../include/recipient_list.h
qmgr.o: qmgr.c
qmgr.o: qmgr.h
qmgr_active.o: ../../include/abounce.h
-qmgr_active.o: ../../include/argv.h
qmgr_active.o: ../../include/attr.h
qmgr_active.o: ../../include/bounce.h
qmgr_active.o: ../../include/defer.h
qmgr_active.o: ../../include/deliver_request.h
-qmgr_active.o: ../../include/dict.h
qmgr_active.o: ../../include/dsn.h
qmgr_active.o: ../../include/dsn_buf.h
qmgr_active.o: ../../include/dsn_mask.h
qmgr_active.o: ../../include/mail_open_ok.h
qmgr_active.o: ../../include/mail_params.h
qmgr_active.o: ../../include/mail_queue.h
-qmgr_active.o: ../../include/maps.h
qmgr_active.o: ../../include/msg.h
qmgr_active.o: ../../include/msg_stats.h
qmgr_active.o: ../../include/mymalloc.h
qmgr_active.o: ../../include/vstring.h
qmgr_active.o: qmgr.h
qmgr_active.o: qmgr_active.c
-qmgr_bounce.o: ../../include/argv.h
qmgr_bounce.o: ../../include/attr.h
qmgr_bounce.o: ../../include/bounce.h
qmgr_bounce.o: ../../include/deliver_completed.h
qmgr_bounce.o: ../../include/deliver_request.h
-qmgr_bounce.o: ../../include/dict.h
qmgr_bounce.o: ../../include/dsn.h
qmgr_bounce.o: ../../include/dsn_buf.h
-qmgr_bounce.o: ../../include/maps.h
qmgr_bounce.o: ../../include/msg_stats.h
qmgr_bounce.o: ../../include/recipient_list.h
qmgr_bounce.o: ../../include/scan_dir.h
qmgr_bounce.o: ../../include/vstring.h
qmgr_bounce.o: qmgr.h
qmgr_bounce.o: qmgr_bounce.c
-qmgr_defer.o: ../../include/argv.h
qmgr_defer.o: ../../include/attr.h
qmgr_defer.o: ../../include/bounce.h
qmgr_defer.o: ../../include/defer.h
qmgr_defer.o: ../../include/deliver_request.h
-qmgr_defer.o: ../../include/dict.h
qmgr_defer.o: ../../include/dsn.h
qmgr_defer.o: ../../include/dsn_buf.h
-qmgr_defer.o: ../../include/maps.h
qmgr_defer.o: ../../include/msg.h
qmgr_defer.o: ../../include/msg_stats.h
qmgr_defer.o: ../../include/recipient_list.h
qmgr_defer.o: ../../include/vstring.h
qmgr_defer.o: qmgr.h
qmgr_defer.o: qmgr_defer.c
-qmgr_deliver.o: ../../include/argv.h
qmgr_deliver.o: ../../include/attr.h
qmgr_deliver.o: ../../include/deliver_request.h
-qmgr_deliver.o: ../../include/dict.h
qmgr_deliver.o: ../../include/dsb_scan.h
qmgr_deliver.o: ../../include/dsn.h
qmgr_deliver.o: ../../include/dsn_buf.h
qmgr_deliver.o: ../../include/mail_params.h
qmgr_deliver.o: ../../include/mail_proto.h
qmgr_deliver.o: ../../include/mail_queue.h
-qmgr_deliver.o: ../../include/maps.h
qmgr_deliver.o: ../../include/msg.h
qmgr_deliver.o: ../../include/msg_stats.h
+qmgr_deliver.o: ../../include/rcpt_print.h
qmgr_deliver.o: ../../include/recipient_list.h
qmgr_deliver.o: ../../include/scan_dir.h
qmgr_deliver.o: ../../include/stringops.h
qmgr_deliver.o: ../../include/vstring_vstream.h
qmgr_deliver.o: qmgr.h
qmgr_deliver.o: qmgr_deliver.c
-qmgr_enable.o: ../../include/argv.h
-qmgr_enable.o: ../../include/dict.h
qmgr_enable.o: ../../include/dsn.h
-qmgr_enable.o: ../../include/dsn_buf.h
-qmgr_enable.o: ../../include/maps.h
qmgr_enable.o: ../../include/msg.h
qmgr_enable.o: ../../include/recipient_list.h
qmgr_enable.o: ../../include/scan_dir.h
qmgr_enable.o: ../../include/sys_defs.h
qmgr_enable.o: ../../include/vbuf.h
qmgr_enable.o: ../../include/vstream.h
-qmgr_enable.o: ../../include/vstring.h
qmgr_enable.o: qmgr.h
qmgr_enable.o: qmgr_enable.c
-qmgr_entry.o: ../../include/argv.h
qmgr_entry.o: ../../include/attr.h
qmgr_entry.o: ../../include/deliver_request.h
-qmgr_entry.o: ../../include/dict.h
qmgr_entry.o: ../../include/dsn.h
-qmgr_entry.o: ../../include/dsn_buf.h
qmgr_entry.o: ../../include/events.h
qmgr_entry.o: ../../include/mail_params.h
-qmgr_entry.o: ../../include/maps.h
qmgr_entry.o: ../../include/msg.h
qmgr_entry.o: ../../include/msg_stats.h
qmgr_entry.o: ../../include/mymalloc.h
qmgr_message.o: ../../include/dsn_buf.h
qmgr_message.o: ../../include/dsn_mask.h
qmgr_message.o: ../../include/iostuff.h
-qmgr_message.o: ../../include/mail_addr_find.h
qmgr_message.o: ../../include/mail_params.h
qmgr_message.o: ../../include/mail_proto.h
qmgr_message.o: ../../include/mail_queue.h
-qmgr_message.o: ../../include/maps.h
qmgr_message.o: ../../include/msg.h
qmgr_message.o: ../../include/msg_stats.h
qmgr_message.o: ../../include/myflock.h
qmgr_message.o: ../../include/vstring.h
qmgr_message.o: qmgr.h
qmgr_message.o: qmgr_message.c
-qmgr_move.o: ../../include/argv.h
-qmgr_move.o: ../../include/dict.h
qmgr_move.o: ../../include/dsn.h
-qmgr_move.o: ../../include/dsn_buf.h
qmgr_move.o: ../../include/mail_queue.h
qmgr_move.o: ../../include/mail_scan_dir.h
-qmgr_move.o: ../../include/maps.h
qmgr_move.o: ../../include/msg.h
qmgr_move.o: ../../include/recipient_list.h
qmgr_move.o: ../../include/scan_dir.h
qmgr_move.o: ../../include/vstring.h
qmgr_move.o: qmgr.h
qmgr_move.o: qmgr_move.c
-qmgr_queue.o: ../../include/argv.h
-qmgr_queue.o: ../../include/dict.h
qmgr_queue.o: ../../include/dsn.h
-qmgr_queue.o: ../../include/dsn_buf.h
qmgr_queue.o: ../../include/events.h
qmgr_queue.o: ../../include/htable.h
qmgr_queue.o: ../../include/mail_params.h
-qmgr_queue.o: ../../include/maps.h
qmgr_queue.o: ../../include/msg.h
qmgr_queue.o: ../../include/mymalloc.h
qmgr_queue.o: ../../include/recipient_list.h
qmgr_queue.o: ../../include/sys_defs.h
qmgr_queue.o: ../../include/vbuf.h
qmgr_queue.o: ../../include/vstream.h
-qmgr_queue.o: ../../include/vstring.h
qmgr_queue.o: qmgr.h
qmgr_queue.o: qmgr_queue.c
-qmgr_scan.o: ../../include/argv.h
-qmgr_scan.o: ../../include/dict.h
qmgr_scan.o: ../../include/dsn.h
-qmgr_scan.o: ../../include/dsn_buf.h
qmgr_scan.o: ../../include/mail_scan_dir.h
-qmgr_scan.o: ../../include/maps.h
qmgr_scan.o: ../../include/msg.h
qmgr_scan.o: ../../include/mymalloc.h
qmgr_scan.o: ../../include/recipient_list.h
qmgr_scan.o: ../../include/sys_defs.h
qmgr_scan.o: ../../include/vbuf.h
qmgr_scan.o: ../../include/vstream.h
-qmgr_scan.o: ../../include/vstring.h
qmgr_scan.o: qmgr.h
qmgr_scan.o: qmgr_scan.c
-qmgr_transport.o: ../../include/argv.h
qmgr_transport.o: ../../include/attr.h
-qmgr_transport.o: ../../include/dict.h
qmgr_transport.o: ../../include/dsn.h
-qmgr_transport.o: ../../include/dsn_buf.h
qmgr_transport.o: ../../include/events.h
qmgr_transport.o: ../../include/htable.h
qmgr_transport.o: ../../include/iostuff.h
qmgr_transport.o: ../../include/mail_conf.h
qmgr_transport.o: ../../include/mail_params.h
qmgr_transport.o: ../../include/mail_proto.h
-qmgr_transport.o: ../../include/maps.h
qmgr_transport.o: ../../include/msg.h
qmgr_transport.o: ../../include/mymalloc.h
qmgr_transport.o: ../../include/recipient_list.h
qmgr_transport.o: ../../include/sys_defs.h
qmgr_transport.o: ../../include/vbuf.h
qmgr_transport.o: ../../include/vstream.h
-qmgr_transport.o: ../../include/vstring.h
qmgr_transport.o: qmgr.h
qmgr_transport.o: qmgr_transport.c
#include <dsn_util.h>
#include <dsn_buf.h>
#include <dsb_scan.h>
+#include <rcpt_print.h>
/* Application-specific. */
flags = message->tflags
| entry->queue->dflags
| (message->inspect_xport ? DEL_REQ_FLAG_BOUNCE : DEL_REQ_FLAG_DEFLT);
- QMGR_MSG_STATS(&stats, message);
- attr_print(stream, ATTR_FLAG_MORE,
+ (void) QMGR_MSG_STATS(&stats, message);
+ attr_print(stream, ATTR_FLAG_NONE,
ATTR_TYPE_NUM, MAIL_ATTR_FLAGS, flags,
ATTR_TYPE_STR, MAIL_ATTR_QUEUE, message->queue_name,
ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, message->queue_id,
ATTR_TYPE_STR, MAIL_ATTR_SASL_USERNAME, message->sasl_username,
ATTR_TYPE_STR, MAIL_ATTR_SASL_SENDER, message->sasl_sender,
ATTR_TYPE_STR, MAIL_ATTR_RWR_CONTEXT, message->rewrite_context,
+ ATTR_TYPE_NUM, MAIL_ATTR_RCPT_COUNT, list.len,
ATTR_TYPE_END);
if (sender_buf != 0)
vstring_free(sender_buf);
for (recipient = list.info; recipient < list.info + list.len; recipient++)
- attr_print(stream, ATTR_FLAG_MORE,
- ATTR_TYPE_LONG, MAIL_ATTR_OFFSET, recipient->offset,
- ATTR_TYPE_STR, MAIL_ATTR_DSN_ORCPT, recipient->dsn_orcpt,
- ATTR_TYPE_NUM, MAIL_ATTR_DSN_NOTIFY, recipient->dsn_notify,
- ATTR_TYPE_STR, MAIL_ATTR_ORCPT, recipient->orig_addr,
- ATTR_TYPE_STR, MAIL_ATTR_RECIP, recipient->address,
+ attr_print(stream, ATTR_FLAG_NONE,
+ ATTR_TYPE_FUNC, rcpt_print, (void *) recipient,
ATTR_TYPE_END);
- attr_print(stream, ATTR_FLAG_NONE,
- ATTR_TYPE_NUM, MAIL_ATTR_OFFSET, 0,
- ATTR_TYPE_END);
if (vstream_fflush(stream) != 0) {
msg_warn("write to process (%s): %m", entry->queue->transport->name);
return (-1);
QMGR_MESSAGE *message = entry->message;
static DSN_BUF *dsb;
int status;
- DSN dsn;
RECIPIENT *recipient;
int nrcpt;
if (status == DELIVER_STAT_CRASH) {
message->flags |= DELIVER_STAT_DEFER;
qmgr_transport_throttle(transport,
- DSN_SMTP(&dsn, "4.3.0",
- "451 unknown mail transport error",
- "unknown mail transport error"));
+ DSN_SIMPLE(&dsb->dsn, "4.3.0",
+ "unknown mail transport error"));
msg_warn("transport %s failure -- see a previous warning/fatal/panic logfile record for the problem description",
transport->name);
*/
for (nrcpt = 0; nrcpt < entry->rcpt_list.len; nrcpt++) {
recipient = entry->rcpt_list.info + nrcpt;
- qmgr_defer_recipient(message, recipient, &dsn);
+ qmgr_defer_recipient(message, recipient, &dsb->dsn);
}
- qmgr_defer_transport(transport, &dsn);
+ qmgr_defer_transport(transport, &dsb->dsn);
}
/*
* (the todo list); stay away from queue entries that have been selected
* (the busy list), or we would have dangling pointers. The queue itself
* won't go away before we dispose of the current queue entry.
+ *
+ * XXX Caution: DSN_COPY() will panic on empty status or reason.
*/
#define SUSPENDED "delivery temporarily suspended: "
if (status == DELIVER_STAT_DEFER) {
message->flags |= DELIVER_STAT_DEFER;
if (VSTRING_LEN(dsb->status)) {
- /* Sanitize the DSN status from the delivery agent. */
+ /* Sanitize the DSN status/reason from the delivery agent. */
if (!dsn_valid(vstring_str(dsb->status)))
vstring_strcpy(dsb->status, "4.0.0");
if (VSTRING_LEN(dsb->reason) == 0)
vstring_strcpy(dsb->reason, "unknown error");
vstring_prepend(dsb->reason, SUSPENDED, sizeof(SUSPENDED) - 1);
- qmgr_queue_throttle(queue, DSN_FROM_DSN_BUF(&dsn, dsb));
+ qmgr_queue_throttle(queue, DSN_FROM_DSN_BUF(dsb));
if (queue->window == 0)
- qmgr_defer_todo(queue, &dsn);
+ qmgr_defer_todo(queue, &dsb->dsn);
}
}
*/
if (qmgr_deliver_initial_reply(stream) != 0) {
qmgr_transport_throttle(transport,
- DSN_SMTP(&dsn, "4.3.0",
- "451 mail transport unavailable",
- "mail transport unavailable"));
+ DSN_SIMPLE(&dsn, "4.3.0",
+ "mail transport unavailable"));
qmgr_defer_transport(transport, &dsn);
(void) vstream_fclose(stream);
return;
if (qmgr_deliver_send_request(entry, stream) < 0) {
qmgr_entry_unselect(queue, entry);
qmgr_transport_throttle(transport,
- DSN_SMTP(&dsn, "4.3.0",
- "451 mail transport unavailable",
- "mail transport unavailable"));
+ DSN_SIMPLE(&dsn, "4.3.0",
+ "mail transport unavailable"));
qmgr_defer_transport(transport, &dsn);
/* warning: entry and queue may be dangling pointers here */
(void) vstream_fclose(stream);
resolve_clnt_verify_from(message->sender, addr, reply);
if (reply->flags & RESOLVE_FLAG_FAIL) {
qmgr_defer_recipient(message, recipient,
- DSN_SMTP(&dsn, "4.3.0",
- "451 address resolver failure",
- "address resolver failure"));
+ DSN_SIMPLE(&dsn, "4.3.0",
+ "address resolver failure"));
return (-1);
} else if (reply->flags & RESOLVE_FLAG_ERROR) {
qmgr_bounce_recipient(message, recipient,
- DSN_SMTP(&dsn, "5.1.3",
- "553 bad address syntax",
- "bad address syntax"));
+ DSN_SIMPLE(&dsn, "5.1.3",
+ "bad address syntax"));
return (-1);
} else {
return (0);
*/
if (recipient->address[0] == 0) {
qmgr_bounce_recipient(message, recipient,
- DSN_SMTP(&dsn, "5.1.3",
- "553 null recipient address",
- "null recipient address"));
+ DSN_SIMPLE(&dsn, "5.1.3",
+ "null recipient address"));
continue;
}
*/
if (var_allow_min_user == 0 && recipient->address[0] == '-') {
qmgr_bounce_recipient(message, recipient,
- DSN_SMTP(&dsn, "5.1.3",
- "553 bad address syntax",
- "bad address syntax"));
+ DSN_SIMPLE(&dsn, "5.1.3",
+ "bad address syntax"));
continue;
}
break;
if (*cpp) {
qmgr_defer_recipient(message, recipient,
- DSN_SMTP(&dsn, "4.3.2",
- "450 delivery suspended",
- "deferred transport"));
+ DSN_SIMPLE(&dsn, "4.3.2",
+ "deferred transport"));
continue;
}
}
if ((stream = mail_connect(MAIL_CLASS_PRIVATE, transport->name, BLOCK_MODE)) == 0) {
msg_warn("connect to transport %s: %m", transport->name);
qmgr_transport_throttle(transport,
- DSN_SMTP(&dsn, "4.3.0",
- "451 mail transport unavailable",
- "mail transport unavailable"));
+ DSN_SIMPLE(&dsn, "4.3.0",
+ "mail transport unavailable"));
return;
}
alloc = (QMGR_TRANSPORT_ALLOC *) mymalloc(sizeof(*alloc));
/* .ad
/* .fi
/* Some external commands cannot handle more than one recipient
-/* per delivery request. Examples of such transports are pagers,
-/* fax machines, and so on.
+/* per delivery request. Examples of such transports are pagers
+/* or fax machines.
/*
/* To prevent Postfix from sending multiple recipients per delivery
/* request, specify
int status;
int result = 0;
int n;
- DSN dsn;
/*
* Depending on the result, bounce or defer the message, and mark the
case PIPE_STAT_OK:
dsb_update(why, "2.0.0", "relayed", DSB_SKIP_RMTA, DSB_SKIP_REPLY,
"delivered via %s service", service);
- (void) DSN_FROM_DSN_BUF(&dsn, why);
+ (void) DSN_FROM_DSN_BUF(why);
for (n = 0; n < request->rcpt_list.len; n++) {
rcpt = request->rcpt_list.info + n;
status = sent(DEL_REQ_TRACE_FLAGS(request->flags),
request->queue_id, &request->msg_stats, rcpt,
- service, &dsn);
+ service, &why->dsn);
if (status == 0 && (request->flags & DEL_REQ_FLAG_SUCCESS))
deliver_completed(src, rcpt->offset);
result |= status;
break;
case PIPE_STAT_BOUNCE:
case PIPE_STAT_DEFER:
- (void) DSN_FROM_DSN_BUF(&dsn, why);
+ (void) DSN_FROM_DSN_BUF(why);
if (STR(why->status)[0] != '4') {
for (n = 0; n < request->rcpt_list.len; n++) {
rcpt = request->rcpt_list.info + n;
status = bounce_append(DEL_REQ_TRACE_FLAGS(request->flags),
request->queue_id,
&request->msg_stats, rcpt,
- service, &dsn);
+ service, &why->dsn);
if (status == 0)
deliver_completed(src, rcpt->offset);
result |= status;
result |= defer_append(DEL_REQ_TRACE_FLAGS(request->flags),
request->queue_id,
&request->msg_stats, rcpt,
- service, &dsn);
+ service, &why->dsn);
}
}
break;
static PIPE_ATTR attr;
RECIPIENT_LIST *rcpt_list = &request->rcpt_list;
DSN_BUF *why = dsb_create();
- DSN dsn;
VSTRING *buf;
ARGV *expanded_argv = 0;
int deliver_status;
deliver_status = 0;
dsb_simple(why, "2.0.0", "delivers to command: %s", attr.command[0]);
+ (void) DSN_FROM_DSN_BUF(why);
for (n = 0; n < request->rcpt_list.len; n++) {
rcpt = request->rcpt_list.info + n;
status = sent(DEL_REQ_TRACE_FLAGS(request->flags),
request->queue_id, &request->msg_stats,
- rcpt, service, DSN_FROM_DSN_BUF(&dsn, why));
+ rcpt, service, &why->dsn);
if (status == 0 && (request->flags & DEL_REQ_FLAG_SUCCESS))
deliver_completed(request->fp, rcpt->offset);
deliver_status |= status;
postlock.o: ../../include/argv.h
postlock.o: ../../include/deliver_flock.h
postlock.o: ../../include/dot_lockfile.h
+postlock.o: ../../include/dsn.h
postlock.o: ../../include/dsn_buf.h
postlock.o: ../../include/dsn_util.h
postlock.o: ../../include/iostuff.h
qmgr.o: ../../include/attr.h
qmgr.o: ../../include/dict.h
qmgr.o: ../../include/dsn.h
-qmgr.o: ../../include/dsn_buf.h
qmgr.o: ../../include/events.h
qmgr.o: ../../include/flush_clnt.h
qmgr.o: ../../include/iostuff.h
qmgr.o: ../../include/mail_proto.h
qmgr.o: ../../include/mail_queue.h
qmgr.o: ../../include/mail_server.h
-qmgr.o: ../../include/maps.h
qmgr.o: ../../include/master_proto.h
qmgr.o: ../../include/msg.h
qmgr.o: ../../include/recipient_list.h
qmgr.o: qmgr.c
qmgr.o: qmgr.h
qmgr_active.o: ../../include/abounce.h
-qmgr_active.o: ../../include/argv.h
qmgr_active.o: ../../include/attr.h
qmgr_active.o: ../../include/bounce.h
qmgr_active.o: ../../include/defer.h
qmgr_active.o: ../../include/deliver_request.h
-qmgr_active.o: ../../include/dict.h
qmgr_active.o: ../../include/dsn.h
qmgr_active.o: ../../include/dsn_buf.h
qmgr_active.o: ../../include/dsn_mask.h
qmgr_active.o: ../../include/mail_open_ok.h
qmgr_active.o: ../../include/mail_params.h
qmgr_active.o: ../../include/mail_queue.h
-qmgr_active.o: ../../include/maps.h
qmgr_active.o: ../../include/msg.h
qmgr_active.o: ../../include/msg_stats.h
qmgr_active.o: ../../include/mymalloc.h
qmgr_active.o: ../../include/vstring.h
qmgr_active.o: qmgr.h
qmgr_active.o: qmgr_active.c
-qmgr_bounce.o: ../../include/argv.h
qmgr_bounce.o: ../../include/attr.h
qmgr_bounce.o: ../../include/bounce.h
qmgr_bounce.o: ../../include/deliver_completed.h
qmgr_bounce.o: ../../include/deliver_request.h
-qmgr_bounce.o: ../../include/dict.h
qmgr_bounce.o: ../../include/dsn.h
qmgr_bounce.o: ../../include/dsn_buf.h
-qmgr_bounce.o: ../../include/maps.h
qmgr_bounce.o: ../../include/msg_stats.h
qmgr_bounce.o: ../../include/recipient_list.h
qmgr_bounce.o: ../../include/scan_dir.h
qmgr_bounce.o: ../../include/vstring.h
qmgr_bounce.o: qmgr.h
qmgr_bounce.o: qmgr_bounce.c
-qmgr_defer.o: ../../include/argv.h
qmgr_defer.o: ../../include/attr.h
qmgr_defer.o: ../../include/bounce.h
qmgr_defer.o: ../../include/defer.h
qmgr_defer.o: ../../include/deliver_request.h
-qmgr_defer.o: ../../include/dict.h
qmgr_defer.o: ../../include/dsn.h
qmgr_defer.o: ../../include/dsn_buf.h
-qmgr_defer.o: ../../include/maps.h
qmgr_defer.o: ../../include/msg.h
qmgr_defer.o: ../../include/msg_stats.h
qmgr_defer.o: ../../include/recipient_list.h
qmgr_defer.o: ../../include/vstring.h
qmgr_defer.o: qmgr.h
qmgr_defer.o: qmgr_defer.c
-qmgr_deliver.o: ../../include/argv.h
qmgr_deliver.o: ../../include/attr.h
qmgr_deliver.o: ../../include/deliver_request.h
-qmgr_deliver.o: ../../include/dict.h
qmgr_deliver.o: ../../include/dsb_scan.h
qmgr_deliver.o: ../../include/dsn.h
qmgr_deliver.o: ../../include/dsn_buf.h
qmgr_deliver.o: ../../include/mail_params.h
qmgr_deliver.o: ../../include/mail_proto.h
qmgr_deliver.o: ../../include/mail_queue.h
-qmgr_deliver.o: ../../include/maps.h
qmgr_deliver.o: ../../include/msg.h
qmgr_deliver.o: ../../include/msg_stats.h
+qmgr_deliver.o: ../../include/rcpt_print.h
qmgr_deliver.o: ../../include/recipient_list.h
qmgr_deliver.o: ../../include/scan_dir.h
qmgr_deliver.o: ../../include/stringops.h
qmgr_deliver.o: ../../include/vstring_vstream.h
qmgr_deliver.o: qmgr.h
qmgr_deliver.o: qmgr_deliver.c
-qmgr_enable.o: ../../include/argv.h
-qmgr_enable.o: ../../include/dict.h
qmgr_enable.o: ../../include/dsn.h
-qmgr_enable.o: ../../include/dsn_buf.h
-qmgr_enable.o: ../../include/maps.h
qmgr_enable.o: ../../include/msg.h
qmgr_enable.o: ../../include/recipient_list.h
qmgr_enable.o: ../../include/scan_dir.h
qmgr_enable.o: ../../include/sys_defs.h
qmgr_enable.o: ../../include/vbuf.h
qmgr_enable.o: ../../include/vstream.h
-qmgr_enable.o: ../../include/vstring.h
qmgr_enable.o: qmgr.h
qmgr_enable.o: qmgr_enable.c
-qmgr_entry.o: ../../include/argv.h
qmgr_entry.o: ../../include/attr.h
qmgr_entry.o: ../../include/deliver_request.h
-qmgr_entry.o: ../../include/dict.h
qmgr_entry.o: ../../include/dsn.h
-qmgr_entry.o: ../../include/dsn_buf.h
qmgr_entry.o: ../../include/events.h
qmgr_entry.o: ../../include/mail_params.h
-qmgr_entry.o: ../../include/maps.h
qmgr_entry.o: ../../include/msg.h
qmgr_entry.o: ../../include/msg_stats.h
qmgr_entry.o: ../../include/mymalloc.h
qmgr_entry.o: ../../include/vstring.h
qmgr_entry.o: qmgr.h
qmgr_entry.o: qmgr_entry.c
-qmgr_job.o: ../../include/argv.h
-qmgr_job.o: ../../include/dict.h
qmgr_job.o: ../../include/dsn.h
-qmgr_job.o: ../../include/dsn_buf.h
qmgr_job.o: ../../include/htable.h
-qmgr_job.o: ../../include/maps.h
qmgr_job.o: ../../include/msg.h
qmgr_job.o: ../../include/mymalloc.h
qmgr_job.o: ../../include/recipient_list.h
qmgr_job.o: ../../include/sys_defs.h
qmgr_job.o: ../../include/vbuf.h
qmgr_job.o: ../../include/vstream.h
-qmgr_job.o: ../../include/vstring.h
qmgr_job.o: qmgr.h
qmgr_job.o: qmgr_job.c
qmgr_message.o: ../../include/argv.h
qmgr_message.o: ../../include/dsn_buf.h
qmgr_message.o: ../../include/dsn_mask.h
qmgr_message.o: ../../include/iostuff.h
-qmgr_message.o: ../../include/mail_addr_find.h
qmgr_message.o: ../../include/mail_params.h
qmgr_message.o: ../../include/mail_proto.h
qmgr_message.o: ../../include/mail_queue.h
-qmgr_message.o: ../../include/maps.h
qmgr_message.o: ../../include/msg.h
qmgr_message.o: ../../include/msg_stats.h
qmgr_message.o: ../../include/myflock.h
qmgr_message.o: ../../include/vstring.h
qmgr_message.o: qmgr.h
qmgr_message.o: qmgr_message.c
-qmgr_move.o: ../../include/argv.h
-qmgr_move.o: ../../include/dict.h
qmgr_move.o: ../../include/dsn.h
-qmgr_move.o: ../../include/dsn_buf.h
qmgr_move.o: ../../include/mail_queue.h
qmgr_move.o: ../../include/mail_scan_dir.h
-qmgr_move.o: ../../include/maps.h
qmgr_move.o: ../../include/msg.h
qmgr_move.o: ../../include/recipient_list.h
qmgr_move.o: ../../include/scan_dir.h
qmgr_move.o: ../../include/vstring.h
qmgr_move.o: qmgr.h
qmgr_move.o: qmgr_move.c
-qmgr_peer.o: ../../include/argv.h
-qmgr_peer.o: ../../include/dict.h
qmgr_peer.o: ../../include/dsn.h
-qmgr_peer.o: ../../include/dsn_buf.h
qmgr_peer.o: ../../include/htable.h
-qmgr_peer.o: ../../include/maps.h
qmgr_peer.o: ../../include/msg.h
qmgr_peer.o: ../../include/mymalloc.h
qmgr_peer.o: ../../include/recipient_list.h
qmgr_peer.o: ../../include/sys_defs.h
qmgr_peer.o: ../../include/vbuf.h
qmgr_peer.o: ../../include/vstream.h
-qmgr_peer.o: ../../include/vstring.h
qmgr_peer.o: qmgr.h
qmgr_peer.o: qmgr_peer.c
-qmgr_queue.o: ../../include/argv.h
-qmgr_queue.o: ../../include/dict.h
qmgr_queue.o: ../../include/dsn.h
-qmgr_queue.o: ../../include/dsn_buf.h
qmgr_queue.o: ../../include/events.h
qmgr_queue.o: ../../include/htable.h
qmgr_queue.o: ../../include/mail_params.h
-qmgr_queue.o: ../../include/maps.h
qmgr_queue.o: ../../include/msg.h
qmgr_queue.o: ../../include/mymalloc.h
qmgr_queue.o: ../../include/recipient_list.h
qmgr_queue.o: ../../include/sys_defs.h
qmgr_queue.o: ../../include/vbuf.h
qmgr_queue.o: ../../include/vstream.h
-qmgr_queue.o: ../../include/vstring.h
qmgr_queue.o: qmgr.h
qmgr_queue.o: qmgr_queue.c
-qmgr_scan.o: ../../include/argv.h
-qmgr_scan.o: ../../include/dict.h
qmgr_scan.o: ../../include/dsn.h
-qmgr_scan.o: ../../include/dsn_buf.h
qmgr_scan.o: ../../include/mail_scan_dir.h
-qmgr_scan.o: ../../include/maps.h
qmgr_scan.o: ../../include/msg.h
qmgr_scan.o: ../../include/mymalloc.h
qmgr_scan.o: ../../include/recipient_list.h
qmgr_scan.o: ../../include/sys_defs.h
qmgr_scan.o: ../../include/vbuf.h
qmgr_scan.o: ../../include/vstream.h
-qmgr_scan.o: ../../include/vstring.h
qmgr_scan.o: qmgr.h
qmgr_scan.o: qmgr_scan.c
-qmgr_transport.o: ../../include/argv.h
qmgr_transport.o: ../../include/attr.h
-qmgr_transport.o: ../../include/dict.h
qmgr_transport.o: ../../include/dsn.h
-qmgr_transport.o: ../../include/dsn_buf.h
qmgr_transport.o: ../../include/events.h
qmgr_transport.o: ../../include/htable.h
qmgr_transport.o: ../../include/iostuff.h
qmgr_transport.o: ../../include/mail_conf.h
qmgr_transport.o: ../../include/mail_params.h
qmgr_transport.o: ../../include/mail_proto.h
-qmgr_transport.o: ../../include/maps.h
qmgr_transport.o: ../../include/msg.h
qmgr_transport.o: ../../include/mymalloc.h
qmgr_transport.o: ../../include/recipient_list.h
qmgr_transport.o: ../../include/sys_defs.h
qmgr_transport.o: ../../include/vbuf.h
qmgr_transport.o: ../../include/vstream.h
-qmgr_transport.o: ../../include/vstring.h
qmgr_transport.o: qmgr.h
qmgr_transport.o: qmgr_transport.c
#include <dsn_util.h>
#include <dsn_buf.h>
#include <dsb_scan.h>
+#include <rcpt_print.h>
/* Application-specific. */
flags = message->tflags
| entry->queue->dflags
| (message->inspect_xport ? DEL_REQ_FLAG_BOUNCE : DEL_REQ_FLAG_DEFLT);
- QMGR_MSG_STATS(&stats, message);
- attr_print(stream, ATTR_FLAG_MORE,
+ (void) QMGR_MSG_STATS(&stats, message);
+ attr_print(stream, ATTR_FLAG_NONE,
ATTR_TYPE_NUM, MAIL_ATTR_FLAGS, flags,
ATTR_TYPE_STR, MAIL_ATTR_QUEUE, message->queue_name,
ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, message->queue_id,
ATTR_TYPE_STR, MAIL_ATTR_SASL_USERNAME, message->sasl_username,
ATTR_TYPE_STR, MAIL_ATTR_SASL_SENDER, message->sasl_sender,
ATTR_TYPE_STR, MAIL_ATTR_RWR_CONTEXT, message->rewrite_context,
+ ATTR_TYPE_NUM, MAIL_ATTR_RCPT_COUNT, list.len,
ATTR_TYPE_END);
if (sender_buf != 0)
vstring_free(sender_buf);
for (recipient = list.info; recipient < list.info + list.len; recipient++)
- attr_print(stream, ATTR_FLAG_MORE,
- ATTR_TYPE_LONG, MAIL_ATTR_OFFSET, recipient->offset,
- ATTR_TYPE_STR, MAIL_ATTR_DSN_ORCPT, recipient->dsn_orcpt,
- ATTR_TYPE_NUM, MAIL_ATTR_DSN_NOTIFY, recipient->dsn_notify,
- ATTR_TYPE_STR, MAIL_ATTR_ORCPT, recipient->orig_addr,
- ATTR_TYPE_STR, MAIL_ATTR_RECIP, recipient->address,
+ attr_print(stream, ATTR_FLAG_NONE,
+ ATTR_TYPE_FUNC, rcpt_print, (void *) recipient,
ATTR_TYPE_END);
- attr_print(stream, ATTR_FLAG_NONE,
- ATTR_TYPE_NUM, MAIL_ATTR_OFFSET, 0,
- ATTR_TYPE_END);
if (vstream_fflush(stream) != 0) {
msg_warn("write to process (%s): %m", entry->queue->transport->name);
return (-1);
QMGR_MESSAGE *message = entry->message;
static DSN_BUF *dsb;
int status;
- DSN dsn;
RECIPIENT *recipient;
int nrcpt;
if (status == DELIVER_STAT_CRASH) {
message->flags |= DELIVER_STAT_DEFER;
qmgr_transport_throttle(transport,
- DSN_SMTP(&dsn, "4.3.0",
- "451 unknown mail transport error",
- "unknown mail transport error"));
+ DSN_SIMPLE(&dsb->dsn, "4.3.0",
+ "unknown mail transport error"));
msg_warn("transport %s failure -- see a previous warning/fatal/panic logfile record for the problem description",
transport->name);
*/
for (nrcpt = 0; nrcpt < entry->rcpt_list.len; nrcpt++) {
recipient = entry->rcpt_list.info + nrcpt;
- qmgr_defer_recipient(message, recipient, &dsn);
+ qmgr_defer_recipient(message, recipient, &dsb->dsn);
}
- qmgr_defer_transport(transport, &dsn);
+ qmgr_defer_transport(transport, &dsb->dsn);
}
/*
* (the todo list); stay away from queue entries that have been selected
* (the busy list), or we would have dangling pointers. The queue itself
* won't go away before we dispose of the current queue entry.
+ *
+ * XXX Caution: DSN_COPY() will panic on empty status or reason.
*/
#define SUSPENDED "delivery temporarily suspended: "
if (status == DELIVER_STAT_DEFER) {
message->flags |= DELIVER_STAT_DEFER;
if (VSTRING_LEN(dsb->status)) {
- /* Sanitize the DSN status from the delivery agent. */
+ /* Sanitize the DSN status/reason from the delivery agent. */
if (!dsn_valid(vstring_str(dsb->status)))
vstring_strcpy(dsb->status, "4.0.0");
if (VSTRING_LEN(dsb->reason) == 0)
vstring_strcpy(dsb->reason, "unknown error");
vstring_prepend(dsb->reason, SUSPENDED, sizeof(SUSPENDED) - 1);
- qmgr_queue_throttle(queue, DSN_FROM_DSN_BUF(&dsn, dsb));
+ qmgr_queue_throttle(queue, DSN_FROM_DSN_BUF(dsb));
if (queue->window == 0)
- qmgr_defer_todo(queue, &dsn);
+ qmgr_defer_todo(queue, &dsb->dsn);
}
}
*/
if (qmgr_deliver_initial_reply(stream) != 0) {
qmgr_transport_throttle(transport,
- DSN_SMTP(&dsn, "4.3.0",
- "451 mail transport unavailable",
- "mail transport unavailable"));
+ DSN_SIMPLE(&dsn, "4.3.0",
+ "mail transport unavailable"));
qmgr_defer_transport(transport, &dsn);
(void) vstream_fclose(stream);
return;
if (qmgr_deliver_send_request(entry, stream) < 0) {
qmgr_entry_unselect(entry);
qmgr_transport_throttle(transport,
- DSN_SMTP(&dsn, "4.3.0",
- "451 mail transport unavailable",
- "mail transport unavailable"));
+ DSN_SIMPLE(&dsn, "4.3.0",
+ "mail transport unavailable"));
qmgr_defer_transport(transport, &dsn);
/* warning: entry may be a dangling pointer here */
(void) vstream_fclose(stream);
resolve_clnt_verify_from(message->sender, addr, reply);
if (reply->flags & RESOLVE_FLAG_FAIL) {
qmgr_defer_recipient(message, recipient,
- DSN_SMTP(&dsn, "4.3.0",
- "451 address resolver failure",
- "address resolver failure"));
+ DSN_SIMPLE(&dsn, "4.3.0",
+ "address resolver failure"));
return (-1);
} else if (reply->flags & RESOLVE_FLAG_ERROR) {
qmgr_bounce_recipient(message, recipient,
- DSN_SMTP(&dsn, "5.1.3",
- "553 bad address syntax",
- "bad address syntax"));
+ DSN_SIMPLE(&dsn, "5.1.3",
+ "bad address syntax"));
return (-1);
} else {
return (0);
*/
if (recipient->address[0] == 0) {
qmgr_bounce_recipient(message, recipient,
- DSN_SMTP(&dsn, "5.1.3",
- "553 null recipient address",
- "null recipient address"));
+ DSN_SIMPLE(&dsn, "5.1.3",
+ "null recipient address"));
continue;
}
*/
if (var_allow_min_user == 0 && recipient->address[0] == '-') {
qmgr_bounce_recipient(message, recipient,
- DSN_SMTP(&dsn, "5.1.3",
- "553 bad address syntax",
- "bad address syntax"));
+ DSN_SIMPLE(&dsn, "5.1.3",
+ "bad address syntax"));
continue;
}
break;
if (*cpp) {
qmgr_defer_recipient(message, recipient,
- DSN_SMTP(&dsn, "4.3.2",
- "450 delivery suspended",
- "deferred transport"));
+ DSN_SIMPLE(&dsn, "4.3.2",
+ "deferred transport"));
continue;
}
}
if ((stream = mail_connect(MAIL_CLASS_PRIVATE, transport->name, BLOCK_MODE)) == 0) {
msg_warn("connect to transport %s: %m", transport->name);
qmgr_transport_throttle(transport,
- DSN_SMTP(&dsn, "4.3.0",
- "451 mail transport unavailable",
- "mail transport unavailable"));
+ DSN_SIMPLE(&dsn, "4.3.0",
+ "mail transport unavailable"));
return;
}
alloc = (QMGR_TRANSPORT_ALLOC *) mymalloc(sizeof(*alloc));
qmqpd_peer.o: ../../include/attr.h
qmqpd_peer.o: ../../include/inet_proto.h
qmqpd_peer.o: ../../include/iostuff.h
+qmqpd_peer.o: ../../include/mail_params.h
qmqpd_peer.o: ../../include/mail_proto.h
qmqpd_peer.o: ../../include/mail_stream.h
qmqpd_peer.o: ../../include/msg.h
sendmail.o: ../../include/debug_process.h
sendmail.o: ../../include/deliver_request.h
sendmail.o: ../../include/dsn.h
-sendmail.o: ../../include/dsn_buf.h
sendmail.o: ../../include/dsn_mask.h
sendmail.o: ../../include/fullname.h
sendmail.o: ../../include/header_opts.h
#define SENDER_FORMAT "%-11s%8ld %20.20s %s\n"
#define DROP_FORMAT "%-10s%c%8ld %20.20s (maildrop queue, sender UID %u)\n"
-static void showq_reasons(VSTREAM *, BOUNCE_LOG *, HTABLE *);
+static void showq_reasons(VSTREAM *, BOUNCE_LOG *, RCPT_BUF *, DSN_BUF *,
+HTABLE *);
#define STR(x) vstring_str(x)
long msg_size = 0;
BOUNCE_LOG *logfile;
HTABLE *dup_filter = 0;
+ RCPT_BUF *rcpt_buf = 0;
+ DSN_BUF *dsn_buf = 0;
char status = (strcmp(queue, MAIL_QUEUE_ACTIVE) == 0 ? '*' :
strcmp(queue, MAIL_QUEUE_HOLD) == 0 ? '!' : ' ');
int msg_size_ok = 0;
&& dup_filter == 0
&& (logfile = bounce_log_open(MAIL_QUEUE_DEFER, id, O_RDONLY, 0)) != 0) {
dup_filter = htable_create(var_dup_filter_limit);
- showq_reasons(client, logfile, dup_filter);
+ if (rcpt_buf == 0)
+ rcpt_buf = rcpb_create();
+ if (dsn_buf == 0)
+ dsn_buf = dsb_create();
+ showq_reasons(client, logfile, rcpt_buf, dsn_buf, dup_filter);
if (bounce_log_close(logfile))
msg_warn("close %s %s: %m", MAIL_QUEUE_DEFER, id);
}
}
vstring_free(buf);
vstring_free(printable_quoted_addr);
+ if (rcpt_buf)
+ rcpb_free(rcpt_buf);
+ if (dsn_buf)
+ dsb_free(dsn_buf);
if (dup_filter)
htable_free(dup_filter, (void (*) (char *)) 0);
}
/* showq_reasons - show deferral reasons */
-static void showq_reasons(VSTREAM *client, BOUNCE_LOG *bp, HTABLE *dup_filter)
+static void showq_reasons(VSTREAM *client, BOUNCE_LOG *bp, RCPT_BUF *rcpt_buf,
+DSN_BUF *dsn_buf, HTABLE *dup_filter)
{
char *saved_reason = 0;
int padding;
+ RECIPIENT *rcpt = &rcpt_buf->rcpt;
+ DSN *dsn = &dsn_buf->dsn;
- while (bounce_log_read(bp) != 0) {
+ while (bounce_log_read(bp, rcpt_buf, dsn_buf) != 0) {
/*
* Update the duplicate filter.
*/
if (var_dup_filter_limit == 0
|| dup_filter->used < var_dup_filter_limit)
- if (htable_locate(dup_filter, bp->rcpt.address) == 0)
- htable_enter(dup_filter, bp->rcpt.address, (char *) 0);
+ if (htable_locate(dup_filter, rcpt->address) == 0)
+ htable_enter(dup_filter, rcpt->address, (char *) 0);
/*
* Don't print the reason when the previous recipient had the same
* problem.
*/
- if (saved_reason == 0 || strcmp(saved_reason, bp->dsn.reason) != 0) {
+ if (saved_reason == 0 || strcmp(saved_reason, dsn->reason) != 0) {
if (saved_reason)
myfree(saved_reason);
- saved_reason = mystrdup(bp->dsn.reason);
+ saved_reason = mystrdup(dsn->reason);
if ((padding = 76 - strlen(saved_reason)) < 0)
padding = 0;
vstream_fprintf(client, "%*s(%s)\n", padding, "", saved_reason);
}
- vstream_fprintf(client, STRING_FORMAT, "", "", "", bp->rcpt.address);
+ vstream_fprintf(client, STRING_FORMAT, "", "", "", rcpt->address);
}
if (saved_reason)
myfree(saved_reason);
SHELL = /bin/sh
SRCS = smtp.c smtp_connect.c smtp_proto.c smtp_chat.c smtp_session.c \
smtp_addr.c smtp_trouble.c smtp_state.c smtp_rcpt.c \
- smtp_sasl_proto.c smtp_sasl_glue.c smtp_reuse.c smtp_map11.c \
- smtp_dsn.c
+ smtp_sasl_proto.c smtp_sasl_glue.c smtp_reuse.c smtp_map11.c
OBJS = smtp.o smtp_connect.o smtp_proto.o smtp_chat.o smtp_session.o \
smtp_addr.o smtp_trouble.o smtp_state.o smtp_rcpt.o \
- smtp_sasl_proto.o smtp_sasl_glue.o smtp_reuse.o smtp_map11.o \
- smtp_dsn.o
+ smtp_sasl_proto.o smtp_sasl_glue.o smtp_reuse.o smtp_map11.o
HDRS = smtp.h smtp_sasl.h smtp_addr.h smtp_reuse.h
TESTSRC =
DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE)
smtp_connect.o: smtp_addr.h
smtp_connect.o: smtp_connect.c
smtp_connect.o: smtp_reuse.h
-smtp_dsn.o: ../../include/argv.h
-smtp_dsn.o: ../../include/attr.h
-smtp_dsn.o: ../../include/deliver_request.h
-smtp_dsn.o: ../../include/dict.h
-smtp_dsn.o: ../../include/dsn.h
-smtp_dsn.o: ../../include/dsn_buf.h
-smtp_dsn.o: ../../include/htable.h
-smtp_dsn.o: ../../include/mail_params.h
-smtp_dsn.o: ../../include/maps.h
-smtp_dsn.o: ../../include/match_list.h
-smtp_dsn.o: ../../include/match_ops.h
-smtp_dsn.o: ../../include/msg_stats.h
-smtp_dsn.o: ../../include/recipient_list.h
-smtp_dsn.o: ../../include/resolve_clnt.h
-smtp_dsn.o: ../../include/scache.h
-smtp_dsn.o: ../../include/string_list.h
-smtp_dsn.o: ../../include/sys_defs.h
-smtp_dsn.o: ../../include/tls.h
-smtp_dsn.o: ../../include/tok822.h
-smtp_dsn.o: ../../include/vbuf.h
-smtp_dsn.o: ../../include/vstream.h
-smtp_dsn.o: ../../include/vstring.h
-smtp_dsn.o: smtp.h
-smtp_dsn.o: smtp_dsn.c
smtp_map11.o: ../../include/argv.h
smtp_map11.o: ../../include/attr.h
smtp_map11.o: ../../include/deliver_request.h
VAR_LMTP_SASL_PATH, DEF_LMTP_SASL_PATH, &var_smtp_sasl_path, 0, 0,
#ifdef USE_TLS
VAR_LMTP_SASL_TLS_OPTS, DEF_LMTP_SASL_TLS_OPTS, &var_smtp_sasl_tls_opts, 0, 0,
+ VAR_LMTP_SASL_TLSV_OPTS, DEF_LMTP_SASL_TLSV_OPTS, &var_smtp_sasl_tlsv_opts, 0, 0,
#endif
VAR_LMTP_SASL_MECHS, DEF_LMTP_SASL_MECHS, &var_smtp_sasl_mechs, 0, 0,
VAR_LMTP_SASL_TYPE, DEF_LMTP_SASL_TYPE, &var_smtp_sasl_type, 1, 0,
VAR_LMTP_TLS_NOTEOFFER, DEF_LMTP_TLS_NOTEOFFER, &var_smtp_tls_note_starttls_offer,
#endif
VAR_LMTP_SENDER_AUTH, DEF_LMTP_SENDER_AUTH, &var_smtp_sender_auth,
+ VAR_LMTP_CNAME_OVERR, DEF_LMTP_CNAME_OVERR, &var_smtp_cname_overr,
0,
};
/* RFC 1652 (8bit-MIME transport)
/* RFC 1870 (Message Size Declaration)
/* RFC 2033 (LMTP protocol)
-/* RFC 2034 (Enhanced Status Codes)
/* RFC 2045 (MIME: Format of Internet Message Bodies)
/* RFC 2046 (MIME: Media Types)
/* RFC 2554 (AUTH command)
/* SMTP client, typically to transform a locally valid address into
/* a globally valid address when sending mail across the Internet.
/* .PP
+/* Available in Postfix version 2.2.9 and later:
+/* .IP "\fBsmtp_cname_overrides_servername (version dependent)\fR"
+/* Allow DNS CNAME records to override the servername that the
+/* Postfix SMTP client uses for logging, SASL password lookup, TLS
+/* policy decisions, or TLS certificate verification.
+/* .PP
/* Available in Postfix version 2.3 and later:
/* .IP "\fBlmtp_discard_lhlo_keyword_address_maps (empty)\fR"
/* Lookup tables, indexed by the remote LMTP server address, with
/* The number of pseudo-random bytes that an \fBsmtp\fR(8) or \fBsmtpd\fR(8)
/* process requests from the \fBtlsmgr\fR(8) server in order to seed its
/* internal pseudo random number generator (PRNG).
+/* .PP
+/* Available in Postfix version 2.3 and later:
+/* .IP "\fBsmtp_sasl_tls_verified_security_options ($smtp_sasl_tls_security_options)\fR"
+/* The SASL authentication security options that the Postfix SMTP
+/* client uses for TLS encrypted SMTP sessions with a verified server
+/* certificate.
/* RESOURCE AND RATE CONTROLS
/* .ad
/* .fi
#ifdef USE_TLS
int var_smtp_starttls_tmout;
char *var_smtp_sasl_tls_opts;
+char *var_smtp_sasl_tlsv_opts;
bool var_smtp_tls_enforce_peername;
int var_smtp_tls_scert_vd;
bool var_smtp_tls_note_starttls_offer;
bool var_smtp_sender_auth;
char *var_lmtp_tcp_port;
int var_scache_proto_tmout;
+bool var_smtp_cname_overr;
/*
- * Global variables. smtp_errno is set by the address lookup routines and by
- * the connection management routines.
+ * Global variables.
*/
-int smtp_errno;
int smtp_host_lookup_mask;
STRING_LIST *smtp_cache_dest;
SCACHE *smtp_scache;
/* DESCRIPTION
/* .nf
+ /*
+ * System library.
+ */
+#include <string.h>
+
/*
* Utility library.
*/
/*
* DSN Support introduced major bloat in error processing.
*/
- VSTRING *dsn_reason; /* on-the-fly formatting buffer */
+ DSN_BUF *why; /* on-the-fly formatting buffer */
} SMTP_STATE;
#define SET_NEXTHOP_STATE(state, lookup_mx, domain, port) { \
/*
* smtp.c
*/
-extern int smtp_errno; /* XXX can we get rid of this? */
+#define SMTP_HAS_DSN(why) (STR((why)->status)[0] != 0)
+#define SMTP_HAS_SOFT_DSN(why) (STR((why)->status)[0] == '4')
+#define SMTP_HAS_HARD_DSN(why) (STR((why)->status)[0] == '5')
+#define SMTP_HAS_LOOP_DSN(why) \
+ (SMTP_HAS_DSN(why) && strcmp(STR((why)->status) + 1, ".4.6") == 0)
-#define SMTP_ERR_NONE 0 /* no error */
-#define SMTP_ERR_FAIL 1 /* permanent error */
-#define SMTP_ERR_RETRY 2 /* temporary error */
-#define SMTP_ERR_LOOP 3 /* mailer loop */
+#define SMTP_SET_SOFT_DSN(why) (STR((why)->status)[0] = '4')
+#define SMTP_SET_HARD_DSN(why) (STR((why)->status)[0] = '5')
extern int smtp_host_lookup_mask; /* host lookup methods to use */
#endif
+ /*
+ * What's in a name?
+ */
+#define SMTP_HNAME(rr) (var_smtp_cname_overr ? (rr)->rname : (rr)->qname)
+
/*
* smtp_connect.c
*/
extern void smtp_chat_reset(SMTP_SESSION *);
extern void smtp_chat_notify(SMTP_SESSION *);
-#define SMTP_RESP_FAKE(resp, _code, _dsn, _str) \
- ((resp)->code = (_code), \
+#define SMTP_RESP_FAKE(resp, _dsn) \
+ ((resp)->code = 0, \
(resp)->dsn = (_dsn), \
- (resp)->str = (_str), \
+ (resp)->str = DSN_BY_LOCAL_MTA, \
(resp))
+#define DSN_BY_LOCAL_MTA ((char *) 0) /* DSN issued by local MTA */
+
/*
* These operations implement a redundant mark-and-sweep algorithm that
* explicitly accounts for the fate of every recipient. The interface is
/*
* smtp_trouble.c
*/
-extern int smtp_sess_fail(SMTP_STATE *, DSN_BUF *);
+extern int smtp_sess_fail(SMTP_STATE *);
extern int PRINTFLIKE(4, 5) smtp_site_fail(SMTP_STATE *, const char *,
SMTP_RESP *, const char *,...);
extern int PRINTFLIKE(4, 5) smtp_mesg_fail(SMTP_STATE *, const char *,
extern int smtp_map11_tree(TOK822 *, MAPS *, int);
extern int smtp_map11_internal(VSTRING *, MAPS *, int);
- /*
- * smtp_dsn.c
- */
-extern void PRINTFLIKE(6, 7) smtp_dsn_update(DSN_BUF *, const char *,
- const char *,
- int,
- const char *,
- const char *,...);
-extern void vsmtp_dsn_update(DSN_BUF *, const char *, const char *,
- int, const char *,
- const char *, va_list);
-extern void smtp_dsn_formal(DSN_BUF *, const char *, const char *, int,
- const char *);
-
-#define SMTP_DSN_ASSIGN(dsn, mta, stat, resp, why) \
- DSN_ASSIGN((dsn), (stat), DSB_DEF_ACTION, (why), DSB_DTYPE_SMTP, (resp), \
- (mta) ? DSB_MTYPE_DNS : DSB_MTYPE_NONE, (mta))
-
-#define DSN_BY_LOCAL_MTA ((char *) 0) /* DSN issued by local MTA */
-
/*
* Silly little macros.
*/
/* when DNS lookups are explicitly disabled.
/*
/* All routines either return a DNS_RR pointer, or return a null
-/* pointer and set the \fIsmtp_errno\fR global variable accordingly:
-/* .IP SMTP_ERR_RETRY
-/* The request failed due to a soft error, and should be retried later.
-/* .IP SMTP_ERR_FAIL
-/* The request attempt failed due to a hard error.
-/* .IP SMTP_ERR_LOOP
-/* The local machine is the best mail exchanger.
-/* .PP
-/* In addition, a description of the problem is made available
-/* via the \fIwhy\fR argument.
+/* pointer and update the \fIwhy\fR argument accordingly.
/* LICENSE
/* .ad
/* .fi
msg_warn("skipping record type %s: %m", dns_strtype(addr->type));
} else {
msg_info("pref %4d host %s/%s",
- addr->pref, addr->name,
+ addr->pref, SMTP_HNAME(addr),
hostaddr.buf);
}
}
addr_list = dns_rr_append(addr_list, addr);
return (addr_list);
default:
- smtp_dsn_formal(why, DSN_BY_LOCAL_MTA,
- "4.4.3", 450, "450 Host not found");
- smtp_errno = SMTP_ERR_RETRY;
+ dsb_status(why, "4.4.3");
return (addr_list);
case DNS_FAIL:
- smtp_dsn_formal(why, DSN_BY_LOCAL_MTA,
- "5.4.3", 550, "550 Name server failure");
- if (smtp_errno != SMTP_ERR_RETRY)
- smtp_errno = SMTP_ERR_FAIL;
+ dsb_status(why, SMTP_HAS_SOFT_DSN(why) ? "4.4.3" : "5.4.3");
return (addr_list);
case DNS_INVAL:
case DNS_NOTFOUND:
- smtp_dsn_formal(why, DSN_BY_LOCAL_MTA,
- "5.4.4", 550, "550 Host not found");
- if (smtp_errno != SMTP_ERR_RETRY)
- smtp_errno = SMTP_ERR_FAIL;
+ dsb_status(why, SMTP_HAS_SOFT_DSN(why) ? "4.4.4" : "5.4.4");
/* maybe native naming service will succeed */
break;
}
if (smtp_host_lookup_mask & SMTP_HOST_FLAG_NATIVE) {
if ((aierr = hostname_to_sockaddr(host, (char *) 0, 0, &res0)) != 0) {
- smtp_dsn_update(why, DSN_BY_LOCAL_MTA,
- DSN_NOHOST(aierr) ? "4.4.4" : "4.3.0",
- 450, "450 Host not found",
- "unable to look up host %s: %s",
- host, MAI_STRERROR(aierr));
- if (smtp_errno != SMTP_ERR_RETRY)
- smtp_errno =
- (RETRY_AI_ERROR(aierr) ? SMTP_ERR_RETRY : SMTP_ERR_FAIL);
+ dsb_simple(why, (SMTP_HAS_SOFT_DSN(why) || RETRY_AI_ERROR(aierr)) ?
+ (DSN_NOHOST(aierr) ? "4.4.4" : "4.3.0") :
+ (DSN_NOHOST(aierr) ? "5.4.4" : "5.3.0"),
+ "unable to look up host %s: %s",
+ host, MAI_STRERROR(aierr));
} else {
for (found = 0, res = res0; res != 0; res = res->ai_next) {
if (strchr((char *) proto_info->sa_family_list, res->ai_family) == 0) {
}
freeaddrinfo(res0);
if (found == 0) {
- smtp_dsn_update(why, DSN_BY_LOCAL_MTA,
- "5.4.4", 550, "550 Host not found",
- "%s: host not found", host);
- if (smtp_errno != SMTP_ERR_RETRY)
- smtp_errno = SMTP_ERR_FAIL;
+ dsb_simple(why, SMTP_HAS_SOFT_DSN(why) ? "4.4.4" : "5.4.4",
+ "%s: host not found", host);
}
return (addr_list);
}
* with DNS lookups (except if we're backup MX, and all the better MX
* hosts can't be found).
*
- * XXX 2821: update smtp_errno (0->FAIL upon unrecoverable lookup error,
- * any->RETRY upon temporary lookup error) so that we can correctly
- * handle the case of no resolvable MX host. Currently this is always
- * treated as a soft error. RFC 2821 wants a more precise response.
+ * XXX 2821: update the error status (0->FAIL upon unrecoverable lookup
+ * error, any->RETRY upon temporary lookup error) so that we can
+ * correctly handle the case of no resolvable MX host. Currently this is
+ * always treated as a soft error. RFC 2821 wants a more precise
+ * response.
*/
for (rr = mx_names; rr; rr = rr->next) {
if (rr->type != T_MX)
unsigned best_pref;
unsigned best_found;
- smtp_errno = SMTP_ERR_NONE; /* Paranoia */
+ dsb_reset(why); /* Paranoia */
/*
* Preferences from DNS use 0..32767, fall-backs use 32768+.
*/
switch (dns_lookup(name, T_MX, 0, &mx_names, (VSTRING *) 0, why->reason)) {
default:
- smtp_dsn_formal(why, DSN_BY_LOCAL_MTA,
- "4.4.3", 450, "450 Host not found");
- smtp_errno = SMTP_ERR_RETRY;
+ dsb_status(why, "4.4.3");
if (var_ign_mx_lookup_err)
addr_list = smtp_host_addr(name, misc_flags, why);
break;
case DNS_FAIL:
- smtp_dsn_formal(why, DSN_BY_LOCAL_MTA,
- "5.4.3", 550, "550 Name server failure");
- smtp_errno = SMTP_ERR_FAIL;
+ dsb_status(why, "5.4.3");
if (var_ign_mx_lookup_err)
addr_list = smtp_host_addr(name, misc_flags, why);
break;
addr_list = smtp_addr_list(mx_names, why);
dns_rr_free(mx_names);
if (addr_list == 0) {
- /* DSN and text does not change. */
- if (var_smtp_defer_mxaddr)
- smtp_errno = SMTP_ERR_RETRY;
+ /* Text does not change. */
+ if (var_smtp_defer_mxaddr) {
+ /* Don't clobber the null terminator. */
+ if (SMTP_HAS_HARD_DSN(why))
+ SMTP_SET_SOFT_DSN(why); /* XXX */
+ /* Require some error status. */
+ else if (!SMTP_HAS_SOFT_DSN(why))
+ msg_panic("smtp_domain_addr: bad status");
+ }
msg_warn("no MX host for %s has a valid address record", name);
break;
}
addr_list = smtp_truncate_self(addr_list, self->pref);
if (addr_list == 0) {
if (best_pref != best_found) {
- smtp_dsn_update(why, DSN_BY_LOCAL_MTA,
- "4.4.4", 450, "450 Host not found",
- "unable to find primary relay for %s",
- name);
- smtp_errno = SMTP_ERR_RETRY;
+ dsb_simple(why, "4.4.4",
+ "unable to find primary relay for %s", name);
} else {
- smtp_dsn_update(why, DSN_BY_LOCAL_MTA,
- "5.4.6", 550, "550 Mailer loop",
- "mail for %s loops back to myself",
- name);
- smtp_errno = SMTP_ERR_LOOP;
+ dsb_simple(why, "5.4.6", "mail for %s loops back to myself",
+ name);
}
}
}
}
break;
case DNS_INVAL:
- smtp_dsn_formal(why, DSN_BY_LOCAL_MTA,
- "5.4.4", 550, "550 Host not found");
- smtp_errno = SMTP_ERR_FAIL;
+ dsb_status(why, "5.4.4");
break;
case DNS_NOTFOUND:
addr_list = smtp_host_addr(name, misc_flags, why);
{
DNS_RR *addr_list;
- smtp_errno = SMTP_ERR_NONE; /* Paranoia */
+ dsb_reset(why); /* Paranoia */
/*
* If the host is specified by numerical address, just convert the
&& (misc_flags & SMTP_MISC_FLAG_LOOP_DETECT)
&& smtp_find_self(addr_list) != 0) {
dns_rr_free(addr_list);
- smtp_dsn_update(why, DSN_BY_LOCAL_MTA,
- "5.4.6", 550, "550 Mailer loop",
- "mail for %s loops back to myself", host);
- smtp_errno = SMTP_ERR_LOOP;
+ dsb_simple(why, "5.4.6", "mail for %s loops back to myself", host);
return (0);
}
if (addr_list && addr_list->next) {
* generate from untrusted data.
*/
#define NULL_TRACE_FLAGS 0
+#define NO_QUEUE_ID ((VSTRING *) 0)
#define LENGTH 78
#define INDENT 4
notice = post_mail_fopen_nowait(mail_addr_double_bounce(),
var_error_rcpt,
CLEANUP_FLAG_MASK_INTERNAL,
- NULL_TRACE_FLAGS);
+ NULL_TRACE_FLAGS, NO_QUEUE_ID);
if (notice == 0) {
msg_warn("postmaster notify: %m");
return;
int len = strlen(addr);
int sock;
- smtp_errno = SMTP_ERR_NONE; /* Paranoia */
+ dsb_reset(why); /* Paranoia */
/*
* Sanity checks.
*/
if (len >= (int) sizeof(sock_un.sun_path)) {
msg_warn("unix-domain name too long: %s", addr);
- smtp_dsn_update(why, DSN_BY_LOCAL_MTA, "4.3.5",
- 450, "450 Mail server configuration error",
- "Server configuration error");
- smtp_errno = SMTP_ERR_RETRY;
+ dsb_simple(why, "4.3.5", "Server configuration error");
return (0);
}
char *bind_addr;
char *bind_var;
- smtp_errno = SMTP_ERR_NONE; /* Paranoia */
+ dsb_reset(why); /* Paranoia */
/*
* Sanity checks.
if (dns_rr_to_sa(addr, port, sa, &salen) != 0) {
msg_warn("%s: skip address type %s: %m",
myname, dns_strtype(addr->type));
- smtp_dsn_update(why, DSN_BY_LOCAL_MTA, "4.4.0",
- 451, "451 network address conversion failed",
- "network address conversion failed: %m");
- smtp_errno = SMTP_ERR_RETRY;
+ dsb_simple(why, "4.4.0", "network address conversion failed: %m");
return (0);
}
SOCKADDR_TO_HOSTADDR(sa, salen, &hostaddr, (MAI_SERVPORT_STR *) 0, 0);
if (msg_verbose)
msg_info("%s: trying: %s[%s] port %d...",
- myname, addr->name, hostaddr.buf, ntohs(port));
+ myname, SMTP_HNAME(addr), hostaddr.buf, ntohs(port));
- return (smtp_connect_sock(sock, sa, salen, addr->name, hostaddr.buf,
+ return (smtp_connect_sock(sock, sa, salen, SMTP_HNAME(addr), hostaddr.buf,
port, destination, why, sess_flags));
}
int conn_stat;
int saved_errno;
VSTREAM *stream;
- int ch;
time_t start_time;
start_time = time((time_t *) 0);
} else {
conn_stat = sane_connect(sock, sa, salen);
}
- /* XXX 42X Means connection error, but only 421 is defined. */
if (conn_stat < 0) {
- smtp_dsn_update(why, DSN_BY_LOCAL_MTA,
- "4.4.1", 420, "420 Unable to connect to server",
- "connect to %s[%s]: %m", name, addr);
- smtp_errno = SMTP_ERR_RETRY;
+ dsb_simple(why, "4.4.1", "connect to %s[%s]: %m", name, addr);
close(sock);
return (0);
}
-
- /*
- * Following code is obsolete now that the SMTP client will connect to
- * alternate hosts when a session fails before "MAIL FROM".
- */
-#if 1
stream = vstream_fdopen(sock, O_RDWR);
-#else
-
- /*
- * Skip this host if it takes no action within some time limit. XXX Some
- * MTAs use 426 to indicate a timeout error.
- */
- if (read_wait(sock, var_smtp_helo_tmout) < 0) {
- smtp_dsn_update(why, DSN_BY_LOCAL_MTA,
- "4.4.2", 426, "426 No response from server",
- "connect to %s[%s]: read timeout",
- addr->name, hostaddr.buf);
- smtp_errno = SMTP_ERR_RETRY;
- close(sock);
- return (0);
- }
-
- /*
- * Skip this host if it disconnects without talking to us.
- */
- stream = vstream_fdopen(sock, O_RDWR);
- if ((ch = VSTREAM_GETC(stream)) == VSTREAM_EOF) {
- smtp_dsn_update(why, DSN_BY_LOCAL_MTA,
- "4.4.0", 421, "421 Lost connection",
- "connect to %s[%s]: server dropped connection"
- " without sending the initial greeting",
- addr->name, hostaddr.buf);
- smtp_errno = SMTP_ERR_RETRY;
- vstream_fclose(stream);
- return (0);
- }
- vstream_ungetc(stream, ch);
-#endif
/*
* Bundle up what we have into a nice SMTP_SESSION object.
/* smtp_connect_local - connect to local server */
-static void smtp_connect_local(SMTP_STATE *state, const char *path,
- DSN_BUF *why)
+static void smtp_connect_local(SMTP_STATE *state, const char *path)
{
DELIVER_REQUEST *request = state->request;
SMTP_SESSION *session;
+ DSN_BUF *why = state->why;
/*
* It's too painful to weave this code into the SMTP connection
/* smtp_connect_remote - establish remote connection */
static void smtp_connect_remote(SMTP_STATE *state, const char *nexthop,
- char *def_service, DSN_BUF *why)
+ char *def_service)
{
DELIVER_REQUEST *request = state->request;
ARGV *sites;
char *dest;
char **cpp;
int non_fallback_sites;
+ DSN_BUF *why = state->why;
/*
* First try to deliver to the indicated destination, then try to deliver
* Don't try fall-back hosts if mail loops to myself. That would just
* make the problem worse.
*/
- if (addr_list == 0 && smtp_errno == SMTP_ERR_LOOP)
+ if (addr_list == 0 && SMTP_HAS_LOOP_DSN(why))
state->misc_flags |= SMTP_MISC_FLAG_FINAL_NEXTHOP;
/*
if (SMTP_RCPT_LEFT(state) > 0) {
/*
- * In case of a "no error" indication we make up an excuse; this can
- * happen when the fall-back relay was already tried via a cached
- * connection, so that the address list scrubber left behind an empty
- * list.
+ * In case of a "no error" indication we make up an excuse: we did
+ * find the host address, but we did not attempt to connect to it.
+ * This can happen when the fall-back relay was already tried via a
+ * cached connection, so that the address list scrubber left behind
+ * an empty list.
*/
- if (smtp_errno == SMTP_ERR_NONE) {
- smtp_dsn_update(why, DSN_BY_LOCAL_MTA,
- "4.3.0", 450, "450 Server unavailable",
- "server unavailable or unable to receive mail");
- smtp_errno = SMTP_ERR_RETRY;
+ if (!SMTP_HAS_DSN(why)) {
+ dsb_simple(why, "4.3.0",
+ "server unavailable or unable to receive mail");
}
/*
* Pay attention to what could be configuration problems, and pretend
* that these are recoverable rather than bouncing the mail.
*/
- else if (smtp_errno != SMTP_ERR_RETRY
+ else if (!SMTP_HAS_SOFT_DSN(why)
&& (state->misc_flags & SMTP_MISC_FLAG_USE_LMTP) == 0) {
/*
msg_warn("%s configuration problem", VAR_SMTP_FALLBACK);
vstring_strcpy(why->status, "4.3.5");
/* XXX Keep the diagnostic code and MTA. */
- smtp_errno = SMTP_ERR_RETRY;
}
/*
msg_warn("%s configuration problem", VAR_RELAYHOST);
vstring_strcpy(why->status, "4.3.5");
/* XXX Keep the diagnostic code and MTA. */
- smtp_errno = SMTP_ERR_RETRY;
}
/*
* Mail for the next-hop destination loops back to myself. Pass
* the mail to the best_mx_transport or bounce it.
*/
- else if (smtp_errno == SMTP_ERR_LOOP && *var_bestmx_transp) {
+ else if (SMTP_HAS_LOOP_DSN(why) && *var_bestmx_transp) {
+ dsb_reset(why); /* XXX */
state->status = deliver_pass_all(MAIL_CLASS_PRIVATE,
var_bestmx_transp,
request);
- smtp_errno = 0; /* XXX */
SMTP_RCPT_LEFT(state) = 0; /* XXX */
}
}
int smtp_connect(SMTP_STATE *state)
{
DELIVER_REQUEST *request = state->request;
- DSN_BUF *why = dsb_create();
char *destination = request->nexthop;
/*
*/
if (state->misc_flags & SMTP_MISC_FLAG_USE_LMTP) {
if (strncmp(destination, "unix:", 5) == 0) {
- smtp_connect_local(state, destination + 5, why);
+ smtp_connect_local(state, destination + 5);
} else {
if (strncmp(destination, "inet:", 5) == 0)
destination += 5;
- smtp_connect_remote(state, destination, DEF_LMTP_SERVICE, why);
+ smtp_connect_remote(state, destination, DEF_LMTP_SERVICE);
}
}
* Postfix configurations that have a host with such a name.
*/
else {
- smtp_connect_remote(state, destination, DEF_SMTP_SERVICE, why);
+ smtp_connect_remote(state, destination, DEF_SMTP_SERVICE);
}
/*
* deferred recipients at the end. We'd probably still want to bounce
* recipients immediately, so we'd end up with another chunk of code for
* defer logging only.
- *
- * XXX Unlike enhanced status codes, changing a 4xx into 5xx SMTP code is
- * not simply a matter of changing the initial digit. What we're doing
- * here is correct only under specific conditions, such as changing 450
- * into 550 or vice versa.
*/
if (SMTP_RCPT_LEFT(state) > 0) {
state->misc_flags |= SMTP_MISC_FLAG_FINAL_SERVER; /* XXX */
- if (smtp_errno == SMTP_ERR_RETRY)
- STR(why->status)[0] = STR(why->dtext)[0] = '4'; /* XXX */
- else
- STR(why->status)[0] = STR(why->dtext)[0] = '5'; /* XXX */
- why->dcode = atoi(STR(why->dtext)); /* XXX */
- smtp_sess_fail(state, why);
+ smtp_sess_fail(state);
/*
* Sanity check. Don't silently lose recipients.
if (SMTP_RCPT_LEFT(state) > 0)
msg_panic("smtp_connect: left-over recipients");
}
- dsb_free(why);
return (state->status);
}
+++ /dev/null
-/*++
-/* NAME
-/* smtp_dsn 3
-/* SUMMARY
-/* application-specific DSN wrappers
-/* SYNOPSIS
-/* #include <smtp.h>
-/*
-/* void smtp_dsn_update(dsb, mta_name, status, code, reply,
-/* reason_fmt, ...)
-/* SMTP_DSN *dsb;
-/* const char *mta_name;
-/* const char *status;
-/* int code;
-/* const char *reply;
-/* const char *reason_fmt;
-/*
-/* void vsmtp_dsn_update(dsb, mta_name, status, code, reply,
-/* reason_fmt, ap)
-/* SMTP_DSN *dsb;
-/* const char *mta_name;
-/* const char *status;
-/* int code;
-/* const char *reply;
-/* const char *reason_fmt;
-/* va_list ap;
-/*
-/* void smtp_dsn_formal(dsb, mta_name, status, code, reply)
-/* DSN_BUF *dsb;
-/* const char *mta_name;
-/* const char *status;
-/* int code;
-/* const char *reply;
-/*
-/* DSN *SMTP_DSN_ASSIGN(dsn, mta_name, status, action, reply, reason)
-/* DSN *dsn;
-/* const char *mta_name;
-/* const char *status;
-/* const char *action;
-/* const char *reply;
-/* const char *reason;
-/* DESCRIPTION
-/* This module implements application-specific wrappers for
-/* the dsbuf(3) delivery status information module. The purpose
-/* of the wrappers is to eliminate clutter from the code.
-/*
-/* smtp_dsn_update() updates the formal and informal delivery
-/* status attributes.
-/*
-/* vsmtp_dsn_update() implements an alternative interface.
-/*
-/* smtp_dsn_formal() updates the formal delivery status
-/* attributes and leaves the informal reason attribute unmodified.
-/*
-/* SMTP_DSN_ASSIGN() is a wrapper around the DSN_ASSIGN macro.
-/*
-/* Arguments:
-/* .IP dsb
-/* Delivery status information. See dsbuf(3).
-/* .IP mta_name
-/* The name of the MTA that issued the response given with the
-/* status and reply arguments. Specify DSN_BY_LOCAL_MTA for
-/* status and "reply" information that was issued by the local
-/* MTA.
-/* .IP status
-/* RFC 3463 status code.
-/* .IP code
-/* SMTP reply code.
-/* .IP reply
-/* SMTP reply code followed by text. The bounce(8) server
-/* replaces non-printable characters by '?', so it is a good
-/* idea to replace embedded newline characters by spaces
-/* before using this module.
-/* .IP reason_fmt
-/* Format string for the informal reason attribute.
-/* .IP DIAGNOSTICS
-/* Fatal: out of memory. Panic: invalid arguments.
-/* BUGS
-/* It seems wasteful to copy mostly-constant information into
-/* VSTRING buffers, but it's unavoidable if one needs to
-/* delegate work to a subordinate routine, and report the error
-/* after the subordinate has terminated.
-/* LICENSE
-/* .ad
-/* .fi
-/* The Secure Mailer license must be distributed with this software.
-/* AUTHOR(S)
-/* Wietse Venema
-/* IBM T.J. Watson Research
-/* P.O. Box 704
-/* Yorktown Heights, NY 10598, USA
-/*--*/
-
-/* System library. */
-
-#include <sys_defs.h>
-#include <stdlib.h> /* 44BSD stdarg.h uses abort() */
-#include <stdarg.h>
-
-/* Utility library. */
-
-#include <vstring.h>
-
-/* Global library. */
-
-#include <dsn_buf.h>
-#include <mail_params.h>
-
-/* Application-specific. */
-
-#include <smtp.h>
-
-/* smtp_dsn_update - update formal and informal DSN attributes */
-
-void smtp_dsn_update(DSN_BUF *why, const char *mta_name,
- const char *status, int code,
- const char *reply,
- const char *format,...)
-{
- va_list ap;
-
- va_start(ap, format);
- vsmtp_dsn_update(why, mta_name, status, code, reply, format, ap);
- va_end(ap);
-}
-
-/* vsmtp_dsn_update - update formal and informal DSN attributes */
-
-void vsmtp_dsn_update(DSN_BUF *why, const char *mta_name,
- const char *status, int code,
- const char *reply,
- const char *format, va_list ap)
-{
- dsb_formal(why, status, DSB_DEF_ACTION,
- mta_name ? DSB_MTYPE_DNS : DSB_MTYPE_NONE,
- mta_name, var_procname, code, reply);
- vstring_vsprintf(why->reason, format, ap);
-}
-
-/* smtp_dsn_formal - update formal DSN attributes only */
-
-void smtp_dsn_formal(DSN_BUF *why, const char *mta_name,
- const char *status, int code,
- const char *reply)
-{
- dsb_formal(why, status, DSB_DEF_ACTION,
- mta_name ? DSB_MTYPE_DNS : DSB_MTYPE_NONE,
- mta_name, var_procname, code, reply);
-}
VAR_SMTP_SASL_PATH, DEF_SMTP_SASL_PATH, &var_smtp_sasl_path, 0, 0,
#ifdef USE_TLS
VAR_SMTP_SASL_TLS_OPTS, DEF_SMTP_SASL_TLS_OPTS, &var_smtp_sasl_tls_opts, 0, 0,
+ VAR_SMTP_SASL_TLSV_OPTS, DEF_SMTP_SASL_TLSV_OPTS, &var_smtp_sasl_tlsv_opts, 0, 0,
#endif
VAR_SMTP_SASL_MECHS, DEF_SMTP_SASL_MECHS, &var_smtp_sasl_mechs, 0, 0,
VAR_SMTP_SASL_TYPE, DEF_SMTP_SASL_TYPE, &var_smtp_sasl_type, 1, 0,
VAR_SMTP_TLS_NOTEOFFER, DEF_SMTP_TLS_NOTEOFFER, &var_smtp_tls_note_starttls_offer,
#endif
VAR_SMTP_SENDER_AUTH, DEF_SMTP_SENDER_AUTH, &var_smtp_sender_auth,
+ VAR_SMTP_CNAME_OVERR, DEF_SMTP_CNAME_OVERR, &var_smtp_cname_overr,
0,
};
session->namaddrport, var_myhostname);
if (session->features & SMTP_FEATURE_BEST_MX)
return (smtp_site_fail(state, DSN_BY_LOCAL_MTA,
- SMTP_RESP_FAKE(&fake, 554, "5.4.6",
- "554 Mailer loop"),
+ SMTP_RESP_FAKE(&fake, "5.4.6"),
"mail for %s loops back to myself",
request->nexthop));
else
return (smtp_site_fail(state, DSN_BY_LOCAL_MTA,
- SMTP_RESP_FAKE(&fake, 450, "4.4.6",
- "450 Mailer loop"),
+ SMTP_RESP_FAKE(&fake, "4.4.6"),
"mail for %s loops back to myself",
request->nexthop));
}
smtp_chat_cmd(session, "STARTTLS");
if ((resp = smtp_chat_resp(session))->code / 100 == 2) {
#ifdef USE_SASL_AUTH
- if (session->sasl_mechanism_list) {
- myfree(session->sasl_mechanism_list);
- session->sasl_mechanism_list = 0;
- }
+ if (session->features & SMTP_FEATURE_AUTH)
+ smtp_sasl_cleanup(session);
#endif
session->features = saved_features;
/* XXX Mix-up of per-session and per-request flags. */
if (session->tls_enforce_tls) {
if (!(session->features & SMTP_FEATURE_STARTTLS)) {
return (smtp_site_fail(state, DSN_BY_LOCAL_MTA,
- SMTP_RESP_FAKE(&fake, 421, "4.7.4",
- "421 TLS is required, but unavailable"),
+ SMTP_RESP_FAKE(&fake, "4.7.4"),
"TLS is required, but was not offered by host %s",
session->namaddr));
} else if (smtp_tls_ctx == 0) {
return (smtp_site_fail(state, DSN_BY_LOCAL_MTA,
- SMTP_RESP_FAKE(&fake, 421, "4.7.5",
- "421 TLS is required, but unavailable"),
+ SMTP_RESP_FAKE(&fake, "4.7.5"),
"TLS is required, but our TLS engine is unavailable"));
} else {
msg_warn("%s: TLS is required but unavailable, don't know why",
myname);
return (smtp_site_fail(state, DSN_BY_LOCAL_MTA,
- SMTP_RESP_FAKE(&fake, 421, "4.7.0",
- "421 TLS is required, but unavailable"),
+ SMTP_RESP_FAKE(&fake, "4.7.0"),
"TLS is required, but unavailable"));
}
}
}
#endif
#ifdef USE_SASL_AUTH
- if (var_smtp_sasl_enable && (session->features & SMTP_FEATURE_AUTH)) {
- if (session->sasl_mechanism_list != 0)
- return (smtp_sasl_helo_login(state));
- else
- return (smtp_site_fail(state, DSN_BY_LOCAL_MTA,
- SMTP_RESP_FAKE(&fake, 421, "4.7.0",
- "421 SASL authentication failed: "
- "server offered no compatible authentication mechanisms"),
- "SASL authentication failed: "
- "server %s offered no compatible authentication mechanisms",
- session->namaddr));
- }
+ if (var_smtp_sasl_enable && (session->features & SMTP_FEATURE_AUTH))
+ return (smtp_sasl_helo_login(state));
#endif
return (0);
vstring_free(serverid);
if (session->tls_context == 0)
return (smtp_site_fail(state, DSN_BY_LOCAL_MTA,
- SMTP_RESP_FAKE(&fake, 421, "4.7.5",
- "421 TLS handshake failure"),
+ SMTP_RESP_FAKE(&fake, "4.7.5"),
"Cannot start TLS: handshake failure"));
/*
{
MIME_STATE_DETAIL *detail;
SMTP_RESP fake;
- char *text;
detail = mime_state_detail(mime_errs);
- text = concatenate("554 ", detail->text, (char *) 0);
smtp_mesg_fail(state, DSN_BY_LOCAL_MTA,
- SMTP_RESP_FAKE(&fake, 554, detail->dsn, text),
+ SMTP_RESP_FAKE(&fake, detail->dsn),
"%s", detail->text);
- myfree(text);
}
/* smtp_loop - exercise the SMTP protocol engine */
*/
if (session->size_limit > 0 && session->size_limit < request->data_size) {
smtp_mesg_fail(state, DSN_BY_LOCAL_MTA,
- SMTP_RESP_FAKE(&fake, 552, "5.3.4",
- "552 message too large"),
+ SMTP_RESP_FAKE(&fake, "5.3.4"),
"message size %lu exceeds size limit %.0f of server %s",
request->data_size, (double) session->size_limit,
session->namaddr);
{
DELIVER_REQUEST *request = state->request;
SMTP_SESSION *session = state->session;
- DSN dsn;
+ DSN_BUF *why = state->why;
int status;
/*
if (session->features & SMTP_FEATURE_DSN)
rcpt->dsn_notify &= ~DSN_NOTIFY_SUCCESS;
- (void) SMTP_DSN_ASSIGN(&dsn, session->host, resp->dsn,
- resp->str, resp->str);
- dsn.action = "relayed";
+ dsb_update(why, resp->dsn, "relayed", DSB_MTYPE_DNS, session->host,
+ DSB_DTYPE_SMTP, resp->str, "%s", resp->str);
status = sent(DEL_REQ_TRACE_FLAGS(request->flags),
request->queue_id, &request->msg_stats, rcpt,
- session->namaddrport, &dsn);
+ session->namaddrport, DSN_FROM_DSN_BUF(why));
if (status == 0)
if (request->flags & DEL_REQ_FLAG_SUCCESS)
deliver_completed(state->src, rcpt->offset);
session->sasl_passwd,
&mechanism, session->sasl_reply);
if (result != XSASL_AUTH_OK) {
- dsb_update(why, "4.7.0", DSB_DEF_ACTION, DSB_SKIP_RMTA, DSB_DTYPE_SASL,
- 421, STR(session->sasl_reply),
+ dsb_update(why, "4.7.0", DSB_DEF_ACTION, DSB_SKIP_RMTA,
+ DSB_DTYPE_SASL, STR(session->sasl_reply),
+ "SASL authentication failed; "
"cannot authenticate to server %s: %s",
session->namaddr, STR(session->sasl_reply));
return (-1);
session->sasl_reply);
if (result != XSASL_AUTH_OK) {
dsb_update(why, "4.7.0", DSB_DEF_ACTION, /* Fix 200512 */
- DSB_SKIP_RMTA, DSB_DTYPE_SASL,
- 421, STR(session->sasl_reply),
+ DSB_SKIP_RMTA, DSB_DTYPE_SASL, STR(session->sasl_reply),
+ "SASL authentication failed; "
"cannot authenticate to server %s: %s",
session->namaddr, STR(session->sasl_reply));
return (-1); /* Fix 200512 */
* We completed the authentication protocol.
*/
if (resp->code / 100 != 2) {
- smtp_dsn_update(why, session->host, resp->dsn, resp->code, resp->str,
- "SASL authentication failed; server %s said: %s",
- session->namaddr, resp->str);
+ dsb_update(why, resp->dsn, DSB_DEF_ACTION,
+ DSB_MTYPE_DNS, session->host,
+ var_procname, resp->str,
+ "SASL authentication failed; server %s said: %s",
+ session->namaddr, resp->str);
return (0);
}
return (1);
int smtp_sasl_helo_login(SMTP_STATE *state)
{
SMTP_SESSION *session = state->session;
- DSN_BUF *why;
+ DSN_BUF *why = state->why;
int ret;
/*
* error is unrecoverable from a session point of view - the session will
* not be reused.
*/
- why = dsb_create();
ret = 0;
- smtp_sasl_start(session, VAR_SMTP_SASL_OPTS, var_smtp_sasl_opts);
- if (smtp_sasl_authenticate(session, why) <= 0) {
- vstring_prepend(why->reason, "Authentication failed: ",
- sizeof("Authentication failed: ") - 1);
- ret = smtp_sess_fail(state, why);
+ if (session->sasl_mechanism_list == 0) {
+ dsb_simple(why, "4.7.0", "SASL authentication failed: "
+ "server %s offered no compatible authentication mechanisms for this type of connection security",
+ session->namaddr);
+ ret = smtp_sess_fail(state);
/* Session reuse is disabled. */
+ } else {
+ if (session->tls_context == 0)
+ smtp_sasl_start(session, VAR_SMTP_SASL_OPTS,
+ var_smtp_sasl_opts);
+ else if (session->tls_context->peer_verified == 0)
+ smtp_sasl_start(session, VAR_SMTP_SASL_TLS_OPTS,
+ var_smtp_sasl_tls_opts);
+ else
+ smtp_sasl_start(session, VAR_SMTP_SASL_TLSV_OPTS,
+ var_smtp_sasl_tlsv_opts);
+ if (smtp_sasl_authenticate(session, why) <= 0) {
+ ret = smtp_sess_fail(state);
+ /* Session reuse is disabled. */
+ }
}
- dsb_free(why);
return (ret);
}
state->endp_prop = 0;
state->cache_used = 0;
}
- state->dsn_reason = 0;
+ state->why = dsb_create();
/*
* The process name, "smtp" or "lmtp", is also used as the DSN server
vstring_free(state->endp_prop);
if (state->cache_used)
htable_free(state->cache_used, (void (*) (char *)) 0);
- if (state->dsn_reason)
- vstring_free(state->dsn_reason);
+ if (state->why)
+ dsb_free(state->why);
myfree((char *) state);
}
/* SYNOPSIS
/* #include "smtp.h"
/*
-/* int smtp_sess_fail(state, why)
+/* int smtp_sess_fail(state)
/* SMTP_STATE *state;
-/* DSN_BUF *why;
/*
/* int smtp_site_fail(state, mta_name, resp, format, ...)
/* SMTP_STATE *state;
/* smtp_bulk_fail - skip, defer or bounce recipients, maybe throttle queue */
-static int smtp_bulk_fail(SMTP_STATE *state, DSN *dsn, int throttle_queue)
+static int smtp_bulk_fail(SMTP_STATE *state, int throttle_queue)
{
DELIVER_REQUEST *request = state->request;
SMTP_SESSION *session = state->session;
+ DSN_BUF *why = state->why;
RECIPIENT *rcpt;
int status;
- int soft_error = (dsn->status[0] == '4');
+ int soft_error = (STR(why->status)[0] == '4');
int nrcpt;
/*
* why we're skipping this host.
*/
if (soft_error && (state->misc_flags & SMTP_MISC_FLAG_FINAL_SERVER) == 0) {
- msg_info("%s: %s", request->queue_id, dsn->reason);
+ msg_info("%s: %s", request->queue_id, STR(why->reason));
for (nrcpt = 0; nrcpt < SMTP_RCPT_LEFT(state); nrcpt++) {
rcpt = request->rcpt_list.info + nrcpt;
if (SMTP_RCPT_ISMARKED(rcpt))
} else
GETTIMEOFDAY(&request->msg_stats.deliver_done);
+ (void) DSN_FROM_DSN_BUF(why);
for (nrcpt = 0; nrcpt < SMTP_RCPT_LEFT(state); nrcpt++) {
rcpt = request->rcpt_list.info + nrcpt;
if (SMTP_RCPT_ISMARKED(rcpt))
status = (soft_error ? defer_append : bounce_append)
(DEL_REQ_TRACE_FLAGS(request->flags), request->queue_id,
&request->msg_stats, rcpt,
- session ? session->namaddrport : "none", dsn);
+ session ? session->namaddrport : "none", &why->dsn);
if (status == 0)
deliver_completed(state->src, rcpt->offset);
SMTP_RCPT_DROP(state, rcpt);
state->status |= status;
}
if (throttle_queue && soft_error && request->hop_status == 0)
- request->hop_status = DSN_COPY(dsn);
+ request->hop_status = DSN_COPY(&why->dsn);
}
/*
/* smtp_sess_fail - skip site, defer or bounce all recipients */
-int smtp_sess_fail(SMTP_STATE *state, DSN_BUF *why)
+int smtp_sess_fail(SMTP_STATE *state)
{
- DSN dsn;
/*
- * We need to incur the expense of copying lots of strings into VSTRING
- * buffers when the error information is collected by a routine that
- * terminates BEFORE the error is reported. If no copies were made, the
- * information would not be frozen in time.
+ * We can't avoid copying copying lots of strings into VSTRING buffers,
+ * because this error information is collected by a routine that
+ * terminates BEFORE the error is reported.
*/
- return (smtp_bulk_fail(state, DSN_FROM_DSN_BUF(&dsn, why), SMTP_THROTTLE));
+ return (smtp_bulk_fail(state, SMTP_THROTTLE));
}
/* vsmtp_fill_dsn - fill in temporary DSN structure */
-static void vsmtp_fill_dsn(SMTP_STATE *state, DSN *dsn, const char *mta_name,
+static void vsmtp_fill_dsn(SMTP_STATE *state, const char *mta_name,
const char *status, const char *reply,
const char *format, va_list ap)
{
+ DSN_BUF *why = state->why;
/*
- * We can avoid the cost of copying lots of strings into VSTRING buffers
- * when the error information is collected by the routine that terminates
- * AFTER the error is reported. In this case, the information is already
- * frozen in time, so we don't need to make copies.
+ * We could avoid copying lots of strings into VSTRING buffers, because
+ * this error information is given to us by a routine that terminates
+ * AFTER the error is reported. However, this results in ugly kludges
+ * when informal text needs to be formatted. So we maintain consistency
+ * with other error reporting in the SMTP client even if we waste a few
+ * cycles.
*/
- if (state->dsn_reason == 0)
- state->dsn_reason = vstring_alloc(100);
- else
- VSTRING_RESET(state->dsn_reason);
- if (mta_name && reply[0] != '4' && reply[0] != '5') {
- vstring_strcpy(state->dsn_reason, "Protocol error: ");
- mta_name = DSN_BY_LOCAL_MTA;
+ VSTRING_RESET(why->reason);
+ if (mta_name && reply && reply[0] != '4' && reply[0] != '5') {
+ vstring_strcpy(why->reason, "Protocol error: ");
status = "5.5.0";
- reply = "501 Protocol error in server reply";
}
- vstring_vsprintf_append(state->dsn_reason, format, ap);
- SMTP_DSN_ASSIGN(dsn, mta_name, status, reply, STR(state->dsn_reason));
-}
-
-/* smtp_fill_dsn - fill in temporary DSN structure */
-
-static void smtp_fill_dsn(SMTP_STATE *state, DSN *dsn, const char *mta_name,
- const char *status, const char *reply,
- const char *format,...)
-{
- va_list ap;
-
- va_start(ap, format);
- vsmtp_fill_dsn(state, dsn, mta_name, status, reply, format, ap);
- va_end(ap);
+ vstring_vsprintf_append(why->reason, format, ap);
+ dsb_formal(why, status, DSB_DEF_ACTION,
+ mta_name ? DSB_MTYPE_DNS : DSB_MTYPE_NONE, mta_name,
+ reply ? DSB_DTYPE_SMTP : DSB_DTYPE_NONE, reply);
}
/* smtp_site_fail - throttle this queue; skip, defer or bounce all recipients */
int smtp_site_fail(SMTP_STATE *state, const char *mta_name, SMTP_RESP *resp,
const char *format,...)
{
- DSN dsn;
va_list ap;
/*
* Initialize.
*/
va_start(ap, format);
- vsmtp_fill_dsn(state, &dsn, mta_name, resp->dsn, resp->str, format, ap);
+ vsmtp_fill_dsn(state, mta_name, resp->dsn, resp->str, format, ap);
va_end(ap);
if (state->session && mta_name)
/*
* Skip, defer or bounce recipients, and throttle this queue.
*/
- return (smtp_bulk_fail(state, &dsn, SMTP_THROTTLE));
+ return (smtp_bulk_fail(state, SMTP_THROTTLE));
}
/* smtp_mesg_fail - skip, defer or bounce all recipients; no queue throttle */
const char *format,...)
{
va_list ap;
- DSN dsn;
/*
* Initialize.
*/
va_start(ap, format);
- vsmtp_fill_dsn(state, &dsn, mta_name, resp->dsn, resp->str, format, ap);
+ vsmtp_fill_dsn(state, mta_name, resp->dsn, resp->str, format, ap);
va_end(ap);
if (state->session && mta_name)
/*
* Skip, defer or bounce recipients, but don't throttle this queue.
*/
- return (smtp_bulk_fail(state, &dsn, SMTP_NOTHROTTLE));
+ return (smtp_bulk_fail(state, SMTP_NOTHROTTLE));
}
/* smtp_rcpt_fail - skip, defer, or bounce recipient */
{
DELIVER_REQUEST *request = state->request;
SMTP_SESSION *session = state->session;
- DSN dsn;
+ DSN_BUF *why = state->why;
int status;
int soft_error;
va_list ap;
* Initialize.
*/
va_start(ap, format);
- vsmtp_fill_dsn(state, &dsn, mta_name, resp->dsn, resp->str, format, ap);
+ vsmtp_fill_dsn(state, mta_name, resp->dsn, resp->str, format, ap);
va_end(ap);
- soft_error = dsn.status[0] == '4';
+ soft_error = STR(why->status)[0] == '4';
if (state->session && mta_name)
smtp_check_code(state->session, resp->code);
* why we're skipping this recipient now.
*/
if (soft_error && (state->misc_flags & SMTP_MISC_FLAG_FINAL_SERVER) == 0) {
- msg_info("%s: %s", request->queue_id, dsn.reason);
+ msg_info("%s: %s", request->queue_id, STR(why->reason));
SMTP_RCPT_KEEP(state, rcpt);
}
* that did qualify for delivery to a backup server.
*/
else {
+ (void) DSN_FROM_DSN_BUF(state->why);
status = (soft_error ? defer_append : bounce_append)
(DEL_REQ_TRACE_FLAGS(request->flags), request->queue_id,
&request->msg_stats, rcpt,
- session ? session->namaddrport : "none", &dsn);
+ session ? session->namaddrport : "none", &why->dsn);
if (status == 0)
deliver_completed(state->src, rcpt->offset);
SMTP_RCPT_DROP(state, rcpt);
int smtp_stream_except(SMTP_STATE *state, int code, const char *description)
{
SMTP_SESSION *session = state->session;
- DSN dsn;
+ DSN_BUF *why = state->why;
/*
* Sanity check.
default:
msg_panic("smtp_stream_except: unknown exception %d", code);
case SMTP_ERR_EOF:
- smtp_fill_dsn(state, &dsn, DSN_BY_LOCAL_MTA,
- "4.4.2", "421 lost connection",
- "lost connection with %s while %s",
- session->namaddr, description);
+ dsb_simple(why, "4.4.2", "lost connection with %s while %s",
+ session->namaddr, description);
break;
case SMTP_ERR_TIME:
- smtp_fill_dsn(state, &dsn, DSN_BY_LOCAL_MTA,
- "4.4.2", "426 conversation timed out",
- "conversation with %s timed out while %s",
- session->namaddr, description);
- break;
- case SMTP_ERR_PROTO:
- smtp_fill_dsn(state, &dsn, DSN_BY_LOCAL_MTA,
- "4.5.0", "403 remote protocol error",
- "remote protocol error in reply from %s while %s",
- session->namaddr, description);
+ dsb_simple(why, "4.4.2", "conversation with %s timed out while %s",
+ session->namaddr, description);
break;
}
- return (smtp_bulk_fail(state, &dsn, SMTP_THROTTLE));
+ return (smtp_bulk_fail(state, SMTP_THROTTLE));
}
smtpd_check.o: ../../include/dns.h
smtpd_check.o: ../../include/domain_list.h
smtpd_check.o: ../../include/dsn.h
-smtpd_check.o: ../../include/dsn_buf.h
smtpd_check.o: ../../include/dsn_util.h
smtpd_check.o: ../../include/fsspace.h
smtpd_check.o: ../../include/htable.h
/* RFC 1869 (SMTP service extensions)
/* RFC 1870 (Message Size Declaration)
/* RFC 1985 (ETRN command)
-/* RFC 2034 (Enhanced Status Codes)
/* RFC 2554 (AUTH command)
/* RFC 2821 (SMTP protocol)
/* RFC 2920 (SMTP Pipelining)
/* Available in Postfix version 2.2 and later:
/* .IP "\fBsmtpd_discard_ehlo_keyword_address_maps (empty)\fR"
/* Lookup tables, indexed by the remote SMTP client address, with
-/* case insensitive lists of EHLO keywords (pipelining, starttls,
-/* auth, etc.) that the SMTP server will not send in the EHLO response
-/* to a remote SMTP client.
+/* case insensitive lists of EHLO keywords (pipelining, starttls, auth,
+/* etc.) that the SMTP server will not send in the EHLO response to a
+/* remote SMTP client.
/* .IP "\fBsmtpd_discard_ehlo_keywords (empty)\fR"
/* A case insensitive list of EHLO keywords (pipelining, starttls,
/* auth, etc.) that the SMTP server will not send in the EHLO response
* generate from untrusted data.
*/
#define NULL_TRACE_FLAGS 0
+#define NO_QUEUE_ID ((VSTRING *) 0)
#define LENGTH 78
#define INDENT 4
notice = post_mail_fopen_nowait(mail_addr_double_bounce(),
var_error_rcpt,
CLEANUP_FLAG_MASK_INTERNAL,
- NULL_TRACE_FLAGS);
+ NULL_TRACE_FLAGS, NO_QUEUE_ID);
if (notice == 0) {
msg_warn("postmaster notify: %m");
return;
/* handling. A call of these functions either succeeds or it does
/* not return at all.
/*
+/* To save memory, zero-length strings are shared and read-only.
+/* The caller must not attempt to modify the null terminator.
+/* This code is enabled unless NO_SHARED_EMPTY_STRINGS is
+/* defined at compile time (for example, you have an sscanf()
+/* routine that pushes characters back into its input).
+/*
/* mymalloc() allocates the requested amount of memory. The memory
/* is not set to zero.
/*
#define SPACE_FOR(len) (offsetof(MBLOCK, u.payload[0]) + len)
+ /*
+ * Optimization for short strings. We share one copy with multiple callers.
+ * This differs from normal heap memory in two ways, because the memory is
+ * shared:
+ *
+ * - It must be read-only to avoid horrible bugs. This is OK because there is
+ * no legitimate reason to modify the null terminator.
+ *
+ * - myfree() cannot overwrite the memory with a filler pattern like it can do
+ * with heap memory. Therefore, some dangling pointer bugs will be masked.
+ */
+#ifndef NO_SHARED_EMPTY_STRINGS
+static const char empty_string[] = "";
+
+#endif
+
/* mymalloc - allocate memory or bust */
char *mymalloc(ssize_t len)
MBLOCK *real_ptr;
ssize_t old_len;
+#ifndef NO_SHARED_EMPTY_STRINGS
+ if (ptr == empty_string)
+ return (mymalloc(len));
+#endif
+
if (len < 1)
msg_panic("myrealloc: requested length %ld", (long) len);
CHECK_IN_PTR(ptr, real_ptr, old_len, "myrealloc");
MBLOCK *real_ptr;
ssize_t len;
- CHECK_IN_PTR(ptr, real_ptr, len, "myfree");
- memset((char *) real_ptr, FILLER, SPACE_FOR(len));
- free((char *) real_ptr);
+#ifndef NO_SHARED_EMPTY_STRINGS
+ if (ptr != empty_string) {
+#endif
+ CHECK_IN_PTR(ptr, real_ptr, len, "myfree");
+ memset((char *) real_ptr, FILLER, SPACE_FOR(len));
+ free((char *) real_ptr);
+#ifndef NO_SHARED_EMPTY_STRINGS
+ }
+#endif
}
/* mystrdup - save string to heap */
{
if (str == 0)
msg_panic("mystrdup: null pointer argument");
+#ifndef NO_SHARED_EMPTY_STRINGS
+ if (*str == 0)
+ return ((char *) empty_string);
+#endif
return (strcpy(mymalloc(strlen(str) + 1), str));
}
msg_panic("mystrndup: null pointer argument");
if (len < 0)
msg_panic("mystrndup: requested length %ld", (long) len);
+#ifndef NO_SHARED_EMPTY_STRINGS
+ if (*str == 0)
+ return ((char *) empty_string);
+#endif
if ((cp = memchr(str, 0, len)) != 0)
len = cp - str;
result = memcpy(mymalloc(len + 1), str, len);
if (!ISDIGIT(ch))
netstring_except(stream, NETSTRING_ERR_FORMAT);
len = len * 10 + ch - '0';
+ /* vstream_fread() would read zero bytes. Reject input anyway. */
+ if (len < 0)
+ netstring_except(stream, NETSTRING_ERR_SIZE);
break;
}
}
VSTRING *vstring_insert(VSTRING *vp, ssize_t start, const char *buf, ssize_t len)
{
- const char *myname = "vstring_insert";
ssize_t new_len;
/*
verify.o: ../../include/dict.h
verify.o: ../../include/dict_ht.h
verify.o: ../../include/dsn.h
-verify.o: ../../include/dsn_buf.h
verify.o: ../../include/htable.h
verify.o: ../../include/iostuff.h
verify.o: ../../include/mail_conf.h
"" : var_verify_sender, STR(addr),
CLEANUP_FLAG_MASK_INTERNAL,
DEL_REQ_FLAG_MTA_VRFY,
+ (VSTRING *) 0,
verify_post_mail_action,
(void *) 0);
if (updated != 0 || var_verify_neg_cache != 0) {
if (msg_verbose)
MSG_LOG_STATE(myname, state);
- dsb_smtp(state.msg_attr.why, "5.1.1", 550, "550 user unknown",
- "unknown user: \"%s\"", state.msg_attr.user);
+ dsb_simple(state.msg_attr.why, "5.1.1",
+ "unknown user: \"%s\"", state.msg_attr.user);
return (bounce_append(BOUNCE_FLAGS(state.request),
BOUNCE_ATTR(state.msg_attr)));
}
char *relay; /* relay host */
MSG_STATS msg_stats; /* time profile */
DSN_BUF *why; /* delivery status */
- DSN dsn; /* delivery status */
} DELIVER_ATTR;
extern void deliver_attr_init(DELIVER_ATTR *);
#define BOUNCE_ATTR(attr) \
attr.queue_id, &attr.msg_stats, &attr.rcpt, attr.relay, \
- DSN_FROM_DSN_BUF(&attr.dsn, attr.why)
+ DSN_FROM_DSN_BUF(attr.why)
#define SENT_ATTR(attr) \
attr.queue_id, &attr.msg_stats, &attr.rcpt, attr.relay, \
- DSN_FROM_DSN_BUF(&attr.dsn, attr.why)
+ DSN_FROM_DSN_BUF(attr.why)
#define COPY_ATTR(attr) \
attr.sender, attr.rcpt.orig_addr, attr.delivered, attr.fp