that use short hostnames will have to specify their FQDN
in main.cf (this will eventually be done by the system
installation/configuration procedure). Should the config
- language support backticks so one can say `domainname`? What
- about $name stuff between the backtics?
+ language support backticks so one can say `domainname`?
+ What about $name stuff between the backtics?
Security: the master now creates FIFOs and UNIX-domain
sockets as the mail owner instead of as root, for better
19980824
- Bugfix: transport table lookup keys were case sensitive.
+ Bugfix: transport table lookup keys were case sensitive.
19980825
Feature: site!user is rewritten to user@site, under control
of the boolean parameter swap_bangpath (default: yes).
- Feature: permit a naked IP address in HELO commands (i.e. an
- address without the enclosing [] as required by the RFC), by
- specifying "permit_naked_ip_address" as one of the restrictions
- in the "smtpd_helo_restrictions" config parameter.
+ Feature: permit a naked IP address in HELO commands (i.e.
+ an address without the enclosing [] as required by the
+ RFC), by specifying "permit_naked_ip_address" as one of
+ the restrictions in the "smtpd_helo_restrictions" config
+ parameter.
19980904
separate file global/mail_version.h, so that a version
change no longer results in massive recompilation.
- Bugfix: Errors-To was flagged as a sender address, so
- the address never was picked up.
+ Bugfix: Errors-To was flagged as a sender address, so the
+ address never was picked up.
Code cleanup: support for Errors-To: headers completed.
Bugfix: after vmailer reload, single-threaded servers could
exit before flushing unwritten data to the client. Example:
cleanup would exit before acking success to pickup, so the
- message would be delivered twice. Bug reported by Brian Candler.
+ message would be delivered twice. Bug reported by Brian
+ Candler.
Cleanup: removed spurious error output from vmailer-script.
Reported by Brian Candler.
Bugfix: the change in virtual/canonical map search order
broke @domain entries; they would never be looked up if
- the address matched $myorigin or $mydestinations. Found
- by Chip Christian who now regrets asking for the change.
+ the address matched $myorigin or $mydestinations. Found by
+ Chip Christian who now regrets asking for the change.
Bugfix: cleanup initialized an error mask incorrectly, so
that it would keep writing to a file larger than the queue
10091002
- SMTPD now logs "lost connection after end-of-message" instead
- of "lost connection after DATA".
+ SMTPD now logs "lost connection after end-of-message"
+ instead of "lost connection after DATA".
10091005
Workaround: with UNIX-domain sockets, LINUX connect() blocks
until the server calls accept(). File: qmgr/qmgr_transport.c.
- Terry Lorrah and Scott Cotton provided the necessary evidence.
+ Terry Lorrah and Scott Cotton provided the necessary
+ evidence.
19981020
Compatibility: the tokenizer for alias/forward/etc.
expansion now updates an optional counter with the number
of destinations found; If no destinations is found in a
- .forward file, deliver to the mailbox instead. Thanks,
+ .forward file, deliver to the mailbox instead. Thanks,
Daniel Eisenbud, for showing the way to go.
Robustness: the pickup daemon should always include a
Robustness: duplicate entries in aliases or maps now cause
a warning instead of a fatal error (and an incomplete file).
- Robustness: mkmap now prints a warning when an entry is
- in "key: value" format, which is the format expected for
- alias databases, not for maps.
+ Robustness: mkmap now prints a warning when an entry is in
+ "key: value" format, which is the format expected for alias
+ databases, not for maps.
Portability: on LINUX, prepend "+" to the getopt() options
string so that getopt() will stop at the first non-option
19981108
- Bugfix: minor memory leak in the user+foo table lookup code.
+ Bugfix: minor memory leak in the user+foo table lookup
+ code.
Configurability: specify virtual.domain in the virtual map,
and mail for unknown@virtual.domain will bounce automatically.
d'Itri put me on the right track.
Configurability: The mydestinations configuration parameter
- now accepts /file/name expressions and type:name lookup tables.
+ now accepts /file/name expressions and type:name lookup
+ tables.
Code cleanup: in order to make the previous two enhancements
possible, revised the string/host/address matching engine
by hand).
Feature: when a writable maildrop directory is a problem,
- sites can make the new "postdrop" utility set-gid. This command
- is never used when the maildrop directory is world-writable.
+ sites can make the new "postdrop" utility set-gid. This
+ command is never used when the maildrop directory is
+ world-writable.
Robustness: make the queue file creation routine more
resistant against denial of service race attack. File:
Hewlett-Packard). File: cleanup/cleanup_envelope.c.
Compatibility: the transport table now uses .domain.name
- to match subdomains, just like sendmail mailer tables
- (patch by Lamont Jones, Hewlett-Packard).
+ to match subdomains, just like sendmail mailer tables (patch
+ by Lamont Jones, Hewlett-Packard).
Feature: mailq now ends with a total queue size summary
(Eilon Gishri, Israel Inter University Computation Center).
19990121
- Feature: pickup (again) logs uid and sender address.
- On repeated request by Scott Cotton, Internet Consultants
+ Feature: pickup (again) logs uid and sender address. On
+ repeated request by Scott Cotton, Internet Consultants
Group, Inc.
Portability: doze() function for systems without usleep().
19990307
- Feature: LDAP lookups, updated by Jon Hensley, Merit
- Network, USA.
+ Feature: LDAP lookups, updated by Jon Hensley, Merit Network,
+ USA.
Feature: regular expression (PCRE) support by Andrew
McNamara, connect.com.au Pty. Ltd., Australia. In order to
use this code specify pcre:/file/name. You can use this
- anywhere you would use a DB or DBM file, NIS or LDAP.
- See: PCRE_README for how to enable this code.
+ anywhere you would use a DB or DBM file, NIS or LDAP. See:
+ PCRE_README for how to enable this code.
Feature: "delay_warning_time = 4" causes Postfix to send
a "your mail is delayed" notice after approx. 4 hours.
Enhancement: include the client network address in the
rejected by RBL response. Lamont Jones, Hewlett-Packard.
-
+
Workaround: use fstat() to figure out if the maildrop is
world-writable. access() uses the real uid, which stinks.
Workaround: more watchdog timers for postfix-unfriendly
systems. By now every Postfix daemon has one. Call it life
- insurance.
+ insurance.
Robustness: increased the maximal time to receive or deliver
mail from $ipc_timeout (default: 3600 seconds) to the more
Bugfix: when a recipient appeared multiple times in a local
alias or include expansion, the delivery status could be
- left uninitialized, causing the mail to be deferred and
+ left uninitialized, causing the mail to be deferred and
delivered again. File: local/recipient.c.
19990327
mail is bounced with a "mail loops back to myself" error).
Config: in order to make feature tracking easier the source
- code distribution now has a copy of the default settings in
- conf/main.cf.default.
+ code distribution now has a copy of the default settings
+ in conf/main.cf.default.
Feature: separate configurable postmaster addresses for
single bounces (bounce_notice_recipient), double bounces
19990410
- Bugfix: auto-detection of changes to DB or DBM lookup
- tables wasn't done for TCP connections.
+ Bugfix: auto-detection of changes to DB or DBM lookup tables
+ wasn't done for TCP connections.
19990410
19990610
Bugfix: the "is this the loopback interface" test was
- broken. Reported by Claus Fischer @microworld.com.
- File: smtp/smtp_connect.c.
+ broken. Reported by Claus Fischer @microworld.com. File:
+ smtp/smtp_connect.c.
Usability: added helpful warnings about restrictions that
are being ignored after check_relay_domains, etc.
File: smtpd/smtpd.c.
Bugfix: fencepost error in the doze() routine (an usleep()
- replacement for systems without one). Found by Simon J Mudd.
- File: util/doze.c.
+ replacement for systems without one). Found by Simon J
+ Mudd. File: util/doze.c.
19990624
19990628
- Feature: the postlock command now returns EX_TEMPFAIL
- when the destination file is locked by another process.
+ Feature: the postlock command now returns EX_TEMPFAIL when
+ the destination file is locked by another process.
19990705
19990714
Robustness: add PATH statement to Solaris2 chroot setup
- script to avoid running the ucb commands. Problem found
- by Panagiotis Astithas @ ece.ntua.gr.
+ script to avoid running the ucb commands. Problem found by
+ Panagiotis Astithas @ ece.ntua.gr.
19990721
19990813
- Bugfix: sendmail -t (extract recipients from headers) did not
- work when the always_bcc feature was turned on. Reported
+ Bugfix: sendmail -t (extract recipients from headers) did
+ not work when the always_bcc feature was turned on. Reported
by: Denis Shaposhnikov @ neva.vlink.ru.
19990813
- Bugfix: "sendmail -bd" returns a bogus exit status (the child
- process ID). Fix by Lamont Jones of Hewlett-Packard. File:
- sendmail/sendmail.c.
+ Bugfix: "sendmail -bd" returns a bogus exit status (the
+ child process ID). Fix by Lamont Jones of Hewlett-Packard.
+ File: sendmail/sendmail.c.
19990824
Changed "permit_address_map" into "permit_recipient_map"
and added a test for the case that they specify a lookup
- table on the right-hand side of an SMTPD access map.
- File: smtpd/smtpd_access.c.
+ table on the right-hand side of an SMTPD access map. File:
+ smtpd/smtpd_access.c.
Cleanup: removed spurious sender address checks for <>.
File: smtpd/smtpd_check.c.
20000623
Bugfix: the SMTP server did not reset the so-called junk
- command counter after successful delivery (Mark Hoffman
- @ wallst.com). File: smtpd/smtpd.c.
+ command counter after successful delivery (Mark Hoffman @
+ wallst.com). File: smtpd/smtpd.c.
20000625
20001025
Bugfix: virtual map expansion stopped too early with
- self-referential aliases. Reported by Michael Douglass
- @ datafoundry.net. File: cleanup/cleanup_map1n.c.
+ self-referential aliases. Reported by Michael Douglass @
+ datafoundry.net. File: cleanup/cleanup_map1n.c.
20001026
of the postlock command, and of local delivery to mailbox
or /file/name. Files: util/safe_open.c, util/myflock.c,
global/deliver_flock.c, global/mbox_conf.c, global/mbox_open.c.
- local/mailbox.c, local/file.c, postlock/postlock.c.
+ local/mailbox.c, local/file.c, postlock/postlock.c.
Compatibility: the old sun_mailtool_compatibility parameter
is being phased out. It still works (by turning off
20001217
Bugfix: reorganized some code in the MYSQL client to end
- a number of memory allocation/deallocation problems.
- This code needs more work. File: dict_mysql.c.
+ a number of memory allocation/deallocation problems. This
+ code needs more work. File: dict_mysql.c.
20001218
20010121
Workaround: specify "broken_sasl_auth_clients = yes" in
- order to support old Microsoft clients that implement
- a non-standard version of RFC 2554 (AUTH command).
+ order to support old Microsoft clients that implement a
+ non-standard version of RFC 2554 (AUTH command).
Workaround: Lotus Domino 5.0.4 violates RFC 2554 and replies
to EHLO with AUTH=LOGIN. File: smtp/smtp_proto.c.
{qmgr,nqmgr}/qmgr_message.c.
Workaround: Solaris socketpair() can fail with EINTR. Added
- a sane_socketpair.c module that joins the ranks of the other
- sane_whatever workarounds. Reported by Andrew McNamara.
+ a sane_socketpair.c module that joins the ranks of the
+ other sane_whatever workarounds. Reported by Andrew McNamara.
File: util/sane_socketpair.[hc]
20010222
Portability: MACOSX patches by Gerben Wierda.
Portability: Solaris /dev/null is a symlink, which tripped
- up the code to safely open a file before local delivery. We now
- grudgingly allow symlinks owned by root. File: util/safe_open.c.
+ up the code to safely open a file before local delivery.
+ We now grudgingly allow symlinks owned by root. File:
+ util/safe_open.c.
20010224
do not immediately close the client side, but close it from
a background thread that waits until the server closes the
socket first. This avoids trouble with socket implementations
- that destroy a socket when the client closes a socket
- before the server has received the client's data. Files:
+ that destroy a socket when the client closes a socket before
+ the server has received the client's data. Files:
util/{inet,unix,stream}_trigger.c, util/events.c,
master/master_trigger.c, postkick/postkick.c.
Workaround: some MTAs fall on their face when they receive
unexpectedly long lines. From now on, Postfix defaults to
- breaking long lines at 2048 (like Sendmail so it has got to
- be right). To get the old, content preserving, behavior
+ breaking long lines at 2048 (like Sendmail so it has got
+ to be right). To get the old, content preserving, behavior
specify "smtp_truncate_lines = no". File: smtp/smtp_proto.c.
20010614
Robustness: add a file size limit to the sendmail and
postdrop submission programs to stop run-away process
- accidents. This is not a defense against DOS attack.
- Files: sendmail/sendmail.c, postdrop/postdrop.c.
+ accidents. This is not a defense against DOS attack. Files:
+ sendmail/sendmail.c, postdrop/postdrop.c.
That resulted in a considerable amount of work to properly
propagate "file too large" conditions back to the sendmail
20020114
- Bugfix: alternate_config_directories did not take comma
- or whitespace as separators. File: global/mail_conf.c.
- Victor Duchovni, Morgan Stanley.
+ Bugfix: alternate_config_directories did not take comma or
+ whitespace as separators. File: global/mail_conf.c. Victor
+ Duchovni, Morgan Stanley.
Bugfix: the rewritten postfix-install script did not chattr
+S the Postfix queue.
Documentation: add note to sendmail manual page that running
"sendmail -bs" as $mail_owner enables SMTP server UCE and
- access control checks. This is meant for use from inetd etc.
- Matthias Andree.
+ access control checks. This is meant for use from inetd
+ etc. Matthias Andree.
20020311
Cleanup: add a msg_warn() call when fork() fails in
pipe_command(), to make problems easier to investigate.
Chris Wedgwood. File: global/pipe_command.c.
-
+
20020320
Feature: smtp_helo_name parameter to specify the hostname
Cleanup: install parameter defaults can now be overruled
from makedefs: sendmail_path, mailq_path, newaliases_path,
- command_directory, daemon_directory. Based on code by
- Victor Duchovni, Morgan Stanley. File: util/sys_defs.h.
+ command_directory, daemon_directory. Based on code by Victor
+ Duchovni, Morgan Stanley. File: util/sys_defs.h.
20020411
20020418
Documentation: added CPU saving patterns for quickly skipping
- base 64 encoded text in message bodies. Liviu Daia.
- Files: {proto,conf}/pcre_table, {proto,conf}/regexp_table,
+ base 64 encoded text in message bodies. Liviu Daia. Files:
+ {proto,conf}/pcre_table, {proto,conf}/regexp_table,
conf/sample_{regexp,pcre}_body.cf.
20020426
util/dict_ldap.c.
Feature: lots of new LDAP stuff: result_filter (filter to
- expand results from queries), chase_referrals,
- LaMont Jones, HP. The LDAP bind timeout now works thanks
- to Victor Duchovni, Morgan Stanley. File: util/dict_ldap.c.
+ expand results from queries), chase_referrals, LaMont Jones,
+ HP. The LDAP bind timeout now works thanks to Victor
+ Duchovni, Morgan Stanley. File: util/dict_ldap.c.
Cleanup: specify "resolve_dequoted_address = no" to prevent
Postfix from looking inside quotes for extra @ etc. characters
20020521-22
- Feature: 8-bit to quoted-printable conversion. First use in
- the Postfix SMTP client. File: smtp/smtp_proto.c.
+ Feature: 8-bit to quoted-printable conversion. First use
+ in the Postfix SMTP client. File: smtp/smtp_proto.c.
Logging: the Postfix SMTP and LMTP clients now report the
the protocol stage when they report a server reply. File:
20020801
- Cleanup: the warning message for matched header/body
- content was misleading. File: cleanup/cleanup_message.c.
+ Cleanup: the warning message for matched header/body content
+ was misleading. File: cleanup/cleanup_message.c.
Safety: moved the "postsuper -r ALL" operation after the
"postsuper -s" check that makes queue file names match
20020810
Feature: new smtp-sink command-line options to specify the
- SMTP hostname, to disable ESMTP protocol support, to
- disable 8BITMIME support, and to syslog selected commands.
- File: smtpstone/smtp-sink.c.
+ SMTP hostname, to disable ESMTP protocol support, to disable
+ 8BITMIME support, and to syslog selected commands. File:
+ smtpstone/smtp-sink.c.
20020814
20020821
Feature: HOLD and DISCARD actions in SMTPD access tables.
- These requests are propagated to the cleanup daemon.
- Files: cleanup/cleanup_envelope.c smtpd/smtpd_check.c.
+ These requests are propagated to the cleanup daemon. Files:
+ cleanup/cleanup_envelope.c smtpd/smtpd_check.c.
Cleanup: eliminate unnecessary references to the obsolete
program_directory configuration parameter (but keep the
This involved a further rewrite of the regexp map code.
File: util/dict_regexp.c.
-
+
200209010
Bugfix: the SMTP client produced suprious warnings about
20020923
Cleanup. Renamed the RBL features according to a scheme
- that was suggested by Liviu Daia in October 2001. The
- names are reject_rbl_client and reject_rhsbl_sender,
- respectively. Added domain name based reject_rhsbl_client
- and reject_rhsbl_recipient restrictions for completeness.
- The reject_rbl restriction name is still recognized for
+ that was suggested by Liviu Daia in October 2001. The names
+ are reject_rbl_client and reject_rhsbl_sender, respectively.
+ Added domain name based reject_rhsbl_client and
+ reject_rhsbl_recipient restrictions for completeness. The
+ reject_rbl restriction name is still recognized for
compatibility with systems maintained by LaMont Jones.
20020924
In the mean time, Linux mail delivery software seems to
have standardized on fcntl() locks. File: util/sys_defs.h.
- Feature: body_checks_size_limit parameter to specify how much
- of a message body segment (or attachment, if you prefer to
- use that term) is subjected to body_checks inspection.
+ Feature: body_checks_size_limit parameter to specify how
+ much of a message body segment (or attachment, if you prefer
+ to use that term) is subjected to body_checks inspection.
Default limit: 50 kbytes. Files: global/mime_state.c,
cleanup/cleanup_message.c.
20021124
- Bugfix: don't use same VSTRING buffer for reading and writing.
- File: verify/verify.c.
+ Bugfix: don't use same VSTRING buffer for reading and
+ writing. File: verify/verify.c.
20021128
service. Fix: use "private/proxymap" if possible, otherwise
use "$queue_dir/private/proxymap". File: global/dict_proxy.c.
- Robustness: daemons now chdir() to the queue directory before
- running the pre-jail initialization code, so that daemons
- running in stand-alone mode produce more consistent results.
- Files: master/single_server.c, master/multi_server.c.
+ Robustness: daemons now chdir() to the queue directory
+ before running the pre-jail initialization code, so that
+ daemons running in stand-alone mode produce more consistent
+ results. Files: master/single_server.c, master/multi_server.c.
master/trigger_server.c.
Bugfix: "sendmail -bs" tried to access the proxymap service.
20030418
- Bugfix: "sendmail -t" broke with unrecognized message headers.
+ Bugfix: "sendmail -t" broke with unrecognized message
+ headers.
20030419
- Feature: "postcat -q" searches the queue for the named file.
+ Feature: "postcat -q" searches the queue for the named
+ file.
Cleanup: made postcat "record names" output more consistent.
Workaround: IRIX select() reports that a non-blocking file
descriptor is writable while write() transfers zero bytes.
- File: util/vstream.c.
-
-20030519
-
- Feature: new require_{date,from,message_id,received}_header
- restriction to reject SMTP mail when some message header
- is missing. Only the From: and Date: headers are actually
- required by Internet mail standards; the Received: and
- Message-ID: headers are optional, but these are often
- missing from SPAM email. Files: global/cleanup_user.h,
- global/cleanup_strerror.c, smtpd/smtpd_check.c,
- cleanup/cleanup_message.c.
+ File: util/vstream.c. Superseded by change 20030523.
20030520
Richard Stockton, Gramma Software. Files:
cleanup/cleanup_envelope.c, cleanup/cleanup_extracted.c.
-2003052[34]
+20030523
+
+ Workaround: IRIX select() reports that a non-blocking file
+ descriptor is writable while write() transfers zero bytes.
+ File: global/pipe_command.c.
+
+2003052[3-6]
Cleanup: rewrote the queue file record processing loops in
cleanup and in [n]qmgr. This code had deteriorated a lot
*qmgr/qmgr_message.c.
Cleanup: Postfix no longer produces queue files with
- backwards compatibility for Postfix versions < 1.0 (a.k.a.
- 20010228). File: cleanup/cleanup_extracted.c.
+ backwards compatibility data for Postfix versions < 1.0
+ (a.k.a. 20010228). Files: cleanup/cleanup_extracted.c,
+ showq/showq.c.
+
+20030528
+
+ Compatibility: "sendmail -q<time>" without -bd option causes
+ the command to exit instead of waiting for input on the
+ standard input stream. File: sendmail/sendmail.c.
Open problems:
4 - Building on a supported system
==================================
-If your system is supported, it is one of
+At some point in time, a version of Postfix was supported on:
- AIX 3.2.5 (long ago)
- AIX 4.1.x (long ago)
- AIX 4.2.0 (long ago)
- AIX 4.3.x (long ago)
+ AIX 3.2.5
+ AIX 4.1.x
+ AIX 4.2.0
+ AIX 4.3.x
AIX 5.2
- BSD/OS 2.x (long ago)
- BSD/OS 3.x (long ago)
- BSD/OS 4.x (long ago)
- Darwin 1.x (long ago)
- FreeBSD 2.x (long ago)
- FreeBSD 3.x (long ago)
+ BSD/OS 2.x
+ BSD/OS 3.x
+ BSD/OS 4.x
+ Darwin 1.x
+ FreeBSD 2.x
+ FreeBSD 3.x
FreeBSD 4.x
FreeBSD 5.x
HP-UX 9.x
Linux RedHat 5.x
Linux RedHat 6.x
Linux RedHat 7.x
- Linux Slackware 3.x (long ago)
+ Linux Slackware 3.x
Linux Slackware 4.x
Linux Slackware 7.x
Linux SuSE 5.x
Linux SuSE 6.x
Linux SuSE 7.x
Mac OS X
- NEXTSTEP 3.x (long ago)
+ NEXTSTEP 3.x
NetBSD 1.x
OPENSTEP 4.x
OSF1.V3 (Digital UNIX)
address is deliverable, even when the address will bounce AFTER
that MTA accepts it.
+Postfix assumes that an address is undeliverable when the nearest
+MTA for the address rejects the probe, regardless of the reason
+for rejection (client rejected, HELO rejected, MAIL FROM rejected,
+etc.). Thus, Postfix rejects mail when the sender's MTA rejects
+mail from your machine. This is a good thing.
+
Unfortunately, some major sites such as YAHOO do not reject unknown
addresses in reply to the RCPT TO command, but report a delivery
failure in response to "end of data" after a message is transferred.
precedence over filters specified with the main.cf content_filter
parameter.
-- Only the last FILTER action from smtpd access maps or in
+- Only the last FILTER action from smtpd access maps or from
header/body_checks takes effect.
- The same content filter is applied to all the recipients of a
--- /dev/null
+#!/bin/sh
+
+# Solaris 8 version by Matthew X. Economou. Caution: this copies
+# too many files. There is no need to copy libc.so and other files
+# that are already linked in before a Postfix daemon chroots itself.
+
+## Copy any shared libraries, device entries, or configuration files
+## needed by Postfix into the jail.
+binlist="
+/usr/libexec/postfix/virtual
+/usr/libexec/postfix/trivial-rewrite
+/usr/libexec/postfix/spawn
+/usr/libexec/postfix/smtpd
+/usr/libexec/postfix/smtp
+/usr/libexec/postfix/showq
+/usr/libexec/postfix/qmqpd
+/usr/libexec/postfix/qmgr
+/usr/libexec/postfix/proxymap
+/usr/libexec/postfix/pipe
+/usr/libexec/postfix/pickup
+/usr/libexec/postfix/nqmgr
+/usr/libexec/postfix/master
+/usr/libexec/postfix/local
+/usr/libexec/postfix/lmtp
+/usr/libexec/postfix/flush
+/usr/libexec/postfix/error
+/usr/libexec/postfix/cleanup
+/usr/libexec/postfix/bounce
+/usr/lib/sendmail
+/usr/sbin/postsuper
+/usr/sbin/postqueue
+/usr/sbin/postmap
+/usr/sbin/postlog
+/usr/sbin/postlock
+/usr/sbin/postkick
+/usr/sbin/postfix
+/usr/sbin/postdrop
+/usr/sbin/postconf
+/usr/sbin/postcat
+/usr/sbin/postalias
+"
+for i in `xargs ldd $binlist | grep -v '^[^:]*:' | sort | uniq | sed -e 's/^[^ ]* =>//' | awk '{print $1}'`; do
+ mkdir -p /var/spool/postfix`dirname $i`
+ ## Sun's version of tar sucks. We'll have to remove the leading
+ ## slashes from file names ourself, otherwise the copy doesn't
+ ## work.
+ (cd / && tar cphf - `echo $i | sed -e 's/^\///'`) | (cd /var/spool/postfix && tar xpf -)
+done
+
+## More stuff for the jail, mostly discovered by inspection
+## (e.g. strings, lsof).
+for i in "
+/dev/zero
+/dev/null
+/dev/udp6
+/dev/tcp6
+/dev/udp
+/dev/tcp
+/dev/rawip
+/dev/ticlts
+/dev/ticotsord
+/dev/ticots
+/devices/pseudo/mm@0:zero
+/devices/pseudo/mm@0:null
+/devices/pseudo/udp6@0:udp6
+/devices/pseudo/tcp6@0:tcp6
+/devices/pseudo/udp@0:udp
+/devices/pseudo/tcp@0:tcp
+/devices/pseudo/icmp@0:icmp
+/devices/pseudo/tl@0:ticlts
+/devices/pseudo/tl@0:ticotsord
+/devices/pseudo/tl@0:ticots
+/etc/nsswitch.conf
+/etc/netconfig
+/etc/default/init
+/etc/inet/services
+/etc/services
+/usr/lib/ld.so
+/usr/lib/ld.so.1
+/usr/lib/sparcv9/straddr.so
+/usr/lib/straddr.so
+/usr/lib/libintl.so
+/usr/lib/libintl.so.1
+/usr/lib/libw.so
+/usr/lib/libw.so.1
+/usr/lib/nss_nis.so.1
+/usr/lib/nss_nisplus.so.1
+/usr/lib/nss_dns.so.1
+/usr/lib/nss_files.so.1
+/usr/share/lib/zoneinfo
+/var/ld/ld.config
+"; do
+ mkdir -p /var/spool/postfix`dirname $i`
+ (cd / && tar cpf - `echo $i | sed -e 's/^\///'`) | (cd /var/spool/postfix && tar xpf -)
+done
+
+exit 0
int hop_count; /* count of received: headers */
char *resent; /* any resent- header seen */
BH_TABLE *dups; /* recipient dup filter */
- long warn_time; /* cleanup_envelope.c */
void (*action) (struct CLEANUP_STATE *, int, const char *, int);
off_t data_offset; /* start of message content */
off_t xtra_offset; /* start of extracted content */
+ int warn_seen; /* REC_TYPE_WARN seen */
int verp_seen; /* REC_TYPE_VERP seen */
int end_seen; /* REC_TYPE_END seen */
int rcpt_count; /* recipient count */
return;
}
if (strchr(REC_TYPE_ENVELOPE, type) == 0) {
- msg_warn("%s: unexpected record type %d in envelope: message rejected",
+ msg_warn("%s: message rejected: unexpected record type %d in envelope",
state->queue_id, type);
state->errs |= CLEANUP_STAT_BAD;
return;
}
+
+ /*
+ * The code for processing recipient records is first, because there can
+ * be lots of them. However, recipient records appear at the end of the
+ * initial or extracted envelope, so that the queue manager does not have
+ * to read the whole envelope before it can start deliveries.
+ */
if (type == REC_TYPE_RCPT) {
+ state->flags |= CLEANUP_FLAG_INRCPT;
if (state->sender == 0) { /* protect showq */
- msg_warn("%s: envelope recipient precedes sender: message rejected",
+ msg_warn("%s: message rejected: envelope recipient precedes sender",
state->queue_id);
state->errs |= CLEANUP_STAT_BAD;
return;
return;
}
if (type == REC_TYPE_DONE) {
+ state->flags |= CLEANUP_FLAG_INRCPT;
if (state->orig_rcpt != 0) {
myfree(state->orig_rcpt);
state->orig_rcpt = 0;
}
if (state->orig_rcpt != 0) {
/* REC_TYPE_ORCP must be followed by REC_TYPE_RCPT or REC_TYPE DONE. */
- msg_warn("%s: out-of-order original recipient record <%.200s>",
+ msg_warn("%s: ignoring out-of-order original recipient record <%.200s>",
state->queue_id, state->orig_rcpt);
myfree(state->orig_rcpt);
state->orig_rcpt = 0;
}
if (type == REC_TYPE_ORCP) {
+ state->flags |= CLEANUP_FLAG_INRCPT;
state->orig_rcpt = mystrdup(buf);
return;
}
+
+ /*
+ * These non-recipient records may appear before or after recipient
+ * records. In order to keep recipient records pure, We take away these
+ * non-recipient records from the input, and output them at the start of
+ * the extracted envelope segment.
+ */
+ if (type == REC_TYPE_FILT) {
+ /* Last instance wins. */
+ if (strchr(buf, ':') == 0) {
+ msg_warn("%s: ignoring invalid content filter: %.100s",
+ state->queue_id, buf);
+ return;
+ }
+ if (state->filter)
+ myfree(state->filter);
+ state->filter = mystrdup(buf);
+ return;
+ }
+ if (type == REC_TYPE_RDR) {
+ /* Last instance wins. */
+ if (strchr(buf, '@') == 0) {
+ msg_warn("%s: ignoring invalid redirect address: %.100s",
+ state->queue_id, buf);
+ return;
+ }
+ if (state->redirect)
+ myfree(state->redirect);
+ state->redirect = mystrdup(buf);
+ return;
+ }
+
+ /*
+ * The following records must not appear after recipient records. We
+ * force the warning record before the sender record so we know when
+ * (not) to emit a warning record. A warning or size record may already
+ * be present when mail is requeued with "postsuper -r".
+ */
+ if (type != REC_TYPE_MESG && (state->flags & CLEANUP_FLAG_INRCPT) != 0) {
+ msg_warn("%s: ignoring %s record after initial envelope recipients",
+ state->queue_id, rec_type_name(type));
+ return;
+ }
+ if (type == REC_TYPE_SIZE)
+ /* Use our own SIZE record instead. */
+ return;
if (type == REC_TYPE_TIME) {
- /* First definition wins. */
+ /* First instance wins. */
if (state->time == 0) {
state->time = atol(buf);
cleanup_out(state, type, buf, len);
return;
}
if (type == REC_TYPE_FULL) {
- /* First definition wins. */
+ /* First instance wins. */
if (state->fullname == 0) {
state->fullname = mystrdup(buf);
cleanup_out(state, type, buf, len);
if (type == REC_TYPE_FROM) {
/* Allow only one instance. */
if (state->sender != 0) {
- msg_warn("%s: too many envelope sender records: message rejected",
+ msg_warn("%s: message rejected: multiple envelope sender records",
state->queue_id);
state->errs |= CLEANUP_STAT_BAD;
return;
}
+ /* Kluge to force REC_TYPE_WARN before recipients. */
+ if (state->warn_seen == 0 && var_delay_warn_time > 0) {
+ cleanup_out_format(state, REC_TYPE_WARN, REC_TYPE_WARN_FORMAT,
+ (long) var_delay_warn_time);
+ state->warn_seen = 1;
+ }
cleanup_addr_sender(state, buf);
return;
}
if (type == REC_TYPE_WARN) {
- /* First definition wins. */
- if (state->warn_time == 0) {
- if ((state->warn_time = atol(buf)) < 0) {
- msg_warn("%s: bad arrival time record: %s: message rejected",
+ /* First instance wins. */
+ if (state->warn_seen == 0) {
+ if (atoi(buf) < 0) {
+ msg_warn("%s: message rejected: bad warning time: %.100s",
state->queue_id, buf);
state->errs |= CLEANUP_STAT_BAD;
+ return;
}
+ state->warn_seen = 1;
+ cleanup_out(state, type, buf, len);
}
return;
}
if (type == REC_TYPE_VERP) {
- /* First definition wins. */
+ /* First instance wins. */
if (state->verp_seen == 0) {
if ((error_text = verp_delims_verify(buf)) != 0) {
- msg_warn("%s: %s: \"%s\": message rejected",
+ msg_warn("%s: message rejected: %s: %.100s",
state->queue_id, error_text, buf);
state->errs |= CLEANUP_STAT_BAD;
return;
return;
}
if (type == REC_TYPE_ATTR) {
- /* Pass through. Last definition wins. */
+ /* Pass through. Last instance wins. */
char *sbuf;
if (state->attr->used >= var_qattr_count_limit) {
- msg_warn("%s: queue file attribute count exceeds safety limit %d"
- ": message rejected",
+ msg_warn("%s: message rejected: attribute count exceeds limit %d",
state->queue_id, var_qattr_count_limit);
state->errs |= CLEANUP_STAT_BAD;
return;
}
sbuf = mystrdup(buf);
if ((error_text = split_nameval(sbuf, &attr_name, &attr_value)) != 0) {
- msg_warn("%s: malformed attribute: %s: %.100s: message rejected",
+ msg_warn("%s: message rejected: malformed attribute: %s: %.100s",
state->queue_id, error_text, buf);
state->errs |= CLEANUP_STAT_BAD;
myfree(sbuf);
cleanup_out(state, type, buf, len);
return;
}
- if (type == REC_TYPE_SIZE)
- /* Use our own SIZE record instead. */
- return;
if (type != REC_TYPE_MESG) {
- /* Anything else. Pass through. */
+ /* Any other allowed record type. Pass through. */
cleanup_out(state, type, buf, len);
return;
}
/*
- * On the transition from envelope segment to content segment, do some
- * sanity checks.
+ * On the transition from initial envelope segment to content segment, do
+ * some sanity checks.
*
- * If senders can be specified in the extracted envelope segment, then we
- * need to move the VERP test there, too.
+ * XXX If senders can be specified in the extracted envelope segment (this
+ * could reduce qmqpd's memory requirements), then we need to move the
+ * VERP test there, too.
*/
if (state->sender == 0 || state->time == 0) {
- msg_warn("%s: missing sender or time envelope record: message rejected",
+ msg_warn("%s: message rejected: missing sender or time envelope record",
state->queue_id);
state->errs |= CLEANUP_STAT_BAD;
return;
}
if (state->verp_seen && (state->sender == 0 || *state->sender == 0)) {
- msg_warn("%s: VERP request with no or null sender: message rejected",
+ msg_warn("%s: message rejected: VERP request with no or null sender",
state->queue_id);
state->errs |= CLEANUP_STAT_BAD;
return;
}
-
- /*
- * Emit records for information that we collected from the envelope
- * segment.
- */
- if (state->warn_time == 0 && var_delay_warn_time > 0)
- state->warn_time = state->time + var_delay_warn_time;
- if (state->warn_time)
- cleanup_out_format(state, REC_TYPE_WARN, REC_TYPE_WARN_FORMAT,
- state->warn_time);
-
+ state->flags &= ~CLEANUP_FLAG_INRCPT;
state->action = cleanup_message;
}
/* int len;
/* DESCRIPTION
/* This module processes message records with information extracted
-/* from message content, or with recipients that are stored after the
-/* message content. It updates recipient records, and writes extracted
-/* information records to the output.
+/* from the initial message envelope or from the message content, or
+/* with recipients that are stored after the message content. It
+/* updates recipient records, and writes extracted information records
+/* to the output.
/*
/* Arguments:
/* .IP state
#define STR(x) vstring_str(x)
-static void cleanup_extracted_process(CLEANUP_STATE *, int, const char *, int);
-
- /*
- * The following queue file records are generated from message header or
- * message body content. We may encounter them in extracted envelope
- * segments after mail is re-injected with "postsuper -r" and we should
- * ignore them. It might be infinitesimally faster to move this test to the
- * pickup daemon, but that would make program maintenance more difficult.
- */
-static char cleanup_extracted_generated[] = {
- REC_TYPE_RRTO, /* return-receipt-to */
- REC_TYPE_ERTO, /* errors-to */
- REC_TYPE_FILT, /* content filter */
- REC_TYPE_INSP, /* content inspector */
- REC_TYPE_RDR, /* redirect address */
- REC_TYPE_ATTR, /* some header attribute */
- 0,
-};
+static void cleanup_extracted_non_rcpt(CLEANUP_STATE *, int, const char *, int);
+static void cleanup_extracted_rcpt(CLEANUP_STATE *, int, const char *, int);
/* cleanup_extracted - initialize extracted segment */
void cleanup_extracted(CLEANUP_STATE *state, int type,
const char *buf, int len)
{
- const char *encoding;
/*
* Start the extracted segment.
cleanup_out_string(state, REC_TYPE_XTRA, "");
/*
- * Postfix keeps all information related to an email message is in a
- * write-once file, including the envelope sender and recipients, and the
- * message content. This design maximizes robustness: one file is easier
- * to keep track of than multiple files, and write-once means that no
- * operation ever needs to be undone. This design also minimizes file
- * system overhead, because creating and removing files is relatively
- * expensive compared to writing files. Separate files are used for
- * logging the causes of deferral or failed delivery.
- *
- * A Postfix queue file consists of three segments.
- *
- * 1) The initial envelope segment with the arrival time, sender address,
- * recipients, and some other stuff that can be recorded before the
- * message content is received, including non-recipient information that
- * results from actions in Postfix SMTP server access tables. In this
- * segment, recipient records may be preceded or followed by
- * non-recipient records.
- *
- * 2) The message content segment with the message headers and body. The
- * message body includes all the MIME segments, if there are any.
- *
- * 3) The extracted envelope segment with information that was extracted
- * from message headers or from the message body, including recipient
- * addresses that were extracted from message headers, and non-recipient
- * information that results from actions in header/body_checks patterns.
- * In this segment, all non-recipient records precede the recipient
- * records.
- *
- * There are two queue file layouts.
- *
- * A) All recipient records are in the initial envelope segment, except for
- * the optional always_bcc recipient which is always stored in the
- * extracted envelope segment. The queue manager reads as many recipients
- * as it can from the initial envelope segment, and then examines all
- * remaining initial envelope records and all extracted envelope records,
- * picking up non-recipient information. This organization favors
- * messages with fewer than $qmgr_active_recipient_limit recipients.
- *
- * B) All recipient records are stored in the extracted envelope segment,
- * after all non-recipient records. The queue manager is guaranteed to
- * have read all the non-recipient records before it sees the first
- * recipient record. This organization can handle messages with very
- * large numbers of recipients.
+ * Pass control to the actual envelope processing routine.
+ */
+ state->action = cleanup_extracted_non_rcpt;
+ cleanup_extracted_non_rcpt(state, type, buf, len);
+}
+
+/* cleanup_extracted_non_rcpt - process non-recipient records */
+
+void cleanup_extracted_non_rcpt(CLEANUP_STATE *state, int type,
+ const char *buf, int len)
+{
+ const char *encoding;
+
+ if (msg_verbose)
+ msg_info("extracted envelope %c %.*s", type, len, buf);
+
+ if (strchr(REC_TYPE_EXTRACT, type) == 0) {
+ msg_warn("%s: message rejected: "
+ "unexpected record type %d in extracted envelope",
+ state->queue_id, type);
+ state->errs |= CLEANUP_STAT_BAD;
+ return;
+ }
+
+ /*
+ * The following records are taken away from the initial envelope segment
+ * and may be overruled by information from header/body_checks; then they
+ * are emitted at the start of the extracted envelope segment.
*
- * All this is the result of an evolutionary process, where compatibility
- * between Postfix versions was a major goal as new features were added.
- * Therefore the file organization is not optimal from a performance
- * point of view. In hindsight, the non-recipient information that
- * follows recipients in the initial envelope segment could be moved to
- * the extracted envelope segment. This would improve file organization
- * A)'s performance with very large numbers of recipients, by eliminating
- * the need to examine all initial envelope records before starting
- * deliveries.
+ * If we encounter these records here, then the message was subjected to
+ * "postsuper -r" and we can ignore these records if we already have
+ * information from header/body_checks.
*/
- if (state->filter != 0)
- cleanup_out_string(state, REC_TYPE_FILT, state->filter);
+ if (type == REC_TYPE_FILT) {
+ /* Our own header/body_checks information wins. */
+ if (state->filter == 0)
+ state->filter = mystrdup(buf);
+ return;
+ }
+ if (type == REC_TYPE_RDR) {
+ /* Our own header/body_checks information wins. */
+ if (state->redirect == 0)
+ state->redirect = mystrdup(buf);
+ return;
+ }
/*
- * The optional redirect target address from header/body_checks actions.
+ * Ignore records that the cleanup server extracts from message headers.
+ * These records may appear in "postsuper -r" email.
*/
- if (state->redirect != 0)
- cleanup_out_string(state, REC_TYPE_RDR, state->redirect);
+ if (type == REC_TYPE_RRTO)
+ /* Use our own headers extracted return address. */
+ return;
+ if (type == REC_TYPE_ERTO)
+ /* Use our own headers extracted error address. */
+ return;
+ if (type == REC_TYPE_ATTR)
+ /* Use our own headers extracted content encoding. */
+ return;
+
+ if (type != REC_TYPE_END && type != REC_TYPE_FROM
+ && type != REC_TYPE_DONE && type != REC_TYPE_ORCP) {
+ /* Any other allowed non-recipient record. Pass through. */
+ cleanup_out(state, type, buf, len);
+ return;
+ }
/*
- * Older Postfix versions didn't MIME emit encoding information, so this
- * record can only be optional.
+ * At the end of the non-recipient record section, emit optional
+ * information from header/body_checks actions, from the start of the
+ * extracted envelope, or from the initial envelope.
*/
+ if (state->filter != 0)
+ cleanup_out_string(state, REC_TYPE_FILT, state->filter);
+
+ if (state->redirect != 0)
+ cleanup_out_string(state, REC_TYPE_RDR, state->redirect);
+
if ((encoding = nvtable_find(state->attr, MAIL_ATTR_ENCODING)) != 0)
cleanup_out_format(state, REC_TYPE_ATTR, "%s=%s",
MAIL_ATTR_ENCODING, encoding);
/*
- * Return-Receipt-To and Errors-To records are now optional.
+ * Terminate the non-recipient records with the Return-Receipt-To and
+ * Errors-To records. The queue manager relies on this information.
*/
- if (state->return_receipt)
- cleanup_out_string(state, REC_TYPE_RRTO, state->return_receipt);
- if (state->errors_to)
- cleanup_out_string(state, REC_TYPE_ERTO, state->errors_to);
+ cleanup_out_string(state, REC_TYPE_RRTO, state->return_receipt ?
+ state->return_receipt : "");
+
+ cleanup_out_string(state, REC_TYPE_ERTO, state->errors_to ?
+ state->errors_to : state->sender);
/*
- * Pass control to the routine that processes the extracted segment.
+ * Pass control to the routine that processes the recipient portion of
+ * the extracted segment.
*/
- state->action = cleanup_extracted_process;
- cleanup_extracted_process(state, type, buf, len);
+ state->action = cleanup_extracted_rcpt;
+ cleanup_extracted_rcpt(state, type, buf, len);
}
-/* cleanup_extracted_process - process extracted segment */
+/* cleanup_extracted_rcpt - process recipients in extracted segment */
-static void cleanup_extracted_process(CLEANUP_STATE *state, int type,
- const char *buf, int len)
+static void cleanup_extracted_rcpt(CLEANUP_STATE *state, int type,
+ const char *buf, int len)
{
- char *myname = "cleanup_extracted_process";
+ char *myname = "cleanup_extracted_rcpt";
if (msg_verbose)
msg_info("extracted envelope %c %.*s", type, len, buf);
if (strchr(REC_TYPE_EXTRACT, type) == 0) {
- msg_warn("%s: unexpected record type %d in extracted envelope"
- ": message rejected", state->queue_id, type);
+ msg_warn("%s: message rejected: "
+ "unexpected record type %d in extracted envelope",
+ state->queue_id, type);
state->errs |= CLEANUP_STAT_BAD;
return;
}
state->orig_rcpt = mystrdup(buf);
return;
}
- if (strchr(cleanup_extracted_generated, type) != 0)
- /* Use our own message header extracted information instead. */
- return;
if (type != REC_TYPE_END) {
- msg_warn("unexpected non-recipient record: %s", rec_type_name(type));
- cleanup_out(state, type, buf, len);
+ msg_warn("%s: ignoring %s record after extracted envelope recipients",
+ state->queue_id, rec_type_name(type));
return;
}
}
}
-/* cleanup_missing - handle missing message header */
-
-static void cleanup_missing(CLEANUP_STATE *state, const char *resent,
- const char *header)
-{
- const char *attr;
-
- if ((attr = nvtable_find(state->attr, MAIL_ATTR_ORIGIN)) == 0)
- attr = "unknown";
- vstring_sprintf(state->temp1, "%s: reject: missing %s%s header from %s;",
- state->queue_id, resent, header, attr);
- if (state->sender)
- vstring_sprintf_append(state->temp1, " from=<%s>", state->sender);
- if (state->recip)
- vstring_sprintf_append(state->temp1, " to=<%s>", state->recip);
- if ((attr = nvtable_find(state->attr, MAIL_ATTR_PROTO_NAME)) != 0)
- vstring_sprintf_append(state->temp1, " proto=%s", attr);
- if ((attr = nvtable_find(state->attr, MAIL_ATTR_HELO_NAME)) != 0)
- vstring_sprintf_append(state->temp1, " helo=<%s>", attr);
- msg_info("%s", vstring_str(state->temp1));
- state->errs |= CLEANUP_STAT_MISS_HDR;
-}
-
/* cleanup_header_done_callback - insert missing message headers */
static void cleanup_header_done_callback(void *context)
struct tm *tp;
TOK822 *token;
- /*
- * Postfix prepends a Received: message header, so we should see two when
- * one is required.
- */
- if ((state->flags & CLEANUP_FLAG_NEED_RCVD) && state->hop_count < 2) {
- cleanup_missing(state, "", "Received");
- return;
- }
-
/*
* Add a missing (Resent-)Message-Id: header. The message ID gives the
* time in GMT units, plus the local queue ID.
*/
if ((state->headers_seen & (1 << (state->resent[0] ?
HDR_RESENT_MESSAGE_ID : HDR_MESSAGE_ID))) == 0) {
- if (state->flags & CLEANUP_FLAG_NEED_MSGID) {
- cleanup_missing(state, state->resent, "Message-Id");
- return;
- }
tp = gmtime(&state->time);
strftime(time_stamp, sizeof(time_stamp), "%Y%m%d%H%M%S", tp);
cleanup_out_format(state, REC_TYPE_NORM, "%sMessage-Id: <%s.%s@%s>",
*/
if ((state->headers_seen & (1 << (state->resent[0] ?
HDR_RESENT_DATE : HDR_DATE))) == 0) {
- if (state->flags & CLEANUP_FLAG_NEED_DATE) {
- cleanup_missing(state, state->resent, "Date");
- return;
- }
cleanup_out_format(state, REC_TYPE_NORM, "%sDate: %s",
state->resent, mail_date(state->time));
}
*/
if ((state->headers_seen & (1 << (state->resent[0] ?
HDR_RESENT_FROM : HDR_FROM))) == 0) {
- if (state->flags & CLEANUP_FLAG_NEED_FROM) {
- cleanup_missing(state, state->resent, "From");
- return;
- }
quote_822_local(state->temp1, *state->sender ?
state->sender : MAIL_ADDR_MAIL_DAEMON);
vstring_sprintf(state->temp2, "%sFrom: %s",
--- /dev/null
+
+ /*
+ * Postfix keeps all information related to an email message is in a
+ * write-once file, including the envelope sender and recipients, and the
+ * message content. This design maximizes robustness: one file is easier
+ * to keep track of than multiple files, and write-once means that no
+ * operation ever needs to be undone. This design also minimizes file
+ * system overhead, because creating and removing files is relatively
+ * expensive compared to writing files. Separate files are used for
+ * logging the causes of deferral or failed delivery.
+ *
+ * A Postfix queue file consists of three segments.
+ *
+ * 1) The initial envelope segment with the arrival time, sender address,
+ * recipients, and some other stuff that can be recorded before the
+ * message content is received, including non-recipient information that
+ * results from actions in Postfix SMTP server access tables. In this
+ * segment, recipient records may be preceded or followed by
+ * non-recipient records.
+ *
+ * 2) The message content segment with the message headers and body. The
+ * message body includes all the MIME segments, if there are any.
+ *
+ * 3) The extracted envelope segment with information that was extracted
+ * from message headers or from the message body, including recipient
+ * addresses that were extracted from message headers, and non-recipient
+ * information that results from actions in header/body_checks patterns.
+ * In this segment, all non-recipient records precede the recipient
+ * records. The last non-recipient records are return-receipt-to and
+ * errors-to.
+ *
+ * There are two queue file layouts.
+ *
+ * A) All recipient records are in the initial envelope segment, except for
+ * the optional always_bcc recipient which is always stored in the
+ * extracted envelope segment. The queue manager reads as many recipients
+ * as it can from the initial envelope segment, and then examines all
+ * remaining initial envelope records and all extracted envelope records,
+ * picking up non-recipient information. This organization favors
+ * messages with fewer than $qmgr_active_recipient_limit recipients.
+ *
+ * B) All recipient records are stored in the extracted envelope segment,
+ * after all non-recipient records. The queue manager is guaranteed to
+ * have read all the non-recipient records before it sees the first
+ * recipient record. This organization can handle messages with very
+ * large numbers of recipients.
+ *
+ * All this is the result of an evolutionary process, where compatibility
+ * between Postfix versions was a major goal as new features were added.
+ * Therefore the file organization is not optimal from a performance
+ * point of view. In hindsight, the non-recipient information that
+ * follows recipients in the initial envelope segment could be moved to
+ * the extracted envelope segment. This would improve file organization
+ * A)'s performance with very large numbers of recipients, by eliminating
+ * the need to examine all initial envelope records before starting
+ * deliveries.
+ */
state->hop_count = 0;
state->resent = "";
state->dups = been_here_init(var_dup_filter_limit, BH_FLAG_FOLD);
- state->warn_time = 0;
state->action = cleanup_envelope;
state->data_offset = -1;
state->xtra_offset = -1;
+ state->warn_seen = 0;
state->verp_seen = 0;
state->end_seen = 0;
state->rcpt_count = 0;
CLEANUP_STAT_BAD, "Internal protocol error",
CLEANUP_STAT_RCPT, "No recipients specified",
CLEANUP_STAT_HOPS, "Too many hops",
- CLEANUP_STAT_MISS_HDR, "Missing message header",
CLEANUP_STAT_SIZE, "Message file too big",
CLEANUP_STAT_CONT, "Message content rejected",
CLEANUP_STAT_WRITE, "Error writing message file",
#define CLEANUP_FLAG_HOLD (1<<2) /* Place message on hold */
#define CLEANUP_FLAG_DISCARD (1<<3) /* Discard message silently */
#define CLEANUP_FLAG_BCC_OK (1<<4) /* Ok to add auto-BCC addresses */
-#define CLEANUP_FLAG_NEED_DATE (1<<5) /* Require (Resent:-)Date: */
-#define CLEANUP_FLAG_NEED_FROM (1<<6) /* Require (Resent:-)From: */
-#define CLEANUP_FLAG_NEED_MSGID (1<<7) /* Require (Resent:-)Message-Id: */
-#define CLEANUP_FLAG_NEED_RCVD (1<<8) /* Require two Received: headers */
+
+ /*
+ * Status.
+ */
+#define CLEANUP_FLAG_INRCPT (1<<16) /* Expecting recipient records only */
/*
* These are set on the fly while processing SMTP envelopes or message
* content.
*/
#define CLEANUP_FLAG_MASK_EXTRA \
- (CLEANUP_FLAG_HOLD | CLEANUP_FLAG_DISCARD | CLEANUP_FLAG_NEED_DATE | \
- CLEANUP_FLAG_NEED_FROM | CLEANUP_FLAG_NEED_MSGID | \
- CLEANUP_FLAG_NEED_RCVD)
+ (CLEANUP_FLAG_HOLD | CLEANUP_FLAG_DISCARD)
/*
* Diagnostics.
#define CLEANUP_STAT_SIZE (1<<2) /* Message file too big */
#define CLEANUP_STAT_CONT (1<<3) /* Message content rejected */
#define CLEANUP_STAT_HOPS (1<<4) /* Too many hops */
-#define CLEANUP_STAT_MISS_HDR (1<<5) /* Some missing header */
#define CLEANUP_STAT_RCPT (1<<6) /* No recipients found */
/*
abcdefghijklmnopqrstuvwxyz{|}~"
extern char *var_smtpd_exp_filter;
-#define REQUIRE_DATE_HDR "require_date_header"
-#define REQUIRE_FROM_HDR "require_from_header"
-#define REQUIRE_MSGID_HDR "require_message_id_header"
-#define REQUIRE_RCVD_HDR "require_received_header"
-
/*
* Heuristic to reject unknown local recipients at the SMTP port.
*/
* Patches change the patchlevel and the release date. Snapshots change the
* release date only, unless they include the same bugfix as a patch release.
*/
-#define MAIL_RELEASE_DATE "20030524"
+#define MAIL_RELEASE_DATE "20030526"
#define VAR_MAIL_VERSION "mail_version"
#define DEF_MAIL_VERSION "2.0.10-" MAIL_RELEASE_DATE
long warn_offset; /* warning bounce flag offset */
time_t warn_time; /* time next warning to be sent */
long data_offset; /* data seek offset */
- long extra_offset; /* extracted data seek offset */
char *queue_name; /* queue name */
char *queue_id; /* queue file */
char *encoding; /* content encoding */
if ((message->rcpt_offset = vstream_ftell(message->fp)) < 0)
msg_fatal("vstream_ftell %s: %m",
VSTREAM_PATH(message->fp));
- /* Must have examined all non-recipient records. */
- if (curr_offset > message->data_offset)
+ /* We already examined all non-recipient records. */
+ if (message->errors_to)
break;
+ /* Examine non-recipient records in extracted segment. */
+ if (curr_offset < message->data_offset
+ && vstream_fseek(message->fp, message->data_offset
+ + message->data_size, SEEK_SET) < 0)
+ msg_fatal("seek file %s: %m", VSTREAM_PATH(message->fp));
+ continue;
}
}
continue;
orig_rcpt = mystrdup(start);
continue;
}
+ if (message->errors_to)
+ /* We already examined all non-recipient records. */
+ continue;
if (rec_type == REC_TYPE_SIZE) {
if (message->data_offset == 0) {
if ((count = sscanf(start, "%ld %ld %d", &message->data_size,
if (rec_type == REC_TYPE_ERTO) {
if (message->errors_to == 0)
message->errors_to = mystrdup(start);
+ /* We already examined all non-recipient records. */
+ if (message->rcpt_offset)
+ break;
continue;
}
if (rec_type == REC_TYPE_RRTO) {
* Avoid clumsiness elsewhere in the program. When sending data across an
* IPC channel, sending an empty string is more convenient than sending a
* null pointer.
- *
- * Allow for Postfix versions that do not store return_receipt or errors_to
- * records.
*/
if (message->errors_to == 0)
- message->errors_to = mystrdup("");
+ message->errors_to = mystrdup(message->sender);
if (message->return_receipt == 0)
message->return_receipt = mystrdup("");
if (message->encoding == 0)
}
rewrite_clnt_internal(REWRITE_CANON, message->redirect_addr,
reply.recipient);
+
UPDATE(recipient->address, STR(reply.recipient));
if (qmgr_resolve_one(message, recipient,
recipient->address, &reply) < 0)
if ((message->rcpt_offset = vstream_ftell(message->fp)) < 0)
msg_fatal("vstream_ftell %s: %m",
VSTREAM_PATH(message->fp));
- /* Must have examined all non-recipient records. */
- if (curr_offset > message->data_offset)
+ /* We already examined all non-recipient records. */
+ if (message->errors_to)
break;
+ /* Examine non-recipient records in extracted segment. */
+ if (curr_offset < message->data_offset
+ && vstream_fseek(message->fp, message->data_offset
+ + message->data_size, SEEK_SET) < 0)
+ msg_fatal("seek file %s: %m", VSTREAM_PATH(message->fp));
+ continue;
}
}
continue;
orig_rcpt = mystrdup(start);
continue;
}
+ if (message->errors_to)
+ /* We already examined all non-recipient records. */
+ continue;
if (rec_type == REC_TYPE_SIZE) {
if (message->data_offset == 0) {
if ((count = sscanf(start, "%ld %ld %d", &message->data_size,
if (rec_type == REC_TYPE_ERTO) {
if (message->errors_to == 0)
message->errors_to = mystrdup(start);
+ /* We already examined all non-recipient records. */
+ if (message->rcpt_offset)
+ break;
continue;
}
if (rec_type == REC_TYPE_RRTO) {
* Avoid clumsiness elsewhere in the program. When sending data across an
* IPC channel, sending an empty string is more convenient than sending a
* null pointer.
- *
- * Allow for Postfix versions that do not store return_receipt or errors_to
- * records.
*/
if (message->errors_to == 0)
- message->errors_to = mystrdup("");
+ message->errors_to = mystrdup(message->sender);
if (message->return_receipt == 0)
message->return_receipt = mystrdup("");
if (message->encoding == 0)
} else if ((state->err & CLEANUP_STAT_RCPT) != 0) {
qmqpd_reply(state, DO_LOG, QMQPD_STAT_HARD,
"Error: no recipients specified");
- } else if ((state->err & CLEANUP_STAT_MISS_HDR) != 0) {
- qmqpd_reply(state, DO_LOG, QMQPD_STAT_HARD,
- "Error: missing message header");
} else {
qmqpd_reply(state, DO_LOG, QMQPD_STAT_RETRY,
"Error: internal error %d", state->err);
int flags = SM_FLAG_DEFAULT;
char *site_to_flush = 0;
char *encoding = 0;
+ char *qtime = 0;
/*
* Be consistent with file permissions.
break;
case 'q':
if (ISDIGIT(optarg[0])) {
- if (mode == SM_MODE_DAEMON) {
- if (msg_verbose)
- msg_info("-%c%s option ignored", c, optarg);
-
- }
+ qtime = optarg;
} else if (optarg[0] == 'R') {
site_to_flush = optarg + 1;
if (*site_to_flush == 0)
/*
* Start processing. Everything is delegated to external commands.
*/
+ if (qtime && mode != SM_MODE_DAEMON)
+ exit(0);
switch (mode) {
default:
msg_panic("unknown operation mode: %d", mode);
arrival_time = atol(start);
break;
case REC_TYPE_SIZE:
- if (sscanf(start, "%ld %ld", &msg_size, &msg_offset) == 2) {
+ if (sscanf(start, "%ld %ld", &msg_size, &msg_offset) == 2)
/* Postfix >= 1.0 (a.k.a. 20010228) style queue file. */
if (msg_size <= 0)
msg_size = size;
- }
break;
case REC_TYPE_FROM:
if (*start == 0)
*/
stream = vstream_fdopen(sock, O_RDWR);
if ((ch = VSTREAM_GETC(stream)) == VSTREAM_EOF) {
- vstring_sprintf(why, "connect to %s[%s]: server dropped connection without sending the initial greeting",
+ vstring_sprintf(why, "connect to %s[%s]: server dropped connection without sending the initial SMTP greeting",
addr->name, inet_ntoa(sin.sin_addr));
smtp_errno = SMTP_RETRY;
vstream_fclose(stream);
smtpd_sasl_mail_reset(state);
#endif
state->discard = 0;
+ if (state->filter) {
+ myfree(state->filter);
+ state->filter = 0;
+ }
+ if (state->redirect) {
+ myfree(state->redirect);
+ state->redirect = 0;
+ }
}
/* rcpt_cmd - process RCPT TO command */
}
/*
- * Send the end-of-segment markers.
+ * Send the end-of-content marker, then do some post-message checks
+and send the end-of-file marker.
*/
- if (state->err == CLEANUP_STAT_OK)
- if (rec_fputs(state->cleanup, REC_TYPE_XTRA, "") < 0
- || rec_fputs(state->cleanup, REC_TYPE_END, "") < 0
+ if (state->err == CLEANUP_STAT_OK) {
+ rec_fputs(state->cleanup, REC_TYPE_XTRA, "");
+ err = smtpd_check_dot(state);
+ if (rec_fputs(state->cleanup, REC_TYPE_END, "") < 0
|| vstream_fflush(state->cleanup))
state->err = CLEANUP_STAT_WRITE;
+ }
/*
* Finish the queue file or finish the cleanup conversation.
*/
- if (state->err == 0)
+ if (state->err == 0 && err == 0)
state->err = mail_stream_finish(state->dest, why = vstring_alloc(10));
else
mail_stream_cleanup(state->dest);
state->dest = 0;
state->cleanup = 0;
+ if (err != 0) {
+ smtpd_chat_reply(state, "%s", err);
+ return (-1);
+ }
+
/*
* Handle any errors. One message may suffer from multiple errors, so
* complain only about the most severe error. Forgive any previous client
} else if ((state->err & CLEANUP_STAT_WRITE) != 0) {
state->error_mask |= MAIL_ERROR_RESOURCE;
smtpd_chat_reply(state, "451 Error: queue file write error");
- } else if ((state->err & CLEANUP_STAT_MISS_HDR) != 0) {
- state->error_mask |= MAIL_ERROR_POLICY;
- smtpd_chat_reply(state, "550 Error: missing message header");
} else {
state->error_mask |= MAIL_ERROR_SOFTWARE;
smtpd_chat_reply(state, "451 Error: internal error %d", state->err);
int defer_if_permit_sender; /* force permit into warning */
int discard; /* discard message */
VSTRING *expand_buf; /* scratch space for $name expansion */
+ int session_hold; /* per-session hold action */
+ int session_discard; /* per-session discard_action */
+ char *session_filter; /* per-session filter action */
+ char *session_redirect; /* per-session redirect action */
+ char *filter; /* per-message filter action */
+ char *redirect; /* per-message redirect action */
} SMTPD_STATE;
extern void smtpd_state_init(SMTPD_STATE *, VSTREAM *);
/* Reject, defer or permit the request unconditionally. This is to be used
/* at the end of a restriction list in order to make the default
/* action explicit.
-/* .IP require_date_header
-/* .IP require_from_header
-/* .IP require_message_id_header
-/* .IP require_received_header
-/* Reject the message when it does not contain a Date: etc.
-/* message header. Only the Date: header is required by mail
-/* standards. The other headers are usually added by MTAs.
/* .IP reject_unknown_client
/* Reject the request when the client hostname could not be found.
/* The \fIunknown_client_reject_code\fR configuration parameter
*/
#define STR vstring_str
#define CONST_STR(x) ((const char *) vstring_str(x))
+#define STREQ(x,y) (*(x) == *(y) && strcmp((x), (y)) == 0)
+
+ /*
+ * Safety.
+ */
+#define SAFE_STRDUP(dst, src) { \
+ if (src) { \
+ if (dst) { \
+ myfree(dst); \
+ } \
+ dst = mystrdup(src); \
+ } \
+ }
/*
* If some decision can't be made due to a temporary error, then change
vstring_sprintf(error_text, "<%s>: %s triggers FILTER %s",
reply_name, reply_class, cmd_text);
log_whatsup(state, "filter", STR(error_text));
-#ifndef TEST
- rec_fprintf(state->dest->stream, REC_TYPE_FILT, "%s", cmd_text);
-#endif
+ /* This action must execute with every MAIL FROM command. */
+ if (var_smtpd_delay_reject == 0
+ && (STREQ(reply_class, SMTPD_NAME_CLIENT)
+ || STREQ(reply_class, SMTPD_NAME_HELO))) {
+ SAFE_STRDUP(state->session_filter, cmd_text);
+ } else {
+ SAFE_STRDUP(state->filter, cmd_text);
+ }
return (SMTPD_CHECK_DUNNO);
}
}
vstring_sprintf(error_text, "<%s>: %s %s", reply_name, reply_class,
*cmd_text ? cmd_text : "triggers HOLD action");
log_whatsup(state, "hold", STR(error_text));
+ /* This action must execute with every MAIL FROM command. */
+ if (var_smtpd_delay_reject == 0
+ && (STREQ(reply_class, SMTPD_NAME_CLIENT)
+ || STREQ(reply_class, SMTPD_NAME_HELO))) {
+ state->session_hold = 1;
+ } else {
#ifndef TEST
- rec_fprintf(state->dest->stream, REC_TYPE_FLGS, "%d",
- CLEANUP_FLAG_HOLD);
+ rec_fprintf(state->dest->stream, REC_TYPE_FLGS, "%d",
+ CLEANUP_FLAG_HOLD);
#endif
+ }
return (SMTPD_CHECK_DUNNO);
}
vstring_sprintf(error_text, "<%s>: %s %s", reply_name, reply_class,
*cmd_text ? cmd_text : "triggers DISCARD action");
log_whatsup(state, "discard", STR(error_text));
+ /* This action must execute with every MAIL FROM command. */
+ if (var_smtpd_delay_reject == 0
+ && (STREQ(reply_class, SMTPD_NAME_CLIENT)
+ || STREQ(reply_class, SMTPD_NAME_HELO))) {
+ state->session_discard = 1;
+ } else {
#ifndef TEST
- rec_fprintf(state->dest->stream, REC_TYPE_FLGS, "%d",
- CLEANUP_FLAG_DISCARD);
- state->discard = 1;
+ state->discard = 1;
+ rec_fprintf(state->dest->stream, REC_TYPE_FLGS, "%d",
+ CLEANUP_FLAG_DISCARD);
#endif
+ }
return (SMTPD_CHECK_OK);
}
vstring_sprintf(error_text, "<%s>: %s triggers REDIRECT %s",
reply_name, reply_class, cmd_text);
log_whatsup(state, "redirect", STR(error_text));
-#ifndef TEST
- rec_fprintf(state->dest->stream, REC_TYPE_RDR, "%s", cmd_text);
-#endif
+ /* This action must execute with every MAIL FROM command. */
+ if (var_smtpd_delay_reject == 0
+ && (STREQ(reply_class, SMTPD_NAME_CLIENT)
+ || STREQ(reply_class, SMTPD_NAME_HELO))) {
+ SAFE_STRDUP(state->session_redirect, cmd_text);
+ } else {
+ SAFE_STRDUP(state->redirect, cmd_text);
+ }
return (SMTPD_CHECK_DUNNO);
}
}
/*
* "sender_name" or "recipient_name".
*/
-#define STREQ(x,y) (*(x) == *(y) && strcmp((x), (y)) == 0)
-
else if (STREQ(suffix, MAIL_ATTR_S_NAME)) {
if (*addr) {
if ((p = strrchr(addr, '@')) != 0) {
DEFER_IF_REJECT2(state, MAIL_ERROR_POLICY,
"450 <%s>: %s rejected: defer_if_reject requested",
reply_name, reply_class);
- } else if (strcasecmp(name, REQUIRE_DATE_HDR) == 0) {
-#ifndef TEST
- rec_fprintf(state->dest->stream, REC_TYPE_FLGS, "%d",
- CLEANUP_FLAG_NEED_DATE);
-#endif
- } else if (strcasecmp(name, REQUIRE_FROM_HDR) == 0) {
-#ifndef TEST
- rec_fprintf(state->dest->stream, REC_TYPE_FLGS, "%d",
- CLEANUP_FLAG_NEED_FROM);
-#endif
- } else if (strcasecmp(name, REQUIRE_MSGID_HDR) == 0) {
-#ifndef TEST
- rec_fprintf(state->dest->stream, REC_TYPE_FLGS, "%d",
- CLEANUP_FLAG_NEED_MSGID);
-#endif
- } else if (strcasecmp(name, REQUIRE_RCVD_HDR) == 0) {
-#ifndef TEST
- rec_fprintf(state->dest->stream, REC_TYPE_FLGS, "%d",
- CLEANUP_FLAG_NEED_RCVD);
-#endif
}
/*
if (sender == 0)
return (0);
+ /*
+ * Actions that were triggered during connect or HELO need to be repeated
+ * with each MAIL FROM command.
+ *
+ * XXX Left-hand side should always be zero. But this may not be the case
+ * during stand-alone testing when commands can execute out of protocol.
+ */
+ if (var_smtpd_delay_reject == 0) {
+ if( state->session_hold)
+ rec_fprintf(state->dest->stream, REC_TYPE_FLGS, "%d",
+ CLEANUP_FLAG_HOLD);
+ if( state->session_discard)
+ rec_fprintf(state->dest->stream, REC_TYPE_FLGS, "%d",
+ CLEANUP_FLAG_DISCARD);
+ SAFE_STRDUP(state->filter, state->session_filter);
+ SAFE_STRDUP(state->redirect, state->session_redirect);
+ }
+
/*
* Minor kluge so that we can delegate work to the generic routine and so
* that we can syslog the recipient with the reject messages.
return (status == SMTPD_CHECK_REJECT ? STR(error_text) : 0);
}
+/* smtpd_check_dot - do stuff after message transfer */
+
+char *smtpd_check_dot(SMTPD_STATE *state)
+{
+ if (state->redirect)
+ rec_fprintf(state->dest->stream, REC_TYPE_RDR, "%s",
+ state->redirect);
+ if (state->filter)
+ rec_fprintf(state->dest->stream, REC_TYPE_FILT, "%s",
+ state->filter);
+ return (0);
+}
+
#ifdef TEST
/*
extern char *smtpd_check_rcpt(SMTPD_STATE *, char *);
extern char *smtpd_check_etrn(SMTPD_STATE *, char *);
extern char *smtpd_check_data(SMTPD_STATE *);
+extern char *smtpd_check_dot(SMTPD_STATE *);
/* LICENSE
/* .ad
state->defer_if_permit.reason = 0;
state->discard = 0;
state->expand_buf = 0;
+ state->session_hold = 0;
+ state->session_discard = 0;
+ state->session_filter = 0;
+ state->session_redirect = 0;
#ifdef USE_SASL_AUTH
if (SMTPD_STAND_ALONE(state))
vstring_free(state->defer_if_reject.reason);
if (state->expand_buf)
vstring_free(state->expand_buf);
+ if (state->session_filter)
+ myfree(state->session_filter);
+ if (state->session_redirect)
+ myfree(state->session_redirect);
#ifdef USE_SASL_AUTH
if (var_smtpd_sasl_enable)