# Exit codes from <sysexits.h>
EX_TEMPFAIL=75
EX_UNAVAILABLE=69
- STATUS=$EX_TEMPFAIL
# Clean up when done or when aborting.
- trap "rm -f in.$$; exit $STATUS" 0 1 2 3 15
-
- quit() { STATUS=${1-$?}; exit; }
+ trap "rm -f in.$$" 0 1 2 3 15
# Start processing.
- cd $INSPECT_DIR || { echo $INSPECT_DIR does not exist; quit $EX_TEMPFAIL; }
+ cd $INSPECT_DIR || { echo $INSPECT_DIR does not exist; exit $EX_TEMPFAIL; }
- cat >in.$$ || { echo Cannot save mail to file; quit $EX_TEMPFAIL; }
+ cat >in.$$ || { echo Cannot save mail to file; exit $EX_TEMPFAIL; }
- # filter <in.$$ || { echo Message content rejected; quit $EX_UNAVAILABLE; }
+ # filter <in.$$ || { echo Message content rejected; exit $EX_UNAVAILABLE; }
$SENDMAIL "$@" <in.$$
- STATUS=$?
+ exit $?
The idea is to first capture the message to file and then run the
content through run a third-party content filter program. If the
core instead if issuing an error message. This is what I
get for accepting code that I cannot test myself.
-20001220
-
- Feature: merged in Andrew McNamara's virtual delivery agent
- (a table-driven agent that does not require recipients to
- have UNIX accounts, and that implements a safe subset of
- the default local delivery agent). Files: virtual/*.
-
20001221
Code cleanup: configuration parameters that are $name
Bugfix: soft errors in client hostname lookups would be
treated as hard errors. Fix by Michael Herrmann
(informatik.tu-muenchen.de). File: smtpd/smtpd_peer.c.
+
+20010110
+
+ Bugfix: the mkdir() EEXIST race condition workaround was
+ not complete. Matthias Andree, Daniel Roesen. Files:
+ global/mail_queue.c, util/make_dirs.c.
+
+20010111
+
+ Portability: IRIX 6.5.10 defines sa_len as a macro, causing
+ a name collision with a variable used by Postfix. Roberto
+ Totaro, enigma.ethz.ch. File: smtpstone/smtp-source.c.
+
+20010116
+
+ Bugfix: REJECT by header/body_checks was flagged in smtpd
+ as a bounce, should be policy, in order to make postmaster
+ notifications more consistent. File: smtpd/smtpd.c.
+
+ Merged updated chroot setup procedure by Matthias Andree.
+ Files: examples/chroot-setup/LINUX2.
+
+20010117
+
+ Formatting: changed the seconds and days formats in the
+ "your mail is delayed" text so that it does not switch to
+ scientific notation. File: bounce/bounce_notify_util.c.
+
+20010119
+
+ Feature: SASL support for the LMTP client. Recent CYRUS
+ software requires this for Postfix over TCP sockets.
+ This was just a cloning operation.
-BEGIN WARNING
-=============
+[Based on information that was provided by Amous Gouaux]
-The information in this file is outdated. The Postfix LMTP server
-can now make connections over UNIX-domain sockets.
+Postfix LMTP support
+====================
+
+LMTP stands for Local Mail Transfer Protocol, and is detailed in
+RFC2033. This protocol is used to communicate with the final
+delivery agent, which may be on the local host or a remote host.
+
+This protocol opens up interesting possibilities: one Postfix front
+end system can drive multiple mailbox back end systems over LMTP.
+As the mail load increases you add Postfix front end systems and
+LMTP mailbox back end systems. You can use LDAP or mysql to share
+the user database among the front end and back end systems.
+
+Postfix LMTP support is based on a modified version of the Postfix
+SMTP client. The initial version was by Philip A. Prindeville of
+Mirapoint, Inc., USA. This code was modified further by Amos Gouaux
+of University of Texas at Dallas, Richardson, USA. Wietse Venema
+reduced the code to its present shape.
+
+
+Overview
+========
+
+Most of the examples in this document involve the CMU Cyrus IMAP/POP
+server, available from:
+
+ http://asg.web.cmu.edu/cyrus/
-With connections over TCP sockets, some Cyrus implementations insist
-on SASL-style authentication, which is not supported by the Postfix
-LMTP client. In that case, use UNIX-domain sockets instead.
+While certainly not the only application that could make use of LMTP,
+it tends to be the most discussed. These examples are based on the
+forthcoming Cyrus 2.0.10, at least at the time of writing. The 2.x
+branch of Cyrus places greater emphasis on LMTP delivery than the
+previous releases. Those using older releases of Cyrus can find a
+discussion in the appendix of this document.
+
+There are a variety of ways LMTP delivery can be configured in
+Postfix. The two basic flavors are delivery over UNIX-domain sockets
+and delivery over TCP sockets. Both flavors can be specified in
+either the Postfix main.cf or in a transport map. The best approach
+to use depends upon the arrangement of your servers and the desired
+level of parallelization. Please be sure to study this entire
+document as there are trade-offs in convenience and performance with
+these different approaches.
The precise syntax for UNIX-domain and TCP connection endpoints is
given in the lmtp(8) manual page.
+
+Using main.cf configuration
+===========================
+
+This is the simplest LMTP configuration. The settings
+local_transport, mailbox_transport, and fallback_transport can
+support the following connections:
+
+1. LMTP over TCP sockets.
+
+ mailbox_transport = lmtp
+
+ Instead of delivering local mail to a mail box such as
+ /var/mail/$user, a connection will be made over TCP to an LMTP
+ server. Currently the default port for this connection is 24,
+ but this can be customized in the "/etc/services" file.
+
+ NOTE:
+
+ With connections over TCP sockets, some Cyrus implementations
+ insist on SASL-style authentication, which is not currently
+ supported by the Postfix LMTP client. See the examples below
+ for additional details.
+
+
+2. LMTP over UNIX-domain sockets.
+
+ mailbox_transport = lmtp:unix:/path/name
+
+ In this case the LMTP connection will be made over a UNIX-domain
+ socket. This "/path/name" should be the socket created by the
+ LMTP server on the local machine.
+
+ NOTE 1:
+
+ If you configured Cyrus using the "--with-libwrap" option, be
+ sure to allow access to the "lmtpd" service from "0.0.0.0".
+ Otherwise LMTP deliveries over UNIX-domain sockets will be
+ blocked. See the examples below for more on using libwrap.
+
+ NOTE 2:
+
+ If you run the lmtp client chrooted, the interpretation of
+ the /path/name is relative to the Postfix queue directory
+ (typically, /var/spool/postfix).
+
+ NOTE 3:
+
+ By default, the Postfix LMTP client does not run chrooted.
+ With LMTP delivery to the local machine there is no good
+ reason to run the Postfix LMTP client chrooted.
+
+
Examples:
+1. LMTP over UNIX-domain sockets.
+
+ To utilize UNIX-domain sockets for the communication between
+ Postfix and Cyrus, the corresponding configuration files should
+ look something like this:
+
+ /etc/cyrus.conf:
+
+ SERVICES {
+ ...
+ lmtpunix cmd="lmtpd" listen="/var/imap/socket/lmtp" prefork=1
+ ...
+ }
+
+ /etc/postfix/main.cf:
+
+ mailbox_transport = lmtp:unix:/var/imap/socket/lmtp
+
+ In this case, mail that is resolved to be local will be delivered
+ to the Cyrus lmtpd server via the socket "/var/imap/socket/lmtp".
+
+ If you configured Cyrus using the "--with-libwrap" option, you
+ will need the following:
+
+ /etc/hosts.allow:
+
+ lmtpd : 0.0.0.0
+
+2. LMTP over TCP sockets.
+
+ For this example, suppose the following files are configured
+ thusly:
+
+ /etc/cyrus.conf:
+
+ SERVICES {
+ ...
+ lmtp cmd="lmtpd -a" listen="127.0.0.1:lmtp" prefork=0
+ ...
+ }
+
+XXX does this mean that connections will be accepted only on 127.0.0.1?
+
+ /etc/services:
+
+ lmtp 2003/tcp
+
+ /etc/postfix/main.cf:
+
+ mailbox_transport = lmtp
+
+ /etc/postfix/master.cf:
+
+ lmtp unix - - n - - lmtp
+
+ Mail that Postfix resolves to be local will be delivered via TCP
+ to the Cyrus LMTP server. Postfix will make a connection to port
+ 2003 on the local host, subsequently transmitting the message to
+ the lmtpd server managed by the Cyrus master process. Since
+ Postfix does not currently support LMTP-AUTH, the "-a" lmtpd
+ option is required.
+
+ CAUTION:
+
+ If you run lmtpd with the "-a" option, be certain that you
+ restrict what systems can connect to this service. This can
+ be done in either one of two ways:
+
+ a. Compile Cyrus with libwrap support, configuring
+ "/etc/hosts.allow" to restrict access to this service to
+ only your mail server.
+
+ b. In the cyrus.conf file, for the "listen" argument to the
+ "lmtp" service, specify the address (in this case
+ localhost), that the service should bind to. This can
+ also be convenient if you have a private network between
+ your Postfix server and your Cyrus server.
+
+ If neither of these actions are taken, anybody will be able
+ to drop junk into your Cyrus message store!
+
+
+3. LMTP over TCP sockets, using hosts.allow.
+
+ While similar to the previous example, this one varies in how the
+ lmtpd service is protected from unauthorized use. Instead of
+ binding the lmtpd service to a specific Internet address, access
+ will be controlled using the "/etc/hosts.allow" tcp_wrappers
+ configuration file. The tcp_wrappers package is available from:
+
+ ftp://ftp.porcupine.org/pub/security/index.html
+
+ To take advantage of tcp_wrappers, Cyrus will need to be
+ configured using the "--with-libwrap" option. See the Cyrus
+ documentation for more details.
+
+ Here are excerpts of the pertinent files:
+
+ /etc/hosts.allow:
+
+ lmtpd : localhost : ALLOW
+ lmtpd : ALL@ALL : DENY
+
+ /etc/cyrus.conf:
+
+ SERVICES {
+ ...
+ lmtp cmd="lmtpd -a" listen="lmtp" prefork=0
+ ...
+ }
+
+ /etc/services:
+
+ lmtp 2003/tcp
+
+ /etc/postfix/main.cf:
+
+ mailbox_transport = lmtp
+
+ The syntax shown in the hosts.allow excerpt above is valid if
+ tcp_wrappers is compiled using a "make" argument of:
+
+ STYLE=-DPROCESS_OPTIONS
+
+ See the tcp_wrappers hosts_options(5) man page for more details.
+
+
+Using transport map configuration
+=================================
+
+This approach is quite similar to specifying the LMTP service in the
+Postfix main.cf configuration file. However, now we will use a
+transport map to route mail to the appropriate LMTP server. Why
+might this approach be useful? This could be handy if you wish to
+route mail for multiple domains to their respective mail retrieval
+(IMAP/POP) server. Example:
+
/etc/postfix/transport:
+
domain1.name lmtp1:unix:/path/name
domain2.name lmtp2:lmtp2host
/etc/postfix/master.cf:
+
lmtp1 unix - - n - - lmtp
lmtp2 unix - - n - - lmtp
-The first example (domain1) uses UNIX-domain connections, the second
-example (domain2) uses TCP.
+ /etc/postfix/main.cf:
-For optimal use of connection caching, specify separate mail delivery
-transports for each domain that receives mail via LMTP:
+ transport_maps = hash:/etc/postfix/transport
-END WARNING
-===========
+Instead of "hash", use the map type of your choice. Some systems use
+"dbm" instead. Use "postconf -m" to find out what map types are
+supported.
-Postfix LMTP support
-====================
-Postfix LMTP support is based on a modified version of the Postfix
-SMTP client. The initial version was by Philip A. Prindeville of
-Mirapoint, Inc., USA. This code was modified further by Amos Gouaux
-of University of Texas at Dallas, Richardson, USA. Wietse Venema
-reduced the code to its present shape.
+Performance considerations
+==========================
-Postfix can be configured to talk to a local or remote LMTP server.
-Most people will run the LMTP server on the same machine that runs
-Postfix. However, a remote LMTP server can be useful if Postfix
-runs on mail relay server(s) that feed incoming mail directly to
-the appropriate mailbox server(s). This way, mailbox servers do
-not need to run an SMTP server at all. Tidy all the way around.
+Hopefully the preceding discussion has seemed pretty straight
+forward. Now things get interesting. After reading the following
+you will see that there are more factors to consider when setting up
+LMTP services.
-Configuring the mailbox server (local or remote)
-================================================
-On the mailbox server, in this case a CMU Cyrus imapd/popd server,
-add the following to /etc/services:
+Single instance message store
+=============================
- pop3 110/tcp # Cyrus POP3
- imap 143/tcp # Cyrus IMAP4
- lmtp 24/tcp
+Presently this topic is more pertinent to sites running Cyrus, but
+may be a factor with other applications as well.
-Next, put the following in /etc/inetd.conf:
+Since 1.6.22, Cyrus has had the feature that if a message containing
+multiple recipients is received via the LMTP protocol, and all these
+recipients were on the same Cyrus partition, only one instance of
+this message would be written to the file system. The other
+recipients would then see a hard link of this single instance.
+Depending on your user base, this can be considerable motivation to
+using LMTP.
- lmtp stream tcp nowait cyrus /usr/sbin/tcpd /usr/local/cyrus/bin/deliver -e -l
+However, there is a catch: currently the Postfix local delivery
+mechanisms are only designed to handle one recipient at a time, which
+in most cases is more than adequate. So, if you wish to support
+single instance message store delivery, you will have to use a
+transport table to map these users to the appropriate LMTP
+destination.
-/usr/sbin/tcpd is from the tcp_wrappers package. You want this to
-make sure only your mail relay(s) can talk to the LMTP server.
-Postfix by default does multiple deliveries per LMTP session
-(connection caching), so do not worry about the overhead of
-tcp_wrapping the LMTP port.
+While the simplest thing to do would be to list the entire domain in
+the transport map for LMTP delivery, this by-passes alias expansion
+for otherwise local addresses. If the site is to run software via
+aliases, like most Mailing List Management (MLM) software, a more
+complex solution is required. Fortunately, a virtual table should do
+the trick.
-On some systems, tcpd is built into inetd, so you do not have to
-specify tcpd in the inetd.conf file. Instead of tcpd/inetd, xinetd
-can do a similar job of logging and access control.
+As an example, suppose we wanted to support single instance message
+store delivery for the domain "example.org". The configuration files
+for this domain could look something like this:
+
+ /etc/postfix/virtual:
+
+ mlist@example.org mlist@localhost
+
+ /etc/postfix/transport:
+
+ example.org lmtp:unix:/var/imap/socket/lmtp
-Configuring Postfix
-===================
+ /etc/postfix/aliases:
-Similar changes to /etc/services:
+ mlist: "|/path/to/mlm/software"
- lmtp 24/tcp
+ /etc/postfix/master.cf:
-You may have to add the following entry to /etc/postfix/master.cf:
+ lmtp unix - - n - - lmtp
- lmtp unix - - n - - lmtp
+ /etc/postfix/main.cf:
-NOTE: Root privileges are not necessary!
+ mydestination = localhost, $myhostname, $mydomain
+ virtual_maps = hash:/etc/postfix/virtual
+ transport_maps = hash:/etc/postfix/transport
+ alias_maps = hash:/etc/postfix/aliases
+ alias_database = hash:/etc/postfix/aliases
-Put this in /etc/postfix/transport:
+ /etc/cyrus.conf:
- inbox.domain.org lmtp:inbox.domain.org
+ SERVICES {
+ ...
+ lmtpunix cmd="lmtpd" listen="/var/imap/socket/lmtp" prefork=1
+ ...
+ }
-Naturally, this means we also need in /etc/postfix/main.cf:
+Breaking things down, we begin with the address "mlist@example.org",
+which represents a mailing list. By placing an entry in the virtual
+map to direct this mail to "mlist@localhost", we can override the
+transport map that would by default route all "@example.org" mail to
+a LMTP server via a UNIX-domain socket.
- transport_maps = hash:/etc/postfix/transport
+To summarize, all mail that is to be processed by an alias entry must
+first be diverted with a virtual table entry so that it does not fall
+into the more general routing established by the transport table.
-Instead of "hash", use the map type of your choice. Some systems
-use "dbm" instead. Use "postconf -m" to find out what map types
-are supported.
Improving connection caching performance
========================================
configuring a separate mail delivery transport for each LMTP server:
/etc/postfix/master.cf:
+
lmtp1 unix - - n - - lmtp
lmtp2 unix - - n - - lmtp
. . . . . . . .
mail lmtp2 transport for the LMTP server #2, and so on.
/etc/postfix/transport:
+
foo.com lmtp1:lmtp1host
bar.com lmtp2:lmtp2host
+
+
+Appendix: Older Cyrus versions
+==============================
+
+First of all, if you are using a Cyrus 2.x version prior to 2.0.10,
+it would be good to upgrade. The previous 2.x releases were beta
+releases, and numerous bug fixes and enhancements have been
+incorporated into the 2.0.10 release.
+
+Further back, 1.6.24 was the last pre-2.x production release.
+(Actually, there was a 1.6.25-BETA, but it is uncertain whether this
+will be released officially as CMU is now focusing support on the 2.x
+branch.) The following discussion touches on how to configure the
+Postfix LMTP facilities with Cyrus 1.6.24.
+
+One of the significant differences between Cyrus 1.x and 2.x is the
+inclusion of the "master" process in 2.x. This "master" process is
+responsible for running the various components of Cyrus, such as
+imapd, pop3d, and lmtpd. Prior to 2.x, these services were managed
+by inetd, the Internet services daemon.
+
+To utilize LMTP delivery with Cyrus 1.6.24, the first thing to do is
+configure inetd. This involves the following file edits:
+
+ /etc/services:
+
+ lmtp 2003/tcp
+
+ /etc/inetd.conf:
+
+ lmtp stream tcp nowait cyrus /usr/sbin/tcpd /usr/cyrus/bin/deliver -e -l
+
+ /etc/hosts.allow:
+
+ deliver : localhost : ALLOW
+ deliver : ALL@ALL : DENY
+
+The "/usr/sbin/tcpd" is from the tcp_wrappers package, which is
+discussed in the example "LMTP over TCP sockets, using hosts.allow."
+It is important that you wrap this LMTP port to protect it from
+unauthorized access.
+
+On some systems, tcpd is built into inetd, so you do not have to
+specify tcpd in the inetd.conf file. Instead of tcpd/inetd, xinetd
+can do a similar job of logging and access control.
+
+Now comes the Postfix configuration. Basically, the Cyrus 2.x
+discussions regarding LMTP delivery over TCP are also applicable to
+Cyrus 1.x, with the exception of the "/etc/cyrus.conf" file. A
+typical Postfix configuration might look like this:
+
+ /etc/postfix/master.cf:
+
+ lmtp unix - - n - - lmtp
+
+ /etc/postfix/main.cf:
+
+ mailbox_transport = lmtp
+
+It is also possible to use the transport map to route mail to your
+Cyrus 1.6.24 LMTP server:
+
+ /etc/postfix/transport:
+
+ domain1.name lmtp1:lmtp1host
+ domain2.name lmtp2:lmtp2host
+
+ /etc/postfix/master.cf:
+
+ lmtp1 unix - - n - - lmtp
+ lmtp2 unix - - n - - lmtp
+
+ /etc/postfix/main.cf:
+
+ transport_maps = hash:/etc/postfix/transport
+
+If you have read the discussion covering the Cyrus 2.x installation,
+you will notice the one significant difference with the Postfix
+configuration is the lack of mention of the UNIX-domain sockets.
+That is because delivery over UNIX-domain sockets is new with Cyrus
+2.x, yet another reason to upgrade. :-)
+
+
+
+# Local Variables:
+# mode: text
+# mode: flyspell
+# fill-column: 69
+# End:
+
+
+
--- /dev/null
+BEGIN WARNING
+=============
+
+The information in this file is outdated. The Postfix LMTP server
+can now make connections over UNIX-domain sockets.
+
+With connections over TCP sockets, some Cyrus implementations insist
+on SASL-style authentication, which is not supported by the Postfix
+LMTP client. In that case, use UNIX-domain sockets instead.
+
+The precise syntax for UNIX-domain and TCP connection endpoints is
+given in the lmtp(8) manual page.
+
+Examples:
+
+ /etc/postfix/transport:
+ domain1.name lmtp1:unix:/path/name
+ domain2.name lmtp2:lmtp2host
+
+ /etc/postfix/master.cf:
+ lmtp1 unix - - n - - lmtp
+ lmtp2 unix - - n - - lmtp
+
+The first example (domain1) uses UNIX-domain connections, the second
+example (domain2) uses TCP.
+
+For optimal use of connection caching, specify separate mail delivery
+transports for each domain that receives mail via LMTP:
+
+END WARNING
+===========
+
+Postfix LMTP support
+====================
+
+Postfix LMTP support is based on a modified version of the Postfix
+SMTP client. The initial version was by Philip A. Prindeville of
+Mirapoint, Inc., USA. This code was modified further by Amos Gouaux
+of University of Texas at Dallas, Richardson, USA. Wietse Venema
+reduced the code to its present shape.
+
+Postfix can be configured to talk to a local or remote LMTP server.
+Most people will run the LMTP server on the same machine that runs
+Postfix. However, a remote LMTP server can be useful if Postfix
+runs on mail relay server(s) that feed incoming mail directly to
+the appropriate mailbox server(s). This way, mailbox servers do
+not need to run an SMTP server at all. Tidy all the way around.
+
+Configuring the mailbox server (local or remote)
+================================================
+
+On the mailbox server, in this case a CMU Cyrus imapd/popd server,
+add the following to /etc/services:
+
+ pop3 110/tcp # Cyrus POP3
+ imap 143/tcp # Cyrus IMAP4
+ lmtp 24/tcp
+
+Next, put the following in /etc/inetd.conf:
+
+ lmtp stream tcp nowait cyrus /usr/sbin/tcpd /usr/local/cyrus/bin/deliver -e -l
+
+/usr/sbin/tcpd is from the tcp_wrappers package. You want this to
+make sure only your mail relay(s) can talk to the LMTP server.
+Postfix by default does multiple deliveries per LMTP session
+(connection caching), so do not worry about the overhead of
+tcp_wrapping the LMTP port.
+
+On some systems, tcpd is built into inetd, so you do not have to
+specify tcpd in the inetd.conf file. Instead of tcpd/inetd, xinetd
+can do a similar job of logging and access control.
+
+Configuring Postfix
+===================
+
+Similar changes to /etc/services:
+
+ lmtp 24/tcp
+
+You may have to add the following entry to /etc/postfix/master.cf:
+
+ lmtp unix - - n - - lmtp
+
+NOTE: Root privileges are not necessary!
+
+Put this in /etc/postfix/transport:
+
+ inbox.domain.org lmtp:inbox.domain.org
+
+Naturally, this means we also need in /etc/postfix/main.cf:
+
+ transport_maps = hash:/etc/postfix/transport
+
+Instead of "hash", use the map type of your choice. Some systems
+use "dbm" instead. Use "postconf -m" to find out what map types
+are supported.
+
+Improving connection caching performance
+========================================
+
+After delivering a message via LMTP, Postfix will keep the connection
+open for a while, so that it can be reused for a subsequent delivery.
+This reduces overhead of LMTP servers that create one process per
+connection.
+
+For LMTP connection caching to work, the Postfix LMTP client should
+not switch destination hosts. This is no problem when you run only
+one LMTP server. However, if you run multiple LMTP servers, this
+can be an issue.
+
+You can prevent the LMTP client from switching between servers by
+configuring a separate mail delivery transport for each LMTP server:
+
+ /etc/postfix/master.cf:
+ lmtp1 unix - - n - - lmtp
+ lmtp2 unix - - n - - lmtp
+ . . . . . . . .
+
+Configure transport table entries such that the lmtp1 mail delivery
+transport is used for all deliveries to the LMTP server #1, the
+mail lmtp2 transport for the LMTP server #2, and so on.
+
+ /etc/postfix/transport:
+ foo.com lmtp1:lmtp1host
+ bar.com lmtp2:lmtp2host
src/lmtp src/trivial-rewrite src/qmgr src/smtp src/bounce src/pipe \
src/showq src/postalias src/postcat src/postconf src/postdrop \
src/postkick src/postlock src/postlog src/postmap src/postsuper \
- src/nqmgr src/spawn src/flush src/virtual # src/base64 proto man html
+ src/spawn src/flush # proto man html
default: update
+REJECT by header/body_checks are now flagged as policy violations
+rather than bounces, for consistency in postmaster notifications.
+
Major changes with snapshot-20001217
====================================
WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
===============================================================
-Do not use this code. The Postfix SASL support is based on the
-Cyrus SASL library, which has not enough documentation about how
-the software is supposed to work. It is not clear if the code is
-safe enough for security-critical applications.
+This code is not blessed by Wietse.
+
+People who go to the trouble of installing Postfix may have the
+expectation that Postfix is more secure than some other mailers.
+
+With SASL authentication enabled in the Postfix SMTP client and
+SMTP server, Postfix becomes no more secure than other mail systems
+that use the Cyrus SASL library.
+
+The Cyrus SASL library has too little documentation about how the
+software is supposed to work; and it is too much code to be used
+in a security-sensitive program such as an SMTP client or server.
+
+However, you are pretty much required to build with SASL support
+if you are going to use the LMTP interface of the Cyrus delivery
+agent. This interface is much faster than forking a new process
+for every message delivery.
Postfix+SASL 1.5.5 appears to work on RedHat 6.1 (pwcheck_method
set to shadow or sasldb), Solaris 2.7 (pwcheck_method set to shadow
SASL. Note also that the Cyrus SASL documentation says that it is
pointless to enable that if you use "sasldb" for "pwcheck_method".
-SASL is a lot of complex code. In a future version the Postfix SASL
-code is likely to be put outside the SMTP server.
-
Introduction
============
non-standard SASL LOGIN authentication method. To enable this
authentication method, specify ``./configure --enable-login''.
+Reportedly, older Microsoft software mis-implements the AUTH
+protocol, and requires that the server replies to EHLO with
+"250-AUTH=stuff..." instead of "250-AUTH stuff...". To accomodate
+such clients, set "allow_broken_auth_clients = yes" in the main.cf
+file.
+
Building Postfix with SASL authentication support
=================================================
--- /dev/null
+# DO NOT EDIT THIS FILE. EDIT THE MAIN.CF FILE INSTEAD. THE STUFF
+# HERE JUST SERVES AS AN EXAMPLE.
+#
+# This file contains example settings of Postfix configuration
+# parameters that control compatibility with broken software.
+
+# The ignore_mx_lookup_error parameter controls what happens when a
+# name server fails to respond to an MX lookup request. By default,
+# Postfix defers delivery and tries again after some delay. Specify
+# "ignore_mx_lookup_error = yes" to force an A record lookup instead.
+#
+ignore_mx_lookup_error = no
+
+# The smtp_always_send_ehlo parameter specifies that the SMTP client
+# should always send EHLO at the start of an SMTP session.
+#
+# By default, Postfix sends EHLO only when the word "ESMTP" appears
+# in the server greeting banner (example: 220 spike.porcupine.org
+# ESMTP Postfix).
+#
+smtp_always_send_ehlo = no
+
+# The smtp_never_send_ehlo parameter specifies that the SMTP client
+# should never send EHLO at the start of an SMTP session.
+#
+# By default, Postfix sends EHLO whenever the word "ESMTP" appears
+# in the server greeting banner (example: 220 spike.porcupine.org
+# ESMTP Postfix).
+#
+smtp_never_send_ehlo = no
+
+# The smtp_skip_4xx_greeting parameter controls what happens when
+# an SMTP server greets us with a 4XX status code (go away, try
+# again later).
+#
+# By default, Postfix moves on the the next mail exchanger. Specify
+# "smtp_skip_4xx_greeting = no" if Postfix should defer delivery
+# immediately.
+#
+smtp_skip_4xx_greeting = yes
+
+# The smtp_skip_5xx_greeting parameter controls what happens when
+# an SMTP server greets us with a 5XX status code (go away, do not
+# try again later).
+#
+# By default, Postfix moves on the the next mail exchanger. Specify
+# "smtp_skip_5xx_greeting = no" if Postfix should bounce the mail
+# immediately.
+#
+smtp_skip_5xx_greeting = yes
+
+# The smtp_skip_quit_response parameter controls whether the SMTP
+# client waits for the response to the QUIT command. The default is
+# to not wait.
+#
+smtp_skip_quit_response = yes
+
+# The strict_rfc821_envelopes configuration parameter controls whether
+# the Postfix SMTP server requires that MAIL FROM and RCPT TO addresses
+# are specified within <>, and that MAIL FROM and RCPT TO addresses
+# do not contain RFC822-style comments or phrases. It's great to
+# stop SPAM mailers. But it also trips up broken peecee clients.
+#
+# By default, Postfix SMTPD allows RFC822 syntax in MAIL FROM and RCPT TO.
+#
+strict_rfc821_envelopes = no
#smtp_bind_address=111.222.333.444
# The smtp_skip_4xx_greeting parameter controls what happens when
-# an SMTP server greets us with a 4XX status code. By default, Postfix
-# backs off. Specify "smtp_skip_4xx_greeting = yes" to move on the
-# the next mail exchanger.
-#
-smtp_skip_4xx_greeting = no
+# an SMTP server greets us with a 4XX status code (go away, try
+# again later).
+#
+# By default, Postfix moves on the the next mail exchanger. Specify
+# "smtp_skip_4xx_greeting = no" if Postfix should defer delivery
+# immediately.
+#
+smtp_skip_4xx_greeting = yes
+
+# The smtp_skip_5xx_greeting parameter controls what happens when
+# an SMTP server greets us with a 5XX status code (go away, do not
+# try again later).
+#
+# By default, Postfix moves on the the next mail exchanger. Specify
+# "smtp_skip_5xx_greeting = no" if Postfix should bounce the mail
+# immediately.
+#
+smtp_skip_5xx_greeting = yes
# The smtp_skip_quit_response parameter controls whether the SMTP
# client waits for the response to the QUIT command. The default is
#
smtpd_timeout = 300s
+# The strict_rfc821_envelopes configuration parameter controls whether
+# the Postfix SMTP server requires that MAIL FROM and RCPT TO addresses
+# are specified within <>, and that MAIL FROM and RCPT TO addresses
+# do not contain RFC822-style comments or phrases. It's great to
+# stop SPAM mailers. But it also trips up broken peecee clients.
+#
+# By default, Postfix SMTPD allows RFC822 syntax in MAIL FROM and RCPT TO.
+#
+strict_rfc821_envelopes = no
+
#
# TARPIT CONTROLS
#
+++ /dev/null
-# DO NOT EDIT THIS FILE. EDIT THE MAIN.CF FILE INSTEAD. THE STUFF
-# HERE JUST SERVES AS AN EXAMPLE.
-#
-# This file contains example settings of Postfix configuration
-# parameters that control virtual database lookups.
-
-# The virtual_maps parameter specifies optional lookup tables to
-# redirect specific addresses or even complete domains to another
-# address. This is typically used to implement virtual domain support.
-#
-# By default, no address redirection is done.
-#
-# If you use this feature, run "postmap /etc/postfix/virtual" to
-# build the necessary DBM or DB file after change.
-#
-# It may take a minute or so before the change becomes visible.
-# Use "postfix reload" to eliminate the delay.
-#
-# virtual_maps = dbm:/etc/postfix/virtual
-# virtual_maps = hash:/etc/postfix/virtual
-# virtual_maps = hash:/etc/postfix/virtual, nis:virtual
-# virtual_maps = hash:/etc/postfix/virtual, netinfo:/virtual
-virtual_maps =
#! /bin/sh
# LINUX2 - shell script to set up a Postfix chroot jail for Linux
-# Tested on SuSE Linux 5.3 (libc5) and 6.4 (glibc2.1)
+# Tested on SuSE Linux 5.3 (libc5) and 7.0 (glibc2.1)
-# Copyright (c) 2000 by Matthias Andree
+# Other testers reported as working:
+#
+# 2001-01-15 Debian sid (unstable)
+# Christian Kurz <shorty@getuid.de>
+
+# Copyright (c) 2000 - 2001 by Matthias Andree
# Redistributable unter the MIT-style license that follows:
# Abstract: "do whatever you want except hold somebody liable or change
# the copyright information".
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.
+# 2000-09-29
+# v0.1: initial release
+
+# 2000-12-05
+# v0.2: copy libdb.* for libnss_db.so
+# remove /etc/localtime in case it's a broken symlink
+# restrict find to maxdepth 1 (faster)
+
+# $Log: LINUX2,v $
+# Revision 1.4 2001/01/15 09:36:35 emma
+# add note it was successfully tested on Debian sid
+#
+
+CP="cp -p"
+
cond_copy() {
# find files as per pattern in $1
# if any, copy to directory $2
dir=`dirname "$1"`
pat=`basename "$1"`
- lr=`find "$dir" -name "$pat"`
+ lr=`find "$dir" -maxdepth 1 -name "$pat"`
if test ! -d "$2" ; then exit 1 ; fi
- if test "x$lr" != "x" ; then cp -p $1 "$2" ; fi
+ if test "x$lr" != "x" ; then $CP $1 "$2" ; fi
}
set -e
# find localtime (SuSE 5.3 does not have /etc/localtime)
lt=/etc/localtime
if test ! -f $lt ; then lt=/usr/lib/zoneinfo/localtime ; fi
+if test ! -f $lt ; then lt=/usr/share/zoneinfo/localtime ; fi
if test ! -f $lt ; then echo "cannot find localtime" ; exit 1 ; fi
-cp -p -f $lt /etc/services /etc/resolv.conf /etc/nsswitch.conf etc
-cp -p -f /etc/host.conf /etc/hosts /etc/passwd etc
+rm -f etc/localtime
+
+# copy localtime and some other system files into the chroot's etc
+$CP -f $lt /etc/services /etc/resolv.conf /etc/nsswitch.conf etc
+$CP -f /etc/host.conf /etc/hosts /etc/passwd etc
ln -s -f /etc/localtime usr/lib/zoneinfo
-cond_copy '/lib/libnss_*' lib
-cond_copy '/lib/libresolv*' lib
\ No newline at end of file
+# copy required libraries into the chroot
+cond_copy '/lib/libnss_*.so*' lib
+cond_copy '/lib/libresolv.so*' lib
+cond_copy '/lib/libdb.so*' lib
+
+postfix reload
DAEMONS = bounce.8.html cleanup.8.html defer.8.html error.8.html local.8.html \
lmtp.8.html master.8.html pickup.8.html pipe.8.html qmgr.8.html \
showq.8.html smtp.8.html smtpd.8.html trivial-rewrite.8.html \
- nqmgr.8.html spawn.8.html flush.8.html virtual.8.html
+ spawn.8.html flush.8.html
COMMANDS= mailq.1.html newaliases.1.html postalias.1.html postcat.1.html \
postconf.1.html postfix.1.html postkick.1.html postlock.1.html \
postlog.1.html postdrop.1.html postmap.1.html sendmail.1.html \
master.8.html: ../src/master/master.c
srctoman $? | nroff -man | man2html | postlink >$@
-nqmgr.8.html: ../src/nqmgr/qmgr.c
- srctoman $? | sed -e 's/qmgr[^_]/n&/' \
- -e 's/qmgr$$/n&/' \
- -e 's/QMGR[^_]/N&/' | \
- nroff -man | man2html | postlink >$@
-
pickup.8.html: ../src/pickup/pickup.c
srctoman $? | nroff -man | man2html | postlink >$@
trivial-rewrite.8.html: ../src/trivial-rewrite/trivial-rewrite.c
srctoman $? | nroff -man | man2html | postlink >$@
-virtual.8.html: ../src/virtual/virtual.c
- srctoman $? | nroff -man | man2html | postlink >$@
-
postalias.1.html: ../src/postalias/postalias.c
srctoman $? | nroff -man | man2html | postlink >$@
+++ /dev/null
-<html> <head> </head> <body> <pre>
-
-
-
-NQMGR(8) NQMGR(8)
-
-
-<b>NAME</b>
- nqmgr - Postfix queue manager
-
-<b>SYNOPSIS</b>
- <b>nqmgr</b> [generic Postfix daemon options]
-
-<b>DESCRIPTION</b>
- The <b>nqmgr</b> daemon awaits the arrival of incoming mail and
- arranges for its delivery via Postfix delivery processes.
- The actual mail routing strategy is delegated to the <a href="trivial-rewrite.8.html"><b>triv-</b>
- <b>ial-rewrite</b>(8)</a> daemon. This program expects to be run
- from the <a href="master.8.html"><b>master</b>(8)</a> process manager.
-
- Mail addressed to the local <b>double-bounce</b> address is
- silently discarded. This stops potential loops caused by
- undeliverable bounce notifications.
-
- Mail addressed to a user listed in the optional <b>relocated</b>
- database is bounced with a "user has moved to <i>new_loca-</i>
- <i>tion</i>" message. See <a href="relocated.5.html"><b>relocated</b>(5)</a> for a precise description.
-
-<b>MAIL</b> <b>QUEUES</b>
- The <b>nqmgr</b> daemon maintains the following queues:
-
- <b>incoming</b>
- Inbound mail from the network, or mail picked up by
- the local <b>pickup</b> agent from the <b>maildrop</b> directory.
-
- <b>active</b> Messages that the queue manager has opened for
- delivery. Only a limited number of messages is
- allowed to enter the <b>active</b> queue (leaky bucket
- strategy, for a fixed delivery rate).
-
- <b>deferred</b>
- Mail that could not be delivered upon the first
- attempt. The queue manager implements exponential
- backoff by doubling the time between delivery
- attempts.
-
- <b>corrupt</b>
- Unreadable or damaged queue files are moved here
- for inspection.
-
-<b>DELIVERY</b> <b>STATUS</b> <b>REPORTS</b>
- The <b>nqmgr</b> daemon keeps an eye on per-message delivery sta-
- tus reports in the following directories. Each status
- report file has the same name as the corresponding message
- file:
-
- <b>bounce</b> Per-recipient status information about why mail is
- bounced. These files are maintained by the
- <a href="bounce.8.html"><b>bounce</b>(8)</a> daemon.
-
- <b>defer</b> Per-recipient status information about why mail is
-
-
-
- 1
-
-
-
-
-
-NQMGR(8) NQMGR(8)
-
-
- delayed. These files are maintained by the
- <a href="defer.8.html"><b>defer</b>(8)</a> daemon.
-
- The <b>nqmgr</b> daemon is responsible for asking the <a href="bounce.8.html"><b>bounce</b>(8)</a>
- or <a href="defer.8.html"><b>defer</b>(8)</a> daemons to send non-delivery reports.
-
-<b>STRATEGIES</b>
- The queue manager implements a variety of strategies for
- either opening queue files (input) or for message delivery
- (output).
-
- <b>leaky</b> <b>bucket</b>
- This strategy limits the number of messages in the
- <b>active</b> queue and prevents the queue manager from
- running out of memory under heavy load.
-
- <b>fairness</b>
- When the <b>active</b> queue has room, the queue manager
- takes one message from the <b>incoming</b> queue and one
- from the <b>deferred</b> queue. This prevents a large mail
- backlog from blocking the delivery of new mail.
-
- <b>slow</b> <b>start</b>
- This strategy eliminates "thundering herd" problems
- by slowly adjusting the number of parallel deliver-
- ies to the same destination.
-
- <b>round</b> <b>robin</b>
- The queue manager sorts delivery requests by desti-
- nation. Round-robin selection prevents one desti-
- nation from dominating deliveries to other destina-
- tions.
-
- <b>exponential</b> <b>backoff</b>
- Mail that cannot be delivered upon the first
- attempt is deferred. The time interval between
- delivery attempts is doubled after each attempt.
-
- <b>destination</b> <b>status</b> <b>cache</b>
- The queue manager avoids unnecessary delivery
- attempts by maintaining a short-term, in-memory
- list of unreachable destinations.
-
- <b>preemptive</b> <b>message</b> <b>scheduling</b>
- The queue manager attempts to minimize the average
- per-recipient delay while still preserving the cor-
- rect per-message delays, using a sophisticated pre-
- emptive message scheduling.
-
-<b>TRIGGERS</b>
- On an idle system, the queue manager waits for the arrival
- of trigger events, or it waits for a timer to go off. A
- trigger is a one-byte message. Depending on the message
- received, the queue manager performs one of the following
-
-
-
- 2
-
-
-
-
-
-NQMGR(8) NQMGR(8)
-
-
- actions (the message is followed by the symbolic constant
- used internally by the software):
-
- <b>D</b> <b>(QMGR</b><i>_</i><b>REQ</b><i>_</i><b>SCAN</b><i>_</i><b>DEFERRED)</b>
- Start a deferred queue scan. If a deferred queue
- scan is already in progress, that scan will be
- restarted as soon as it finishes.
-
- <b>I</b> <b>(QMGR</b><i>_</i><b>REQ</b><i>_</i><b>SCAN</b><i>_</i><b>INCOMING)</b>
- Start an incoming queue scan. If an incoming queue
- scan is already in progress, that scan will be
- restarted as soon as it finishes.
-
- <b>A</b> <b>(QMGR</b><i>_</i><b>REQ</b><i>_</i><b>SCAN</b><i>_</i><b>ALL)</b>
- Ignore deferred queue file time stamps. The request
- affects the next deferred queue scan.
-
- <b>F</b> <b>(QMGR</b><i>_</i><b>REQ</b><i>_</i><b>FLUSH</b><i>_</i><b>DEAD)</b>
- Purge all information about dead transports and
- destinations.
-
- <b>W</b> <b>(TRIGGER</b><i>_</i><b>REQ</b><i>_</i><b>WAKEUP)</b>
- Wakeup call, This is used by the master server to
- instantiate servers that should not go away for-
- ever. The action is to start an incoming queue
- scan.
-
- The <b>nqmgr</b> daemon reads an entire buffer worth of triggers.
- Multiple identical trigger requests are collapsed into
- one, and trigger requests are sorted so that <b>A</b> and <b>F</b> pre-
- cede <b>D</b> and <b>I</b>. Thus, in order to force a deferred queue
- run, one would request <b>A</b> <b>F</b> <b>D</b>; in order to notify the queue
- manager of the arrival of new mail one would request <b>I</b>.
-
-<b>STANDARDS</b>
- None. The <b>nqmgr</b> daemon does not interact with the outside
- world.
-
-<b>SECURITY</b>
- The <b>nqmgr</b> daemon is not security sensitive. It reads sin-
- gle-character messages from untrusted local users, and
- thus may be susceptible to denial of service attacks. The
- <b>nqmgr</b> daemon does not talk to the outside world, and it
- can be run at fixed low privilege in a chrooted environ-
- ment.
-
-<b>DIAGNOSTICS</b>
- Problems and transactions are logged to the syslog daemon.
- Corrupted message files are saved to the <b>corrupt</b> queue for
- further inspection.
-
- Depending on the setting of the <b>notify</b><i>_</i><b>classes</b> parameter,
- the postmaster is notified of bounces and of other trou-
- ble.
-
-
-
- 3
-
-
-
-
-
-NQMGR(8) NQMGR(8)
-
-
-<b>BUGS</b>
- A single queue manager process has to compete for disk
- access with multiple front-end processes such as <b>smtpd</b>. A
- sudden burst of inbound mail can negatively impact out-
- bound delivery rates.
-
-<b>CONFIGURATION</b> <b>PARAMETERS</b>
- The following <b>main.cf</b> parameters are especially relevant
- to this program. See the Postfix <b>main.cf</b> file for syntax
- details and for default values. Use the <b>postfix</b> <b>reload</b>
- command after a configuration change.
-
-<b>Miscellaneous</b>
- <b>allow</b><i>_</i><b>min</b><i>_</i><b>user</b>
- Do not bounce recipient addresses that begin with
- '-'.
-
- <b>relocated</b><i>_</i><b>maps</b>
- Tables with contact information for users, hosts or
- domains that no longer exist. See <a href="relocated.5.html"><b>relocated</b>(5)</a>.
-
- <b>queue</b><i>_</i><b>directory</b>
- Top-level directory of the Postfix queue.
-
-<b>Active</b> <b>queue</b> <b>controls</b>
- In the text below, <i>transport</i> is the first field in a <b>mas-</b>
- <b>ter.cf</b> entry.
-
- <b>qmgr</b><i>_</i><b>message</b><i>_</i><b>active</b><i>_</i><b>limit</b>
- Limit the number of messages in the active queue.
-
- <b>qmgr</b><i>_</i><b>message</b><i>_</i><b>recipient</b><i>_</i><b>limit</b>
- Limit the number of in-memory recipients.
-
- This parameter also limits the size of the short-
- term, in-memory destination cache.
-
- <b>qmgr</b><i>_</i><b>message</b><i>_</i><b>recipient</b><i>_</i><b>minimum</b>
- Per message minimum of in-memory recipients.
-
- <b>default</b><i>_</i><b>recipient</b><i>_</i><b>limit</b>
- Default limit on the number of in-memory recipients
- per transport.
-
- <i>transport_</i><b>recipient</b><i>_</i><b>limit</b>
- Limit on the number of in-memory recipients, for
- the named message <i>transport</i>.
-
- <b>default</b><i>_</i><b>extra</b><i>_</i><b>recipient</b><i>_</i><b>limit</b>
- Default limit on the total number of per transport
- in-memory recipients that the preempting messages
- can have.
-
-
-
-
-
- 4
-
-
-
-
-
-NQMGR(8) NQMGR(8)
-
-
- <i>transport_</i><b>extra</b><i>_</i><b>recipient</b><i>_</i><b>limit</b>
- Limit on the number of in-memory recipients which
- all preempting messages delivered by the transport
- <i>transport</i> can have.
-
-<b>Timing</b> <b>controls</b>
- <b>min</b><i>_</i><b>backoff</b>
- Minimal time in seconds between delivery attempts
- of a deferred message.
-
- This parameter also limits the time an unreachable
- destination is kept in the short-term, in-memory
- destination status cache.
-
- <b>max</b><i>_</i><b>backoff</b>
- Maximal time in seconds between delivery attempts
- of a deferred message.
-
- <b>maximal</b><i>_</i><b>queue</b><i>_</i><b>lifetime</b>
- Maximal time in days a message is queued before it
- is sent back as undeliverable.
-
- <b>queue</b><i>_</i><b>run</b><i>_</i><b>delay</b>
- Time in seconds between deferred queue scans. Queue
- scans do not overlap.
-
- <b>transport</b><i>_</i><b>retry</b><i>_</i><b>time</b>
- Time in seconds between attempts to contact a bro-
- ken delivery transport.
-
-<b>Concurrency</b> <b>controls</b>
- <b>initial</b><i>_</i><b>destination</b><i>_</i><b>concurrency</b>
- Initial per-destination concurrency level for par-
- allel delivery to the same destination.
-
- <b>default</b><i>_</i><b>destination</b><i>_</i><b>concurrency</b><i>_</i><b>limit</b>
- Default limit on the number of parallel deliveries
- to the same destination.
-
- <i>transport_</i><b>destination</b><i>_</i><b>concurrency</b><i>_</i><b>limit</b>
- Limit on the number of parallel deliveries to the
- same destination, for delivery via the named mes-
- sage <i>transport</i>.
-
-<b>Recipient</b> <b>controls</b>
- <b>default</b><i>_</i><b>destination</b><i>_</i><b>recipient</b><i>_</i><b>limit</b>
- Default limit on the number of recipients per mes-
- sage transfer.
-
- <i>transport_</i><b>destination</b><i>_</i><b>recipient</b><i>_</i><b>limit</b>
- Limit on the number of recipients per message
- transfer, for the named message <i>transport</i>.
-
-
-
-
-
- 5
-
-
-
-
-
-NQMGR(8) NQMGR(8)
-
-
-<b>Message</b> <b>scheduling</b>
- <i>transport_</i><b>delivery</b><i>_</i><b>slot</b><i>_</i><b>cost</b> (valid range: 0,2,3...)
- This parameter basically controls how often a mes-
- sage delivered by <i>transport</i> can be preempted by
- another message. An internal per-message/transport
- counter is incremented by one for each <i>trans-</i>
- <i>port_</i><b>delivery</b><i>_</i><b>slot</b><i>_</i><b>cost</b> deliveries handled by
- <i>transport</i>. This counter represents the number of
- "available delivery slots" for use by other mes-
- sages. Current message can be preempted by another
- message when that other message can be delivered
- using less <i>transport</i> agents than the value of the
- "available delivery slots" counter.
-
- Value equal to 0 disables the message preemption
- for <i>transport</i>.
-
- <i>transport_</i><b>minimum</b><i>_</i><b>delivery</b><i>_</i><b>slots</b>
- Message preemption is not attempted at all whenever
- a message that can't ever accumulate at least
- <i>transport_</i><b>minimum</b><i>_</i><b>delivery</b><i>_</i><b>slots</b> available delivery
- slots is being delivered by <i>transport</i>.
-
- <i>transport_</i><b>delivery</b><i>_</i><b>slot</b><i>_</i><b>discount</b> (valid range: 0..100)
-
- <i>transport_</i><b>delivery</b><i>_</i><b>slot</b><i>_</i><b>loan</b>
- These parameters speed up the moment when a message
- preemption can happen. Instead of waiting until
- the full amount of delivery slots required is
- available, the preemption can happen when <i>trans-</i>
- <i>port_</i><b>delivery</b><i>_</i><b>slot</b><i>_</i><b>discount</b> percent of the required
- amount plus <i>transport_</i><b>delivery</b><i>_</i><b>slot</b><i>_</i><b>loan</b> still
- remains to be accumulated. Note that the full
- amount will still have to be accumulated before
- another preemption can take place later.
-
- <b>default</b><i>_</i><b>delivery</b><i>_</i><b>slot</b><i>_</i><b>cost</b>
-
- <b>default</b><i>_</i><b>minimum</b><i>_</i><b>delivery</b><i>_</i><b>slots</b>
-
- <b>default</b><i>_</i><b>delivery</b><i>_</i><b>slot</b><i>_</i><b>discount</b>
-
- <b>default</b><i>_</i><b>delivery</b><i>_</i><b>slot</b><i>_</i><b>loan</b>
- Default values for the transport specific parame-
- ters described above.
-
-<b>SEE</b> <b>ALSO</b>
- <a href="master.8.html">master(8)</a>, process manager
- <a href="relocated.5.html">relocated(5)</a>, format of the "user has moved" table
- syslogd(8) system logging
- <a href="trivial-rewrite.8.html">trivial-rewrite(8)</a>, address routing
-
-<b>LICENSE</b>
- The Secure Mailer license must be distributed with this
-
-
-
- 6
-
-
-
-
-
-NQMGR(8) NQMGR(8)
-
-
- software.
-
-<b>AUTHOR(S)</b>
- Wietse Venema
- IBM T.J. Watson Research
- P.O. Box 704
- Yorktown Heights, NY 10598, USA
-
- Scheduler enhancements:
- Patrik Rak
- Modra 6
- 155 00, Prague, Czech Republic
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 7
-
-
-</pre> </body> </html>
For example, allow <a href="http://www.faqs.org/rfcs/rfc822.html">RFC822</a>-style address forms with
comments, like Sendmail does.
+ <b>allow</b><i>_</i><b>broken</b><i>_</i><b>auth</b><i>_</i><b>clients</b>
+ Support older Microsoft clients that mis-implement
+ the AUTH protocol, and that expect an EHLO response
+ of "250 AUTH=list" instead of "250 AUTH list".
+
<b>Content</b> <b>inspection</b> <b>controls</b>
<b>content</b><i>_</i><b>filter</b>
The name of a mail delivery transport that filters
Location of Postfix support commands (default:
<b>$program</b><i>_</i><b>directory</b>).
- <b>debug</b><i>_</i><b>peer</b><i>_</i><b>level</b>
- Increment in verbose logging level when a remote
- host matches a pattern in the <b>debug</b><i>_</i><b>peer</b><i>_</i><b>list</b>
- parameter.
-
SMTPD(8) SMTPD(8)
+ <b>debug</b><i>_</i><b>peer</b><i>_</i><b>level</b>
+ Increment in verbose logging level when a remote
+ host matches a pattern in the <b>debug</b><i>_</i><b>peer</b><i>_</i><b>list</b>
+ parameter.
+
<b>debug</b><i>_</i><b>peer</b><i>_</i><b>list</b>
List of domain or network patterns. When a remote
host matches a pattern, increase the verbose log-
reject responses. This can be useful for testing
purposes.
-<b>Resource</b> <b>controls</b>
- <b>line</b><i>_</i><b>length</b><i>_</i><b>limit</b>
- Limit the amount of memory in bytes used for the
- handling of partial input lines.
-
SMTPD(8) SMTPD(8)
+<b>Resource</b> <b>controls</b>
+ <b>line</b><i>_</i><b>length</b><i>_</i><b>limit</b>
+ Limit the amount of memory in bytes used for the
+ handling of partial input lines.
+
<b>message</b><i>_</i><b>size</b><i>_</i><b>limit</b>
Limit the total size in bytes of a message, includ-
ing on-disk storage for envelope information.
Restrict what recipient addresses are allowed in
<b>RCPT</b> <b>TO</b> commands.
- <b>smtpd</b><i>_</i><b>etrn</b><i>_</i><b>restrictions</b>
- Restrict what domain names can be used in <b>ETRN</b> com-
- mands, and what clients may issue <b>ETRN</b> commands.
-
-
4
SMTPD(8) SMTPD(8)
+ <b>smtpd</b><i>_</i><b>etrn</b><i>_</i><b>restrictions</b>
+ Restrict what domain names can be used in <b>ETRN</b> com-
+ mands, and what clients may issue <b>ETRN</b> commands.
+
<b>allow</b><i>_</i><b>untrusted</b><i>_</i><b>routing</b>
Allow untrusted clients to specify addresses with
sender-specified routing. Enabling this opens up
name mapping violates the <b>reject</b><i>_</i><b>unknown</b><i>_</i><b>clients</b>
restriction.
- <b>unknown</b><i>_</i><b>hostname</b><i>_</i><b>reject</b><i>_</i><b>code</b>
- Server response when a client violates the
- <b>reject</b><i>_</i><b>unknown</b><i>_</i><b>hostname</b> restriction.
-
SMTPD(8) SMTPD(8)
+ <b>unknown</b><i>_</i><b>hostname</b><i>_</i><b>reject</b><i>_</i><b>code</b>
+ Server response when a client violates the
+ <b>reject</b><i>_</i><b>unknown</b><i>_</i><b>hostname</b> restriction.
+
<b>SEE</b> <b>ALSO</b>
<a href="cleanup.8.html">cleanup(8)</a> message canonicalization
<a href="master.8.html">master(8)</a> process manager
-
-
-
-
+++ /dev/null
-<html> <head> </head> <body> <pre>
-
-
-
-VIRTUAL(8) VIRTUAL(8)
-
-
-<b>NAME</b>
- virtual - Postfix virtual domain mail delivery agent
-
-<b>SYNOPSIS</b>
- <b>virtual</b> [generic Postfix daemon options]
-
-<b>DESCRIPTION</b>
- This daemon is designed for ISP's offering virtual mail
- hosting services. Originally based on the local delivery
- agent, this agent locates user mailboxes via map lookups
- of the full recipient address, rather than hard-coded unix
- password file searches of the local part only.
-
- The <b>virtual</b> daemon processes delivery requests from the
- Postfix queue manager to deliver mail to virtual local
- recipients. Each delivery request specifies a queue file,
- a sender address, a domain or host to deliver to, and one
- or more recipients. This program expects to be run from
- the <a href="master.8.html"><b>master</b>(8)</a> process manager.
-
- The <b>virtual</b> daemon updates queue files and marks recipi-
- ents as finished, or it informs the queue manager that
- delivery should be tried again at a later time. Delivery
- problem reports are sent to the <a href="bounce.8.html"><b>bounce</b>(8)</a> or <a href="defer.8.html"><b>defer</b>(8)</a> dae-
- mon as appropriate.
-
-<b>MAILBOX</b> <b>DELIVERY</b>
- The <b>virtual</b> delivery agent can deliver to UNIX-style mail-
- box file or to qmail-style maildir files. The pathname and
- delivery mailbox style are controlled by the <b>virtual</b><i>_</i><b>mail-</b>
- <b>box</b><i>_</i><b>base</b> and <b>virtual</b><i>_</i><b>mailbox</b><i>_</i><b>maps</b> configuration parameters
- (see below).
-
- In the case of UNIX-style mailbox delivery, the <b>local</b> dae-
- mon prepends a "<b>From</b> <i>sender</i> <i>time_stamp</i>" envelope header to
- each message, prepends a <b>Delivered-To:</b> header with the
- envelope recipient address, prepends a <b>Return-Path:</b> header
- with the envelope sender address, prepends a > character
- to lines beginning with "<b>From</b> ", and appends an empty
- line. The mailbox is locked for exclusive access while
- delivery is in progress. In case of problems, an attempt
- is made to truncate the mailbox to its original length.
-
- In the case of <b>maildir</b> delivery, the local daemon prepends
- a <b>Delivered-To:</b> header with the envelope recipient address
- and prepends a <b>Return-Path:</b> header with the envelope
- sender address.
-
-<b>DELIVERY</b> <b>RIGHTS</b>
- Deliveries are made with the user and group privileges
- that are listed in the tables specified with the <b>vir-</b>
- <b>tual</b><i>_</i><b>uid</b><i>_</i><b>maps</b> and <b>virtual</b><i>_</i><b>gid</b><i>_</i><b>maps</b>, respectively.
-
- The <b>virtual</b><i>_</i><b>minimum</b><i>_</i><b>uid</b> parameter specifies a lower bound
-
-
-
- 1
-
-
-
-
-
-VIRTUAL(8) VIRTUAL(8)
-
-
- on user ID values that may be specified in <b>vir-</b>
- <b>tual</b><i>_</i><b>uid</b><i>_</i><b>maps</b>. Mail will not be delivered when a too low
- UID value is found.
-
-<b>STANDARDS</b>
- <a href="http://www.faqs.org/rfcs/rfc822.html">RFC 822</a> (ARPA Internet Text Messages)
-
-<b>DIAGNOSTICS</b>
- Problems and transactions are logged to <b>syslogd</b>(8). Cor-
- rupted message files are marked so that the queue manager
- can move them to the <b>corrupt</b> queue afterwards.
-
- Depending on the setting of the <b>notify</b><i>_</i><b>classes</b> parameter,
- the postmaster is notified of bounces and of other trou-
- ble.
-
-<b>CONFIGURATION</b> <b>PARAMETERS</b>
- The following <b>main.cf</b> parameters are especially relevant
- to this program. See the Postfix <b>main.cf</b> file for syntax
- details and for default values. Use the <b>postfix</b> <b>reload</b>
- command after a configuration change.
-
-<b>Mailbox</b> <b>delivery</b>
- <b>virtual</b><i>_</i><b>mailbox</b><i>_</i><b>base</b>
- Specifies a path that is prepended to all mailbox
- or maildir paths. This is a safety measure to
- ensure that an out of control map in <b>virtual</b><i>_</i><b>mail-</b>
- <b>box</b><i>_</i><b>maps</b> doesn't litter the filesystem with mail-
- boxes. While it could be set to "/", this setting
- isn't recommended.
-
- <b>virtual</b><i>_</i><b>mailbox</b><i>_</i><b>maps</b>
- Recipients are looked up in this map to determine
- the path to their mailbox or maildir. If the
- returned path ends in a slash ("/"), maildir-style
- delivery is carried out, otherwise the path is
- assumed to specify a mailbox file.
-
- Note that <b>virtual</b><i>_</i><b>mailbox</b><i>_</i><b>base</b> is unconditionally
- prepended to this path.
-
- <b>virtual</b><i>_</i><b>minimum</b><i>_</i><b>uid</b>
- Specifies a minimum uid that will be accepted as a
- return from a <b>virtual</b><i>_</i><b>uid</b><i>_</i><b>maps</b> lookup. Returned
- values less than this will be rejected, and the
- message will be deferred.
-
- <b>virtual</b><i>_</i><b>uid</b><i>_</i><b>maps</b>
- Recipients are looked up in this map to determine
- the UID to be used when writing to the target mail-
- box.
-
- <b>virtual</b><i>_</i><b>gid</b><i>_</i><b>maps</b>
- Recipients are looked up in this map to determine
-
-
-
- 2
-
-
-
-
-
-VIRTUAL(8) VIRTUAL(8)
-
-
- the GID to be used when writing to the target mail-
- box.
-
-<b>Locking</b> <b>controls</b>
- <b>mailbox</b><i>_</i><b>delivery</b><i>_</i><b>lock</b>
- How to lock UNIX-style mailboxes: one or more of
- <b>flock</b>, <b>fcntl</b> or <b>dotlock</b>.
-
- Use the command <b>postconf</b> <b>-m</b> to find out what lock-
- ing methods are available on your system.
-
- <b>deliver</b><i>_</i><b>lock</b><i>_</i><b>attempts</b>
- Limit the number of attempts to acquire an exclu-
- sive lock on a mailbox file.
-
- <b>deliver</b><i>_</i><b>lock</b><i>_</i><b>delay</b>
- Time (default: seconds) between successive attempts
- to acquire an exclusive lock on a mailbox file.
-
- <b>stale</b><i>_</i><b>lock</b><i>_</i><b>time</b>
- Limit the time after which a stale lockfile is
- removed.
-
-<b>Resource</b> <b>controls</b>
- <b>virtual</b><i>_</i><b>destination</b><i>_</i><b>concurrency</b><i>_</i><b>limit</b>
- Limit the number of parallel deliveries to the same
- domain. The default limit is taken from the
- <b>default</b><i>_</i><b>destination</b><i>_</i><b>concurrency</b><i>_</i><b>limit</b> parameter.
-
- <b>virtual</b><i>_</i><b>destination</b><i>_</i><b>recipient</b><i>_</i><b>limit</b>
- Limit the number of recipients per message deliv-
- ery. The default limit is taken from the
- <b>default</b><i>_</i><b>destination</b><i>_</i><b>recipient</b><i>_</i><b>limit</b> parameter.
-
-<b>HISTORY</b>
- This agent was originally based on the local delivery
- agent. Modifications mainly consisted of removing code
- that wasn't applicable or wasn't safe in this context
- (aliases, .forwards, program aliases).
-
- The <b>Delivered-To:</b> header appears in the <b>qmail</b> system by
- Daniel Bernstein.
-
- The <i>maildir</i> structure appears in the <b>qmail</b> system by
- Daniel Bernstein.
-
-<b>SEE</b> <b>ALSO</b>
- <a href="bounce.8.html">bounce(8)</a> non-delivery status reports
- syslogd(8) system logging
- <a href="qmgr.8.html">qmgr(8)</a> queue manager
-
-<b>LICENSE</b>
- The Secure Mailer license must be distributed with this
- software.
-
-
-
- 3
-
-
-
-
-
-VIRTUAL(8) VIRTUAL(8)
-
-
-<b>AUTHOR(S)</b>
- Wietse Venema
- IBM T.J. Watson Research
- P.O. Box 704
- Yorktown Heights, NY 10598, USA
-
- Andrew McNamara
- andrewm@connect.com.au
- connect.com.au Pty. Ltd.
- Level 3, 213 Miller St
- North Sydney 2060, NSW, Australia
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 4
-
-
-</pre> </body> </html>
DAEMONS = man8/bounce.8 man8/defer.8 man8/cleanup.8 man8/error.8 man8/local.8 \
man8/lmtp.8 man8/master.8 man8/pickup.8 man8/pipe.8 man8/qmgr.8 \
man8/showq.8 man8/smtp.8 man8/smtpd.8 man8/trivial-rewrite.8 \
- man8/nqmgr.8 man8/spawn.8 man8/flush.8 man8/virtual.8
+ man8/spawn.8 man8/flush.8
COMMANDS= man1/postalias.1 man1/postcat.1 man1/postconf.1 man1/postfix.1 \
man1/postkick.1 man1/postlock.1 man1/postlog.1 man1/postdrop.1 \
man1/postmap.1 man1/sendmail.1 man1/mailq.1 man1/newaliases.1 \
man8/master.8: ../src/master/master.c
../mantools/srctoman $? >$@
-man8/nqmgr.8: ../src/nqmgr/qmgr.c
- ../mantools/srctoman $? | \
- sed -e 's/qmgr[^_]/n&/' \
- -e 's/qmgr$$/n&/' \
- -e 's/QMGR[^_]/N&/' >$@
-
man8/pickup.8: ../src/pickup/pickup.c
../mantools/srctoman $? >$@
man8/trivial-rewrite.8: ../src/trivial-rewrite/trivial-rewrite.c
../mantools/srctoman $? >$@
-man8/virtual.8: ../src/virtual/virtual.c
- ../mantools/srctoman $? >$@
-
man1/postalias.1: ../src/postalias/postalias.c
../mantools/srctoman $? >$@
+++ /dev/null
-.TH NQMGR 8
-.ad
-.fi
-.SH NAME
-nqmgr
-\-
-Postfix queue manager
-.SH SYNOPSIS
-.na
-.nf
-\fBnqmgr\fR [generic Postfix daemon options]
-.SH DESCRIPTION
-.ad
-.fi
-The \fBnqmgr\fR daemon awaits the arrival of incoming mail
-and arranges for its delivery via Postfix delivery processes.
-The actual mail routing strategy is delegated to the
-\fBtrivial-rewrite\fR(8) daemon.
-This program expects to be run from the \fBmaster\fR(8) process
-manager.
-
-Mail addressed to the local \fBdouble-bounce\fR address is silently
-discarded. This stops potential loops caused by undeliverable
-bounce notifications.
-
-Mail addressed to a user listed in the optional \fBrelocated\fR
-database is bounced with a "user has moved to \fInew_location\fR"
-message. See \fBrelocated\fR(5) for a precise description.
-.SH MAIL QUEUES
-.na
-.nf
-.ad
-.fi
-The \fBnqmgr\fR daemon maintains the following queues:
-.IP \fBincoming\fR
-Inbound mail from the network, or mail picked up by the
-local \fBpickup\fR agent from the \fBmaildrop\fR directory.
-.IP \fBactive\fR
-Messages that the queue manager has opened for delivery. Only
-a limited number of messages is allowed to enter the \fBactive\fR
-queue (leaky bucket strategy, for a fixed delivery rate).
-.IP \fBdeferred\fR
-Mail that could not be delivered upon the first attempt. The queue
-manager implements exponential backoff by doubling the time between
-delivery attempts.
-.IP \fBcorrupt\fR
-Unreadable or damaged queue files are moved here for inspection.
-.SH DELIVERY STATUS REPORTS
-.na
-.nf
-.ad
-.fi
-The \fBnqmgr\fR daemon keeps an eye on per-message delivery status
-reports in the following directories. Each status report file has
-the same name as the corresponding message file:
-.IP \fBbounce\fR
-Per-recipient status information about why mail is bounced.
-These files are maintained by the \fBbounce\fR(8) daemon.
-.IP \fBdefer\fR
-Per-recipient status information about why mail is delayed.
-These files are maintained by the \fBdefer\fR(8) daemon.
-.PP
-The \fBnqmgr\fR daemon is responsible for asking the
-\fBbounce\fR(8) or \fBdefer\fR(8) daemons to send non-delivery
-reports.
-.SH STRATEGIES
-.na
-.nf
-.ad
-.fi
-The queue manager implements a variety of strategies for
-either opening queue files (input) or for message delivery (output).
-.IP "\fBleaky bucket\fR"
-This strategy limits the number of messages in the \fBactive\fR queue
-and prevents the queue manager from running out of memory under
-heavy load.
-.IP \fBfairness\fR
-When the \fBactive\fR queue has room, the queue manager takes one
-message from the \fBincoming\fR queue and one from the \fBdeferred\fR
-queue. This prevents a large mail backlog from blocking the delivery
-of new mail.
-.IP "\fBslow start\fR"
-This strategy eliminates "thundering herd" problems by slowly
-adjusting the number of parallel deliveries to the same destination.
-.IP "\fBround robin\fR
-The queue manager sorts delivery requests by destination.
-Round-robin selection prevents one destination from dominating
-deliveries to other destinations.
-.IP "\fBexponential backoff\fR"
-Mail that cannot be delivered upon the first attempt is deferred.
-The time interval between delivery attempts is doubled after each
-attempt.
-.IP "\fBdestination status cache\fR"
-The queue manager avoids unnecessary delivery attempts by
-maintaining a short-term, in-memory list of unreachable destinations.
-.IP "\fBpreemptive message scheduling\fR"
-The queue manager attempts to minimize the average per-recipient delay
-while still preserving the correct per-message delays, using
-a sophisticated preemptive message scheduling.
-.SH TRIGGERS
-.na
-.nf
-.ad
-.fi
-On an idle system, the queue manager waits for the arrival of
-trigger events, or it waits for a timer to go off. A trigger
-is a one-byte message.
-Depending on the message received, the queue manager performs
-one of the following actions (the message is followed by the
-symbolic constant used internally by the software):
-.IP "\fBD (QMGR_REQ_SCAN_DEFERRED)\fR"
-Start a deferred queue scan. If a deferred queue scan is already
-in progress, that scan will be restarted as soon as it finishes.
-.IP "\fBI (QMGR_REQ_SCAN_INCOMING)\fR"
-Start an incoming queue scan. If an incoming queue scan is already
-in progress, that scan will be restarted as soon as it finishes.
-.IP "\fBA (QMGR_REQ_SCAN_ALL)\fR"
-Ignore deferred queue file time stamps. The request affects
-the next deferred queue scan.
-.IP "\fBF (QMGR_REQ_FLUSH_DEAD)\fR"
-Purge all information about dead transports and destinations.
-.IP "\fBW (TRIGGER_REQ_WAKEUP)\fR"
-Wakeup call, This is used by the master server to instantiate
-servers that should not go away forever. The action is to start
-an incoming queue scan.
-.PP
-The \fBnqmgr\fR daemon reads an entire buffer worth of triggers.
-Multiple identical trigger requests are collapsed into one, and
-trigger requests are sorted so that \fBA\fR and \fBF\fR precede
-\fBD\fR and \fBI\fR. Thus, in order to force a deferred queue run,
-one would request \fBA F D\fR; in order to notify the queue manager
-of the arrival of new mail one would request \fBI\fR.
-.SH STANDARDS
-.na
-.nf
-.ad
-.fi
-None. The \fBnqmgr\fR daemon does not interact with the outside world.
-.SH SECURITY
-.na
-.nf
-.ad
-.fi
-The \fBnqmgr\fR daemon is not security sensitive. It reads
-single-character messages from untrusted local users, and thus may
-be susceptible to denial of service attacks. The \fBnqmgr\fR daemon
-does not talk to the outside world, and it can be run at fixed low
-privilege in a chrooted environment.
-.SH DIAGNOSTICS
-.ad
-.fi
-Problems and transactions are logged to the syslog daemon.
-Corrupted message files are saved to the \fBcorrupt\fR queue
-for further inspection.
-
-Depending on the setting of the \fBnotify_classes\fR parameter,
-the postmaster is notified of bounces and of other trouble.
-.SH BUGS
-.ad
-.fi
-A single queue manager process has to compete for disk access with
-multiple front-end processes such as \fBsmtpd\fR. A sudden burst of
-inbound mail can negatively impact outbound delivery rates.
-.SH CONFIGURATION PARAMETERS
-.na
-.nf
-.ad
-.fi
-The following \fBmain.cf\fR parameters are especially relevant to
-this program. See the Postfix \fBmain.cf\fR file for syntax details
-and for default values. Use the \fBpostfix reload\fR command after
-a configuration change.
-.SH Miscellaneous
-.ad
-.fi
-.IP \fBallow_min_user\fR
-Do not bounce recipient addresses that begin with '-'.
-.IP \fBrelocated_maps\fR
-Tables with contact information for users, hosts or domains
-that no longer exist. See \fBrelocated\fR(5).
-.IP \fBqueue_directory\fR
-Top-level directory of the Postfix queue.
-.SH "Active queue controls"
-.ad
-.fi
-In the text below, \fItransport\fR is the first field in a
-\fBmaster.cf\fR entry.
-.IP \fBqmgr_message_active_limit\fR
-Limit the number of messages in the active queue.
-.IP \fBqmgr_message_recipient_limit\fR
-Limit the number of in-memory recipients.
-.sp
-This parameter also limits the size of the short-term, in-memory
-destination cache.
-.IP \fBqmgr_message_recipient_minimum\fR
-Per message minimum of in-memory recipients.
-.IP \fBdefault_recipient_limit\fR
-Default limit on the number of in-memory recipients per transport.
-.IP \fItransport\fB_recipient_limit\fR
-Limit on the number of in-memory recipients, for the named
-message \fItransport\fR.
-.IP \fBdefault_extra_recipient_limit\fR
-Default limit on the total number of per transport in-memory
-recipients that the preempting messages can have.
-.IP \fItransport\fB_extra_recipient_limit\fR
-Limit on the number of in-memory recipients which all preempting
-messages delivered by the transport \fItransport\fR can have.
-.SH "Timing controls"
-.ad
-.fi
-.IP \fBmin_backoff\fR
-Minimal time in seconds between delivery attempts
-of a deferred message.
-.sp
-This parameter also limits the time an unreachable destination
-is kept in the short-term, in-memory destination status cache.
-.IP \fBmax_backoff\fR
-Maximal time in seconds between delivery attempts
-of a deferred message.
-.IP \fBmaximal_queue_lifetime\fR
-Maximal time in days a message is queued
-before it is sent back as undeliverable.
-.IP \fBqueue_run_delay\fR
-Time in seconds between deferred queue scans. Queue scans do
-not overlap.
-.IP \fBtransport_retry_time\fR
-Time in seconds between attempts to contact a broken
-delivery transport.
-.SH "Concurrency controls"
-.ad
-.fi
-.IP \fBinitial_destination_concurrency\fR
-Initial per-destination concurrency level for parallel delivery
-to the same destination.
-.IP \fBdefault_destination_concurrency_limit\fR
-Default limit on the number of parallel deliveries to the same
-destination.
-.IP \fItransport\fB_destination_concurrency_limit\fR
-Limit on the number of parallel deliveries to the same destination,
-for delivery via the named message \fItransport\fR.
-.SH "Recipient controls"
-.ad
-.fi
-.IP \fBdefault_destination_recipient_limit\fR
-Default limit on the number of recipients per message transfer.
-.IP \fItransport\fB_destination_recipient_limit\fR
-Limit on the number of recipients per message transfer, for the
-named message \fItransport\fR.
-.SH "Message scheduling"
-.ad
-.fi
-.IP "\fItransport\fB_delivery_slot_cost\fR (valid range: 0,2,3...)
-This parameter basically controls how often a message
-delivered by \fItransport\fR can be preempted by another
-message.
-An internal per-message/transport counter is incremented by one
-for each \fItransport\fB_delivery_slot_cost\fR
-deliveries handled by \fItransport\fR. This counter represents
-the number of "available delivery slots" for use by other messages.
-Current message can be preempted by another message when that
-other message can be delivered using less \fItransport\fR agents
-than the value of the "available delivery slots" counter.
-.sp
-Value equal to 0 disables the message preemption for \fItransport\fR.
-.IP \fItransport\fB_minimum_delivery_slots\fR
-Message preemption is not attempted at all whenever a message
-that can't ever accumulate at least \fItransport\fB_minimum_delivery_slots\fR
-available delivery slots is being delivered by \fItransport\fR.
-.IP "\fItransport\fB_delivery_slot_discount\fR (valid range: 0..100)"
-.IP \fItransport\fB_delivery_slot_loan\fR
-These parameters speed up the moment when a message preemption can happen.
-Instead of waiting until the full amount of delivery slots
-required is available, the preemption can happen when
-\fItransport\fB_delivery_slot_discount\fR percent of the required
-amount plus \fItransport\fB_delivery_slot_loan\fR still remains to
-be accumulated. Note that the full amount will still have to be
-accumulated before another preemption can take place later.
-.IP \fBdefault_delivery_slot_cost\fR
-.IP \fBdefault_minimum_delivery_slots\fR
-.IP \fBdefault_delivery_slot_discount\fR
-.IP \fBdefault_delivery_slot_loan\fR
-Default values for the transport specific parameters described above.
-.SH SEE ALSO
-.na
-.nf
-master(8), process manager
-relocated(5), format of the "user has moved" table
-syslogd(8) system logging
-trivial-rewrite(8), address routing
-.SH LICENSE
-.na
-.nf
-.ad
-.fi
-The Secure Mailer license must be distributed with this software.
-.SH AUTHOR(S)
-.na
-.nf
-Wietse Venema
-IBM T.J. Watson Research
-P.O. Box 704
-Yorktown Heights, NY 10598, USA
-
-Scheduler enhancements:
-Patrik Rak
-Modra 6
-155 00, Prague, Czech Republic
.IP \fBstrict_rfc821_envelopes\fR
Disallow non-RFC 821 style addresses in envelopes. For example,
allow RFC822-style address forms with comments, like Sendmail does.
+.IP \fBallow_broken_auth_clients\fR
+Support older Microsoft clients that mis-implement the AUTH
+protocol, and that expect an EHLO response of "250 AUTH=list"
+instead of "250 AUTH list".
.SH "Content inspection controls"
.IP \fBcontent_filter\fR
The name of a mail delivery transport that filters mail and that
+++ /dev/null
-.TH VIRTUAL 8
-.ad
-.fi
-.SH NAME
-virtual
-\-
-Postfix virtual domain mail delivery agent
-.SH SYNOPSIS
-.na
-.nf
-\fBvirtual\fR [generic Postfix daemon options]
-.SH DESCRIPTION
-.ad
-.fi
-This daemon is designed for ISP's offering virtual mail hosting
-services. Originally based on the local delivery agent, this agent
-locates user mailboxes via map lookups of the full recipient
-address, rather than hard-coded unix password file searches of
-the local part only.
-
-The \fBvirtual\fR daemon processes delivery requests from the
-Postfix queue manager to deliver mail to virtual local recipients.
-Each delivery request specifies a queue file, a sender address,
-a domain or host to deliver to, and one or more recipients.
-This program expects to be run from the \fBmaster\fR(8) process
-manager.
-
-The \fBvirtual\fR daemon updates queue files and marks recipients
-as finished, or it informs the queue manager that delivery should
-be tried again at a later time. Delivery problem reports are sent
-to the \fBbounce\fR(8) or \fBdefer\fR(8) daemon as appropriate.
-.SH MAILBOX DELIVERY
-.na
-.nf
-.ad
-.fi
-The \fBvirtual\fR delivery agent can deliver to UNIX-style mailbox
-file or to qmail-style maildir files. The pathname and delivery
-mailbox style are controlled by the \fBvirtual_mailbox_base\fR
-and \fBvirtual_mailbox_maps\fR configuration parameters (see below).
-
-In the case of UNIX-style mailbox delivery,
-the \fBlocal\fR daemon prepends a "\fBFrom \fIsender time_stamp\fR"
-envelope header to each message, prepends a \fBDelivered-To:\fR header
-with the envelope recipient address, prepends a \fBReturn-Path:\fR
-header with the envelope sender address, prepends a \fB>\fR character
-to lines beginning with "\fBFrom \fR", and appends an empty line.
-The mailbox is locked for exclusive access while delivery is in
-progress. In case of problems, an attempt is made to truncate the
-mailbox to its original length.
-
-In the case of \fBmaildir\fR delivery, the local daemon prepends
-a \fBDelivered-To:\fR header with the envelope recipient address
-and prepends a \fBReturn-Path:\fR header with the envelope sender
-address.
-.SH DELIVERY RIGHTS
-.na
-.nf
-.ad
-.fi
-Deliveries are made with the user and group privileges that are
-listed in the tables specified with the \fBvirtual_uid_maps\fR
-and \fBvirtual_gid_maps\fR, respectively.
-
-The \fBvirtual_minimum_uid\fR parameter specifies a lower bound on
-user ID values that may be specified in \fBvirtual_uid_maps\fR. Mail
-will not be delivered when a too low UID value is found.
-.SH STANDARDS
-.na
-.nf
-RFC 822 (ARPA Internet Text Messages)
-.SH DIAGNOSTICS
-.ad
-.fi
-Problems and transactions are logged to \fBsyslogd\fR(8).
-Corrupted message files are marked so that the queue
-manager can move them to the \fBcorrupt\fR queue afterwards.
-
-Depending on the setting of the \fBnotify_classes\fR parameter,
-the postmaster is notified of bounces and of other trouble.
-.SH CONFIGURATION PARAMETERS
-.na
-.nf
-.ad
-.fi
-The following \fBmain.cf\fR parameters are especially relevant to
-this program. See the Postfix \fBmain.cf\fR file for syntax details
-and for default values. Use the \fBpostfix reload\fR command after
-a configuration change.
-.SH Mailbox delivery
-.ad
-.fi
-.IP \fBvirtual_mailbox_base\fR
-Specifies a path that is prepended to all mailbox or maildir paths.
-This is a safety measure to ensure that an out of control map in
-\fBvirtual_mailbox_maps\fR doesn't litter the filesystem with mailboxes.
-While it could be set to "/", this setting isn't recommended.
-.IP \fBvirtual_mailbox_maps\fR
-Recipients are looked up in this map to determine the path to
-their mailbox or maildir. If the returned path ends in a slash
-("/"), maildir-style delivery is carried out, otherwise the
-path is assumed to specify a mailbox file.
-
-Note that \fBvirtual_mailbox_base\fR is unconditionally prepended
-to this path.
-.IP \fBvirtual_minimum_uid\fR
-Specifies a minimum uid that will be accepted as a return from
-a \fBvirtual_uid_maps\fR lookup. Returned values less than this
-will be rejected, and the message will be deferred.
-.IP \fBvirtual_uid_maps\fR
-Recipients are looked up in this map to determine the UID to be
-used when writing to the target mailbox.
-.IP \fBvirtual_gid_maps\fR
-Recipients are looked up in this map to determine the GID to be
-used when writing to the target mailbox.
-.SH "Locking controls"
-.ad
-.fi
-.IP \fBmailbox_delivery_lock\fR
-How to lock UNIX-style mailboxes: one or more of \fBflock\fR,
-\fBfcntl\fR or \fBdotlock\fR.
-
-Use the command \fBpostconf -m\fR to find out what locking methods
-are available on your system.
-.IP \fBdeliver_lock_attempts\fR
-Limit the number of attempts to acquire an exclusive lock
-on a mailbox file.
-.IP \fBdeliver_lock_delay\fR
-Time (default: seconds) between successive attempts to acquire
-an exclusive lock on a mailbox file.
-.IP \fBstale_lock_time\fR
-Limit the time after which a stale lockfile is removed.
-.SH "Resource controls"
-.ad
-.fi
-.IP \fBvirtual_destination_concurrency_limit\fR
-Limit the number of parallel deliveries to the same domain.
-The default limit is taken from the
-\fBdefault_destination_concurrency_limit\fR parameter.
-.IP \fBvirtual_destination_recipient_limit\fR
-Limit the number of recipients per message delivery.
-The default limit is taken from the
-\fBdefault_destination_recipient_limit\fR parameter.
-.SH HISTORY
-.na
-.nf
-.ad
-.fi
-This agent was originally based on the local delivery
-agent. Modifications mainly consisted of removing code that wasn't
-applicable or wasn't safe in this context (aliases, .forwards,
-program aliases).
-
-The \fBDelivered-To:\fR header appears in the \fBqmail\fR system
-by Daniel Bernstein.
-
-The \fImaildir\fR structure appears in the \fBqmail\fR system
-by Daniel Bernstein.
-.SH SEE ALSO
-.na
-.nf
-bounce(8) non-delivery status reports
-syslogd(8) system logging
-qmgr(8) queue manager
-.SH LICENSE
-.na
-.nf
-.ad
-.fi
-The Secure Mailer license must be distributed with this software.
-.SH AUTHOR(S)
-.na
-.nf
-Wietse Venema
-IBM T.J. Watson Research
-P.O. Box 704
-Yorktown Heights, NY 10598, USA
-
-Andrew McNamara
-andrewm@connect.com.au
-connect.com.au Pty. Ltd.
-Level 3, 213 Miller St
-North Sydney 2060, NSW, Australia
+++ /dev/null
-../../.indent.pro
\ No newline at end of file
+++ /dev/null
-SHELL = /bin/sh
-SRCS = base64encode.c base64decode.c
-OBJS = base64encode.o base64decode.o
-HDRS =
-TESTSRC =
-WARN = -W -Wformat -Wimplicit -Wmissing-prototypes \
- -Wparentheses -Wstrict-prototypes -Wswitch -Wuninitialized \
- -Wunused
-DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE)
-CFLAGS = $(DEBUG) $(OPT) $(DEFS)
-TESTPROG=
-INC_DIR = ../../include
-PROG = base64encode base64decode
-LIBS = ../../lib/libglobal.a ../../lib/libutil.a
-
-.c.o:; $(CC) $(CFLAGS) -c $*.c
-
-all: $(PROG)
-
-Makefile: Makefile.in
- (set -e; echo "# DO NOT EDIT"; $(OPTS) $(SHELL) ../../makedefs && cat $?) >$@
-
-base64decode: base64decode.o $(LIBS)
- $(CC) $(CFLAGS) -o $@ base64decode.o $(LIBS) $(SYSLIBS)
-
-base64encode: base64encode.o $(LIBS)
- $(CC) $(CFLAGS) -o $@ base64encode.o $(LIBS) $(SYSLIBS)
-
-test: $(TESTPROG)
-
-update: ../../bin/base64encode ../../bin/base64decode
-
-../../bin/base64encode: base64encode
- cp $? $@
-
-../../bin/base64decode: base64decode
- cp $? $@
-
-printfck: $(OBJS) $(PROG)
- rm -rf printfck
- mkdir printfck
- sed '1,/^# do not edit/!d' Makefile >printfck/Makefile
- set -e; for i in *.c; do printfck -f .printfck $$i >printfck/$$i; done
- cd printfck; make "INC_DIR=../../../include" `cd ..; ls *.o`
-
-lint:
- lint $(DEFS) $(SRCS) $(LINTFIX)
-
-clean:
- rm -f *.o *core $(PROG) $(TESTPROG) junk
- rm -rf printfck
-
-tidy: clean
-
-depend: $(MAKES)
- (sed '1,/^# do not edit/!d' Makefile.in; \
- set -e; for i in [a-z][a-z0-9]*.c; do \
- $(CC) -E $(DEFS) $(INCL) $$i | sed -n -e '/^# *1 *"\([^"]*\)".*/{' \
- -e 's//'`echo $$i|sed 's/c$$/o/'`': \1/' -e 'p' -e '}'; \
- done) | grep -v '[.][o][:][ ][/]' >$$$$ && mv $$$$ Makefile.in
- @$(EXPORT) make -f Makefile.in Makefile 1>&2
-
-# do not edit below this line - it is generated by 'make depend'
-base64decode.o: base64decode.c
-base64decode.o: ../../include/sys_defs.h
-base64decode.o: ../../include/vstring.h
-base64decode.o: ../../include/vbuf.h
-base64decode.o: ../../include/vstream.h
-base64decode.o: ../../include/vstring_vstream.h
-base64decode.o: ../../include/msg.h
-base64decode.o: ../../include/msg_vstream.h
-base64encode.o: base64encode.c
-base64encode.o: ../../include/sys_defs.h
-base64encode.o: ../../include/vstring.h
-base64encode.o: ../../include/vbuf.h
-base64encode.o: ../../include/vstream.h
-base64encode.o: ../../include/vstring_vstream.h
-base64encode.o: ../../include/msg.h
-base64encode.o: ../../include/msg_vstream.h
+++ /dev/null
-/* base64decode - transform base 64 data to printable form */
-
-/* System library. */
-
-#include <sys_defs.h>
-#include <ctype.h>
-
-/* SASL library. */
-
-#include <sasl.h>
-#include <saslutil.h>
-
-/* Utility library. */
-
-#include <vstring.h>
-#include <vstream.h>
-#include <vstring_vstream.h>
-#include <msg.h>
-#include <msg_vstream.h>
-
-/* Application-specific. */
-
-#define STR(x) vstring_str(x)
-#define LEN(x) VSTRING_LEN(x)
-
-#define UCHAR(x) ((unsigned char *) (x))
-
-static VSTRING *escape(VSTRING *escaped, char *input, int len)
-{
- char *cp;
- unsigned ch;
-
- VSTRING_RESET(escaped);
- for (cp = input; cp < input + len; cp++) {
- ch = *UCHAR(cp);
- if (ISASCII(ch) && ISPRINT(ch))
- VSTRING_ADDCH(escaped, ch);
- else
- vstring_sprintf_append(escaped, "\\%03o", ch);
- }
- VSTRING_TERMINATE(escaped);
- return (escaped);
-}
-
-int main(int unused_argc, char **argv)
-{
- VSTRING *input = vstring_alloc(100);
- VSTRING *escaped = vstring_alloc(100);
- VSTRING *result = vstring_alloc(100);
- int len;
-
- msg_vstream_init(argv[0], VSTREAM_ERR);
-
- while (vstring_get_nonl(input, VSTREAM_IN) != VSTREAM_EOF) {
- VSTRING_SPACE(result, LEN(input));
- if (sasl_decode64(STR(input), LEN(input),
- STR(result), &len) != SASL_OK)
- msg_fatal("malformed input");
- vstream_printf("%s\n", STR(escape(escaped, STR(result), len)));
- vstream_fflush(VSTREAM_OUT);
- }
- return (0);
-}
+++ /dev/null
-/* base64encode - transform printable form to base 64 data */
-
-/* System library. */
-
-#include <sys_defs.h>
-#include <ctype.h>
-
-/* SASL library. */
-
-#include <sasl.h>
-#include <saslutil.h>
-
-/* Utility library. */
-
-#include <vstring.h>
-#include <vstream.h>
-#include <vstring_vstream.h>
-#include <msg.h>
-#include <msg_vstream.h>
-
-/* Application-specific. */
-
-#define STR(x) vstring_str(x)
-#define LEN(x) VSTRING_LEN(x)
-
-#define UCHAR(x) ((unsigned char *) (x))
-
-static VSTRING *unescape(VSTRING *unescaped, VSTRING *input)
-{
- char *cp = STR(input);
- int ch;
- int oval;
- int i;
-
- VSTRING_RESET(unescaped);
- while ((ch = *UCHAR(cp++)) != 0) {
- switch (ch) {
- case '\\':
- for (oval = 0, i = 0; i < 3 && (ch = *UCHAR(cp)) != 0; i++) {
- if (!ISDIGIT(ch) || ch == '8' || ch == '9')
- break;
- oval = (oval << 3) | (ch - '0');
- cp++;
- }
- VSTRING_ADDCH(unescaped, oval);
- break;
- default:
- VSTRING_ADDCH(unescaped, ch);
- break;
- }
- }
- VSTRING_TERMINATE(unescaped);
- return (unescaped);
-}
-
-int main(int unused_argc, char **argv)
-{
- VSTRING *input = vstring_alloc(100);
- VSTRING *unescaped = vstring_alloc(100);
- VSTRING *result = vstring_alloc(100);
- int result_len;
- int len;
-
- msg_vstream_init(argv[0], VSTREAM_ERR);
-
- while (vstring_get_nonl(input, VSTREAM_IN) != VSTREAM_EOF) {
- unescape(unescaped, input);
- result_len = ((LEN(unescaped) + 2) / 3) * 4 + 1;
- VSTRING_SPACE(result, result_len);
- if (sasl_encode64(STR(unescaped), LEN(unescaped), STR(result),
- result_len, &len) != SASL_OK)
- msg_panic("sasl_encode64 botch");
- vstream_printf("%.*s\n", len, STR(result));
- vstream_fflush(VSTREAM_OUT);
- }
- return (0);
-}
"####################################################################");
post_mail_fputs(bounce, "");
post_mail_fprintf(bounce,
- "Your message could not be delivered for %.1g hours.",
+ "Your message could not be delivered for %.1f hours.",
var_delay_warn_time / 3600.0);
post_mail_fprintf(bounce,
- "It will be retried until it is %.1g days old.",
+ "It will be retried until it is %.1f days old.",
var_max_queue_time / 86400.0);
}
#define DEF_STRICT_RFC821_ENV 0
extern bool var_strict_rfc821_env;
+ /*
+ * Standards violation: send "250 AUTH=list" in order to accomodate broken
+ * Microsoft clients.
+ */
+#define VAR_BROKEN_AUTH_CLNTS "allow_broken_auth_clients"
+#define DEF_BROKEN_AUTH_CLNTS 0
+extern bool var_broken_auth_clients;
+
/*
* Standards violation: disable VRFY.
*/
extern int var_smtpd_junk_cmd_limit;
/*
- * SASL authentication support, server side.
+ * SASL authentication support, SMTP server side.
*/
#define VAR_SMTPD_SASL_ENABLE "smtpd_sasl_auth_enable"
#define DEF_SMTPD_SASL_ENABLE 0
extern char *var_smtpd_sasl_realm;
/*
- * SASL authentication support, client side.
+ * SASL authentication support, SMTP client side.
*/
#define VAR_SMTP_SASL_ENABLE "smtp_sasl_auth_enable"
#define DEF_SMTP_SASL_ENABLE 0
#define DEF_SMTP_SASL_OPTS "noplaintext, noanonymous"
extern char *var_smtp_sasl_opts;
+ /*
+ * SASL authentication support, LMTP client side.
+ */
+#define VAR_LMTP_SASL_ENABLE "lmtp_sasl_auth_enable"
+#define DEF_LMTP_SASL_ENABLE 0
+extern bool var_lmtp_sasl_enable;
+
+#define VAR_LMTP_SASL_PASSWD "lmtp_sasl_password_maps"
+#define DEF_LMTP_SASL_PASSWD ""
+extern char *var_lmtp_sasl_passwd;
+
+#define VAR_LMTP_SASL_OPTS "lmtp_sasl_security_options"
+#define DEF_LMTP_SASL_OPTS "noplaintext, noanonymous"
+extern char *var_lmtp_sasl_opts;
+
+ /*
+ * SASL-based relay etc. control.
+ */
#define PERMIT_SASL_AUTH "permit_sasl_authenticated"
/*
#define VAR_VIRT_MAILBOX_MAPS "virtual_mailbox_maps"
#define DEF_VIRT_MAILBOX_MAPS ""
extern char *var_mailbox_maps;
+
#define VAR_VIRT_UID_MAPS "virtual_uid_maps"
#define DEF_VIRT_UID_MAPS ""
extern char *var_uid_maps;
+
#define VAR_VIRT_GID_MAPS "virtual_gid_maps"
#define DEF_VIRT_GID_MAPS ""
extern char *var_gid_maps;
+
#define VAR_VIRT_USEDOTLOCK "virtual_usedotlock"
#define DEF_VIRT_USEDOTLOCK 0
extern bool var_virt_usedotlock;
+
#define VAR_VIRT_MINUID "virtual_minimum_uid"
#define DEF_VIRT_MINUID 100
-extern int var_virt_minimum_uid;
+extern int var_virt_minimum_uid;
+
#define VAR_VIRT_MAILBOX_BASE "virtual_mailbox_base"
#define DEF_VIRT_MAILBOX_BASE ""
extern char *var_virt_mailbox_base;
* missing subdirectory.
*/
if ((fp = vstream_fopen(path, flags, mode)) == 0)
+ if (errno == ENOENT)
if ((flags & O_CREAT) == O_CREAT && mail_queue_mkdirs(path) == 0)
fp = vstream_fopen(path, flags, mode);
return (fp);
* Version of this program.
*/
#define VAR_MAIL_VERSION "mail_version"
-#define DEF_MAIL_VERSION "Snapshot-20010105"
+#define DEF_MAIL_VERSION "Release-20010119"
extern char *var_mail_version;
/* LICENSE
SHELL = /bin/sh
SRCS = lmtp.c lmtp_connect.c lmtp_proto.c lmtp_chat.c lmtp_session.c \
- lmtp_addr.c lmtp_trouble.c lmtp_state.c
+ lmtp_addr.c lmtp_trouble.c lmtp_state.c lmtp_sasl_glue.c \
+ lmtp_sasl_proto.c
OBJS = lmtp.o lmtp_connect.o lmtp_proto.o lmtp_chat.o lmtp_session.o \
- lmtp_addr.o lmtp_trouble.o lmtp_state.o
+ lmtp_addr.o lmtp_trouble.o lmtp_state.o lmtp_sasl_glue.o \
+ lmtp_sasl_proto.o
HDRS = lmtp.h
TESTSRC =
WARN = -W -Wformat -Wimplicit -Wmissing-prototypes \
lmtp.o: ../../include/mail_error.h
lmtp.o: ../../include/mail_server.h
lmtp.o: lmtp.h
+lmtp.o: lmtp_sasl.h
lmtp_addr.o: lmtp_addr.c
lmtp_addr.o: ../../include/sys_defs.h
lmtp_addr.o: ../../include/msg.h
lmtp_proto.o: ../../include/mark_corrupt.h
lmtp_proto.o: lmtp.h
lmtp_proto.o: ../../include/argv.h
+lmtp_proto.o: lmtp_sasl.h
lmtp_proto.o: ../../include/quote_821_local.h
+lmtp_sasl_glue.o: lmtp_sasl_glue.c
+lmtp_sasl_glue.o: ../../include/sys_defs.h
+lmtp_sasl_glue.o: ../../include/msg.h
+lmtp_sasl_glue.o: ../../include/mymalloc.h
+lmtp_sasl_glue.o: ../../include/stringops.h
+lmtp_sasl_glue.o: ../../include/vstring.h
+lmtp_sasl_glue.o: ../../include/vbuf.h
+lmtp_sasl_glue.o: ../../include/split_at.h
+lmtp_sasl_glue.o: ../../include/name_mask.h
+lmtp_sasl_glue.o: ../../include/mail_params.h
+lmtp_sasl_glue.o: ../../include/string_list.h
+lmtp_sasl_glue.o: ../../include/maps.h
+lmtp_sasl_glue.o: ../../include/dict.h
+lmtp_sasl_glue.o: ../../include/vstream.h
+lmtp_sasl_glue.o: ../../include/argv.h
+lmtp_sasl_glue.o: lmtp_sasl.h
+lmtp_sasl_proto.o: lmtp_sasl_proto.c
+lmtp_sasl_proto.o: ../../include/sys_defs.h
+lmtp_sasl_proto.o: ../../include/msg.h
+lmtp_sasl_proto.o: ../../include/mymalloc.h
+lmtp_sasl_proto.o: ../../include/mail_params.h
+lmtp_sasl_proto.o: lmtp_sasl.h
lmtp_session.o: lmtp_session.c
lmtp_session.o: ../../include/sys_defs.h
lmtp_session.o: ../../include/mymalloc.h
lmtp_state.o: ../../include/argv.h
lmtp_state.o: ../../include/deliver_request.h
lmtp_state.o: ../../include/recipient_list.h
+lmtp_state.o: lmtp_sasl.h
lmtp_trouble.o: lmtp_trouble.c
lmtp_trouble.o: ../../include/sys_defs.h
lmtp_trouble.o: ../../include/msg.h
/* RFC 1870 (Message Size Declaration)
/* RFC 2033 (LMTP protocol)
/* RFC 2197 (Pipelining)
+/* RFC 2554 (AUTH command)
/* DIAGNOSTICS
/* Problems and transactions are logged to \fBsyslogd\fR(8).
/* Corrupted message files are marked so that the queue manager can
/* .IP \fBlmtp_tcp_port\fR
/* The TCP port to be used when connecting to a LMTP server. Used as
/* backup if the \fBlmtp\fR service is not found in \fBservices\fR(4).
+/* .SH "Authentication controls"
+/* .IP \fBlmtp_enable_sasl_auth\fR
+/* Enable per-session authentication as per RFC 2554 (SASL).
+/* By default, Postfix is built without SASL support.
+/* .IP \fBlmtp_sasl_password_maps\fR
+/* Lookup tables with per-host or domain \fIname\fR:\fIpassword\fR entries.
+/* No entry for a host means no attempt to authenticate.
+/* .IP \fBlmtp_sasl_security_options\fR
+/* Zero or more of the following.
+/* .RS
+/* .IP \fBnoplaintext\fR
+/* Disallow authentication methods that use plaintext passwords.
+/* .IP \fBnoactive\fR
+/* Disallow authentication methods that are vulnerable to non-dictionary
+/* active attacks.
+/* .IP \fBnodictionary\fR
+/* Disallow authentication methods that are vulnerable to passive
+/* dictionary attack.
+/* .IP \fBnoanonymous\fR
+/* Disallow anonymous logins.
+/* .RE
/* .SH "Resource controls"
/* .ad
/* .fi
/* Application-specific. */
#include "lmtp.h"
+#include "lmtp_sasl.h"
/*
* Tunable parameters. These have compiled-in defaults that can be overruled
int var_lmtp_skip_quit_resp;
char *var_notify_classes;
char *var_error_rcpt;
+char *var_lmtp_sasl_opts;
+char *var_lmtp_sasl_passwd;
+bool var_lmtp_sasl_enable;
/*
* Global variables.
static void pre_init(char *unused_name, char **unused_argv)
{
debug_peer_init();
+#ifdef USE_SASL_AUTH
+ if (var_lmtp_sasl_enable)
+ lmtp_sasl_initialize();
+#endif
}
/* cleanup - close any open connections, etc. */
msg_info("cleanup: just closed down session");
}
lmtp_state_free(state);
+#ifdef USE_SASL_AUTH
+ if (var_lmtp_sasl_enable)
+ sasl_done();
+#endif
}
/* pre_accept - see if tables have changed */
VAR_DEBUG_PEER_LIST, DEF_DEBUG_PEER_LIST, &var_debug_peer_list, 0, 0,
VAR_NOTIFY_CLASSES, DEF_NOTIFY_CLASSES, &var_notify_classes, 0, 0,
VAR_ERROR_RCPT, DEF_ERROR_RCPT, &var_error_rcpt, 1, 0,
+ VAR_LMTP_SASL_PASSWD, DEF_LMTP_SASL_PASSWD, &var_lmtp_sasl_passwd, 0, 0,
+ VAR_LMTP_SASL_OPTS, DEF_LMTP_SASL_OPTS, &var_lmtp_sasl_opts, 0, 0,
0,
};
static CONFIG_INT_TABLE int_table[] = {
/* DESCRIPTION
/* .nf
+ /*
+ * SASL library.
+ */
+#ifdef USE_SASL_AUTH
+#include <sasl.h>
+#include <saslutil.h>
+#endif
+
/*
* Utility library.
*/
int features; /* server features */
ARGV *history; /* transaction log */
int error_mask; /* error classes */
+#ifdef USE_SASL_AUTH
+ char *sasl_mechanism_list; /* server mechanism list */
+ char *sasl_username; /* client username */
+ char *sasl_passwd; /* client password */
+ sasl_conn_t *sasl_conn; /* SASL internal state */
+ VSTRING *sasl_encoded; /* encoding buffer */
+ VSTRING *sasl_decoded; /* decoding buffer */
+ sasl_callback_t *sasl_callbacks; /* stateful callbacks */
+#endif
int sndbufsize; /* total window size */
int sndbuffree; /* remaining window */
int reuse; /* connection being reused */
#define LMTP_FEATURE_8BITMIME (1<<1)
#define LMTP_FEATURE_PIPELINING (1<<2)
#define LMTP_FEATURE_SIZE (1<<3)
+#define SMTP_FEATURE_AUTH (1<<5)
/*
* lmtp.c
#include <mail_params.h>
#include <mail_addr.h>
#include <post_mail.h>
+#include <mail_error.h>
/* Application-specific. */
{
LMTP_SESSION *session = state->session;
static LMTP_RESP rdata;
- int more;
char *cp;
int last_char;
VSTRING_RESET(rdata.buf);
for (;;) {
last_char = smtp_get(state->buffer, session->stream, var_line_limit);
- cp = printable(STR(state->buffer), '?');
+ printable(STR(state->buffer), '?');
if (last_char != '\n')
msg_warn("%s: response longer than %d: %.30s...",
- session->namaddr, var_line_limit, cp);
+ session->namaddr, var_line_limit, STR(state->buffer));
if (msg_verbose)
- msg_info("< %s: %s", session->namaddr, cp);
- while (ISDIGIT(*cp))
- cp++;
- rdata.code = (cp - STR(state->buffer) == 3 ?
- atoi(STR(state->buffer)) : 0);
- more = (*cp == '-');
+ msg_info("< %s: %s", session->namaddr, STR(state->buffer));
/*
* Defend against a denial of service attack by limiting the amount
vstring_strcat(rdata.buf, STR(state->buffer));
lmtp_chat_append(state, "In: ", STR(state->buffer));
}
- if (VSTRING_LEN(state->buffer) == 0) /* XXX remote brain damage */
- continue;
- if (!ISDIGIT(STR(state->buffer)[0])) /* XXX remote brain damage */
- continue;
- if (more == 0)
- break;
+
+ /*
+ * Parse into code and text. Ignore unrecognized garbage. This means
+ * that any character except space (or end of line) will have the
+ * same effect as the '-' line continuation character.
+ */
+ for (cp = STR(state->buffer); *cp && ISDIGIT(*cp); cp++)
+ /* void */ ;
+ if (cp - STR(state->buffer) == 3) {
+ if (*cp == '-')
+ continue;
+ if (*cp == ' ' || *cp == 0)
+ break;
+ }
+ state->error_mask |= MAIL_ERROR_PROTOCOL;
}
+ rdata.code = atoi(STR(state->buffer));
VSTRING_TERMINATE(rdata.buf);
rdata.str = STR(rdata.buf);
return (&rdata);
#include <rec_type.h>
#include <off_cvt.h>
#include <mark_corrupt.h>
+#include <quote_821_local.h>
/* Application-specific. */
#include "lmtp.h"
-#include "quote_821_local.h"
+#include "lmtp_sasl.h"
/*
* Sender and receiver state. A session does not necessarily go through a
*/
smtp_timeout_setup(state->session->stream, var_lmtp_lhlo_tmout);
if ((except = vstream_setjmp(state->session->stream)) != 0)
- return (lmtp_stream_except(state, except, "sending LHLO"));
+ return (smtp_stream_except(state, except, "sending LHLO"));
/*
* Read and parse the server's LMTP greeting banner.
*/
if (((resp = lmtp_chat_resp(state))->code / 100) != 2)
return (lmtp_site_fail(state, resp->code,
- "%s refused to talk to me: %s",
+ "host %s refused to talk to me: %s",
session->namaddr, translit(resp->str, "\n", " ")));
/*
lmtp_chat_cmd(state, "LHLO %s", var_myhostname);
if ((resp = lmtp_chat_resp(state))->code / 100 != 2)
return (lmtp_site_fail(state, resp->code,
- "%s refused to talk to me: %s",
+ "host %s refused to talk to me: %s",
session->namaddr,
translit(resp->str, "\n", " ")));
state->features |= LMTP_FEATURE_PIPELINING;
else if (strcasecmp(word, "SIZE") == 0)
state->features |= LMTP_FEATURE_SIZE;
+#ifdef USE_SASL_AUTH
+ else if (var_lmtp_sasl_enable && strcasecmp(word, "AUTH") == 0)
+ lmtp_sasl_helo_auth(state, words);
+#endif
}
}
if (msg_verbose)
msg_info("server features: 0x%x", state->features);
+#ifdef USE_SASL_AUTH
+ if (var_lmtp_sasl_enable && (state->features & SMTP_FEATURE_AUTH))
+ return (lmtp_sasl_helo_login(state));
+#endif
+
/*
* We use LMTP command pipelining if the server said it supported it.
* Since we use blocking I/O, RFC 2197 says that we should inspect the
smtp_timeout_setup(state->session->stream,
*xfer_timeouts[recv_state]);
if ((except = vstream_setjmp(state->session->stream)) != 0)
- RETURN(lmtp_stream_except(state, except,
+ RETURN(smtp_stream_except(state, except,
xfer_states[recv_state]));
resp = lmtp_chat_resp(state);
case LMTP_STATE_MAIL:
if (resp->code / 100 != 2) {
lmtp_mesg_fail(state, resp->code,
- "%s said: %s", session->namaddr,
+ "host %s said: %s", session->namaddr,
translit(resp->str, "\n", " "));
mail_from_rejected = 1;
}
survivors[nrcpt++] = recv_rcpt;
} else {
lmtp_rcpt_fail(state, resp->code, rcpt,
- "%s said: %s", session->namaddr,
+ "host %s said: %s", session->namaddr,
translit(resp->str, "\n", " "));
rcpt->offset = 0; /* in case deferred */
}
if (resp->code / 100 != 3) {
if (nrcpt > 0)
lmtp_mesg_fail(state, resp->code,
- "%s said: %s", session->namaddr,
+ "host %s said: %s", session->namaddr,
translit(resp->str, "\n", " "));
nrcpt = -1;
}
}
} else {
lmtp_rcpt_fail(state, resp->code, rcpt,
- "%s said: %s", session->namaddr,
+ "host %s said: %s", session->namaddr,
translit(resp->str, "\n", " "));
rcpt->offset = 0; /* in case deferred */
}
smtp_timeout_setup(state->session->stream,
var_lmtp_data1_tmout);
if ((except = vstream_setjmp(state->session->stream)) != 0)
- RETURN(lmtp_stream_except(state, except,
+ RETURN(smtp_stream_except(state, except,
"sending message body"));
if (vstream_fseek(state->src, request->data_offset, SEEK_SET) < 0)
--- /dev/null
+/*++
+/* NAME
+/* lmtp_sasl 3h
+/* SUMMARY
+/* Postfix SASL interface for LMTP client
+/* SYNOPSIS
+/* #include "lmtp_sasl.h"
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * SASL protocol functions
+ */
+extern void lmtp_sasl_initialize(void);
+extern void lmtp_sasl_connect(LMTP_STATE *);
+extern int lmtp_sasl_passwd_lookup(LMTP_STATE *);
+extern void lmtp_sasl_start(LMTP_STATE *);
+extern int lmtp_sasl_authenticate(LMTP_STATE *, VSTRING *);
+extern void lmtp_sasl_cleanup(LMTP_STATE *);
+
+extern void lmtp_sasl_helo_auth(LMTP_STATE *, const char *);
+extern int lmtp_sasl_helo_login(LMTP_STATE *);
+
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Initial implementation by:
+/* Till Franke
+/* SuSE Rhein/Main AG
+/* 65760 Eschborn, Germany
+/*
+/* Adopted by:
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*--*/
--- /dev/null
+/*++
+/* NAME
+/* lmtp_sasl 3
+/* SUMMARY
+/* Postfix SASL interface for LMTP client
+/* SYNOPSIS
+/* #include lmtp_sasl.h
+/*
+/* void lmtp_sasl_initialize()
+/*
+/* void lmtp_sasl_connect(state)
+/* LMTP_STATE *state;
+/*
+/* void lmtp_sasl_start(state)
+/* LMTP_STATE *state;
+/*
+/* int lmtp_sasl_passwd_lookup(state)
+/* LMTP_STATE *state;
+/*
+/* int lmtp_sasl_authenticate(state, why)
+/* LMTP_STATE *state;
+/* VSTRING *why;
+/*
+/* void lmtp_sasl_cleanup(state)
+/* LMTP_STATE *state;
+/* DESCRIPTION
+/* lmtp_sasl_initialize() initializes the SASL library. This
+/* routine must be called once at process startup, before any
+/* chroot operations.
+/*
+/* lmtp_sasl_connect() performs per-session initialization. This
+/* routine must be called once at the start of each connection.
+/*
+/* lmtp_sasl_start() performs per-session initialization. This
+/* routine must be called once per session before doing any SASL
+/* authentication.
+/*
+/* lmtp_sasl_passwd_lookup() looks up the username/password
+/* for the current SMTP server. The result is zero in case
+/* of failure.
+/*
+/* lmtp_sasl_authenticate() implements the SASL authentication
+/* dialog. The result is < 0 in case of protocol failure, zero in
+/* case of unsuccessful authentication, > 0 in case of success.
+/* The why argument is updated with a reason for failure.
+/* This routine must be called only when lmtp_sasl_passwd_lookup()
+/* suceeds.
+/*
+/* lmtp_sasl_cleanup() cleans up. It must be called at the
+/* end of every SMTP session that uses SASL authentication.
+/* This routine is a noop for non-SASL sessions.
+/*
+/* Arguments:
+/* .IP state
+/* Session context.
+/* .IP mech_list
+/* String of SASL mechanisms (separated by blanks)
+/* DIAGNOSTICS
+/* All errors are fatal.
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Original author:
+/* Till Franke
+/* SuSE Rhein/Main AG
+/* 65760 Eschborn, Germany
+/*
+/* Adopted by:
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*--*/
+
+ /*
+ * System library.
+ */
+#include <sys_defs.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef STRCASECMP_IN_STRINGS_H
+#include <strings.h>
+#endif
+
+ /*
+ * Utility library
+ */
+#include <msg.h>
+#include <mymalloc.h>
+#include <stringops.h>
+#include <split_at.h>
+#include <name_mask.h>
+
+ /*
+ * Global library
+ */
+#include <mail_params.h>
+#include <string_list.h>
+#include <maps.h>
+
+ /*
+ * Application-specific
+ */
+#include "lmtp.h"
+#include "lmtp_sasl.h"
+
+#ifdef USE_SASL_AUTH
+
+ /*
+ * Authentication security options.
+ */
+static NAME_MASK lmtp_sasl_sec_mask[] = {
+ "noplaintext", SASL_SEC_NOPLAINTEXT,
+ "noactive", SASL_SEC_NOACTIVE,
+ "nodictionary", SASL_SEC_NODICTIONARY,
+ "noanonymous", SASL_SEC_NOANONYMOUS,
+ 0,
+};
+
+static int lmtp_sasl_sec_opts;
+
+ /*
+ * Silly little macros.
+ */
+#define STR(x) vstring_str(x)
+
+ /*
+ * Per-host login/password information.
+ */
+static MAPS *lmtp_sasl_passwd_map;
+
+/* lmtp_sasl_log - logging call-back routine */
+
+static int lmtp_sasl_log(void *unused_context, int priority,
+ const char *message)
+{
+ switch (priority) {
+ case SASL_LOG_ERR:
+ case SASL_LOG_WARNING:
+ msg_warn("%s", message);
+ break;
+ case SASL_LOG_INFO:
+ if (msg_verbose)
+ msg_info("%s", message);
+ break;
+ }
+ return (SASL_OK);
+}
+
+/* lmtp_sasl_get_user - username lookup call-back routine */
+
+static int lmtp_sasl_get_user(void *context, int unused_id, const char **result,
+ unsigned *len)
+{
+ char *myname = "lmtp_sasl_get_user";
+ LMTP_STATE *state = (LMTP_STATE *) context;
+
+ if (msg_verbose)
+ msg_info("%s: %s", myname, state->sasl_username);
+
+ /*
+ * Sanity check.
+ */
+ if (state->sasl_passwd == 0)
+ msg_panic("%s: no username looked up", myname);
+
+ *result = state->sasl_username;
+ if (len)
+ *len = strlen(state->sasl_username);
+ return (SASL_OK);
+}
+
+/* lmtp_sasl_get_passwd - password lookup call-back routine */
+
+static int lmtp_sasl_get_passwd(sasl_conn_t *conn, void *context,
+ int id, sasl_secret_t **psecret)
+{
+ char *myname = "lmtp_sasl_get_passwd";
+ LMTP_STATE *state = (LMTP_STATE *) context;
+ int len;
+
+ if (msg_verbose)
+ msg_info("%s: %s", myname, state->sasl_passwd);
+
+ /*
+ * Sanity check.
+ */
+ if (!conn || !psecret || id != SASL_CB_PASS)
+ return (SASL_BADPARAM);
+ if (state->sasl_passwd == 0)
+ msg_panic("%s: no password looked up", myname);
+
+ /*
+ * Convert the password into a counted string.
+ */
+ len = strlen(state->sasl_passwd);
+ if ((*psecret = (sasl_secret_t *) malloc(sizeof(sasl_secret_t) + len)) == 0)
+ return (SASL_NOMEM);
+ (*psecret)->len = len;
+ memcpy((*psecret)->data, state->sasl_passwd, len + 1);
+
+ return (SASL_OK);
+}
+
+/* lmtp_sasl_passwd_lookup - password lookup routine */
+
+int lmtp_sasl_passwd_lookup(LMTP_STATE *state)
+{
+ char *myname = "lmtp_sasl_passwd_lookup";
+ const char *value;
+ char *passwd;
+
+ /*
+ * Sanity check.
+ */
+ if (lmtp_sasl_passwd_map == 0)
+ msg_panic("%s: passwd map not initialized", myname);
+
+ /*
+ * Look up the per-server password information. Try the hostname first,
+ * then try the destination.
+ */
+ if ((value = maps_find(lmtp_sasl_passwd_map, state->session->host, 0)) != 0
+ || (value = maps_find(lmtp_sasl_passwd_map, state->request->nexthop, 0)) != 0) {
+ state->sasl_username = mystrdup(value);
+ passwd = split_at(state->sasl_username, ':');
+ state->sasl_passwd = mystrdup(passwd ? passwd : "");
+ if (msg_verbose)
+ msg_info("%s: host `%s' user `%s' pass `%s'",
+ myname, state->session->host,
+ state->sasl_username, state->sasl_passwd);
+ return (1);
+ } else {
+ if (msg_verbose)
+ msg_info("%s: host `%s' no auth info found",
+ myname, state->session->host);
+ return (0);
+ }
+}
+
+/* lmtp_sasl_initialize - per-process initialization (pre jail) */
+
+void lmtp_sasl_initialize(void)
+{
+
+ /*
+ * Global callbacks. These have no per-session context.
+ */
+ static sasl_callback_t callbacks[] = {
+ {SASL_CB_LOG, &lmtp_sasl_log, 0},
+ {SASL_CB_LIST_END, 0, 0}
+ };
+
+ /*
+ * Sanity check.
+ */
+ if (lmtp_sasl_passwd_map)
+ msg_panic("lmtp_sasl_initialize: repeated call");
+ if (*var_lmtp_sasl_passwd == 0)
+ msg_fatal("specify a password table via the `%s' configuration parameter",
+ VAR_LMTP_SASL_PASSWD);
+
+ /*
+ * Open the per-host password table and initialize the SASL library. Use
+ * shared locks for reading, just in case someone updates the table.
+ */
+ lmtp_sasl_passwd_map = maps_create("lmtp_sasl_passwd",
+ var_lmtp_sasl_passwd, DICT_FLAG_LOCK);
+ if (sasl_client_init(callbacks) != SASL_OK)
+ msg_fatal("SASL library initialization");
+
+ /*
+ * Configuration parameters.
+ */
+ lmtp_sasl_sec_opts = name_mask(VAR_LMTP_SASL_OPTS, lmtp_sasl_sec_mask,
+ var_lmtp_sasl_opts);
+}
+
+/* lmtp_sasl_connect - per-session client initialization */
+
+void lmtp_sasl_connect(LMTP_STATE *state)
+{
+ state->sasl_mechanism_list = 0;
+ state->sasl_username = 0;
+ state->sasl_passwd = 0;
+ state->sasl_conn = 0;
+ state->sasl_encoded = 0;
+ state->sasl_decoded = 0;
+ state->sasl_callbacks = 0;
+}
+
+/* lmtp_sasl_start - per-session SASL initialization */
+
+void lmtp_sasl_start(LMTP_STATE *state)
+{
+ static sasl_callback_t callbacks[] = {
+ {SASL_CB_USER, &lmtp_sasl_get_user, 0},
+ {SASL_CB_AUTHNAME, &lmtp_sasl_get_user, 0},
+ {SASL_CB_PASS, &lmtp_sasl_get_passwd, 0},
+ {SASL_CB_LIST_END, 0, 0}
+ };
+ sasl_callback_t *cp;
+ sasl_security_properties_t sec_props;
+
+ if (msg_verbose)
+ msg_info("starting new SASL client");
+
+ /*
+ * Per-session initialization. Provide each session with its own callback
+ * context.
+ */
+#define NULL_SECFLAGS 0
+
+ state->sasl_callbacks = (sasl_callback_t *) mymalloc(sizeof(callbacks));
+ memcpy((char *) state->sasl_callbacks, callbacks, sizeof(callbacks));
+ for (cp = state->sasl_callbacks; cp->id != SASL_CB_LIST_END; cp++)
+ cp->context = (void *) state;
+ if (sasl_client_new("smtp", state->session->host,
+ state->sasl_callbacks, NULL_SECFLAGS,
+ (sasl_conn_t **) &state->sasl_conn) != SASL_OK)
+ msg_fatal("per-session SASL client initialization");
+
+ /*
+ * Per-session security properties. XXX This routine is not sufficiently
+ * documented. What is the purpose of all this?
+ */
+ memset(&sec_props, 0L, sizeof(sec_props));
+ sec_props.min_ssf = 0;
+ sec_props.max_ssf = 1; /* don't allow real SASL
+ * security layer */
+ sec_props.security_flags = lmtp_sasl_sec_opts;
+ sec_props.maxbufsize = 0;
+ sec_props.property_names = 0;
+ sec_props.property_values = 0;
+ if (sasl_setprop(state->sasl_conn, SASL_SEC_PROPS,
+ &sec_props) != SASL_OK)
+ msg_fatal("set per-session SASL security properties");
+
+ /*
+ * We use long-lived conversion buffers rather than local variables in
+ * order to avoid memory leaks in case of read/write timeout or I/O
+ * error.
+ */
+ state->sasl_encoded = vstring_alloc(10);
+ state->sasl_decoded = vstring_alloc(10);
+}
+
+/* lmtp_sasl_authenticate - run authentication protocol */
+
+int lmtp_sasl_authenticate(LMTP_STATE *state, VSTRING *why)
+{
+ char *myname = "lmtp_sasl_authenticate";
+ unsigned enc_length;
+ unsigned enc_length_out;
+ char *clientout;
+ unsigned clientoutlen;
+ unsigned serverinlen;
+ LMTP_RESP *resp;
+ const char *mechanism;
+ int result;
+ char *line;
+
+#define NO_SASL_SECRET 0
+#define NO_SASL_INTERACTION 0
+#define NO_SASL_LANGLIST ((const char *) 0)
+#define NO_SASL_OUTLANG ((const char **) 0)
+
+ if (msg_verbose)
+ msg_info("%s: %s: SASL mechanisms %s",
+ myname, state->session->namaddr, state->sasl_mechanism_list);
+
+ /*
+ * Start the client side authentication protocol.
+ */
+ result = sasl_client_start((sasl_conn_t *) state->sasl_conn,
+ state->sasl_mechanism_list,
+ NO_SASL_SECRET, NO_SASL_INTERACTION,
+ &clientout, &clientoutlen, &mechanism);
+ if (result != SASL_OK && result != SASL_CONTINUE) {
+ vstring_sprintf(why, "cannot SASL authenticate to server %s: %s",
+ state->session->namaddr,
+ sasl_errstring(result, NO_SASL_LANGLIST,
+ NO_SASL_OUTLANG));
+ return (-1);
+ }
+
+ /*
+ * Send the AUTH command and the optional initial client response.
+ * sasl_encode64() produces four bytes for each complete or incomplete
+ * triple of input bytes. Allocate an extra byte for string termination.
+ */
+#define ENCODE64_LENGTH(n) ((((n) + 2) / 3) * 4)
+
+ if (clientoutlen > 0) {
+ if (msg_verbose)
+ msg_info("%s: %s: uncoded initial reply: %.*s",
+ myname, state->session->namaddr,
+ (int) clientoutlen, clientout);
+ enc_length = ENCODE64_LENGTH(clientoutlen) + 1;
+ VSTRING_SPACE(state->sasl_encoded, enc_length);
+ if (sasl_encode64(clientout, clientoutlen,
+ STR(state->sasl_encoded), enc_length,
+ &enc_length_out) != SASL_OK)
+ msg_panic("%s: sasl_encode64 botch", myname);
+ free(clientout);
+ lmtp_chat_cmd(state, "AUTH %s %s", mechanism, STR(state->sasl_encoded));
+ } else {
+ lmtp_chat_cmd(state, "AUTH %s", mechanism);
+ }
+
+ /*
+ * Step through the authentication protocol until the server tells us
+ * that we are done.
+ */
+ while ((resp = lmtp_chat_resp(state))->code / 100 == 3) {
+
+ /*
+ * Process a server challenge.
+ */
+ line = resp->str;
+ (void) mystrtok(&line, "- \t\n"); /* skip over result code */
+ serverinlen = strlen(line);
+ VSTRING_SPACE(state->sasl_decoded, serverinlen);
+ if (sasl_decode64(line, serverinlen,
+ STR(state->sasl_decoded), &enc_length) != SASL_OK) {
+ vstring_sprintf(why, "malformed SASL challenge from server %s",
+ state->session->namaddr);
+ return (-1);
+ }
+ if (msg_verbose)
+ msg_info("%s: %s: decoded challenge: %.*s",
+ myname, state->session->namaddr,
+ (int) enc_length, STR(state->sasl_decoded));
+ result = sasl_client_step((sasl_conn_t *) state->sasl_conn,
+ STR(state->sasl_decoded), enc_length,
+ NO_SASL_INTERACTION, &clientout, &clientoutlen);
+ if (result != SASL_OK && result != SASL_CONTINUE)
+ msg_warn("SASL authentication failed to server %s: %s",
+ state->session->namaddr,
+ sasl_errstring(result, NO_SASL_LANGLIST,
+ NO_SASL_OUTLANG));
+
+ /*
+ * Send a client response.
+ */
+ if (clientoutlen > 0) {
+ if (msg_verbose)
+ msg_info("%s: %s: uncoded client response %.*s",
+ myname, state->session->namaddr,
+ (int) clientoutlen, clientout);
+ enc_length = ENCODE64_LENGTH(clientoutlen) + 1;
+ VSTRING_SPACE(state->sasl_encoded, enc_length);
+ if (sasl_encode64(clientout, clientoutlen,
+ STR(state->sasl_encoded), enc_length,
+ &enc_length_out) != SASL_OK)
+ msg_panic("%s: sasl_encode64 botch", myname);
+ free(clientout);
+ } else {
+ vstring_strcat(state->sasl_encoded, "");
+ }
+ lmtp_chat_cmd(state, "%s", STR(state->sasl_encoded));
+ }
+
+ /*
+ * We completed the authentication protocol.
+ */
+ if (resp->code / 100 != 2) {
+ vstring_sprintf(why, "SASL authentication failed; server %s said: %s",
+ state->session->namaddr, resp->str);
+ return (0);
+ }
+ return (1);
+}
+
+/* lmtp_sasl_cleanup - per-session cleanup */
+
+void lmtp_sasl_cleanup(LMTP_STATE *state)
+{
+ if (state->sasl_username) {
+ myfree(state->sasl_username);
+ state->sasl_username = 0;
+ }
+ if (state->sasl_passwd) {
+ myfree(state->sasl_passwd);
+ state->sasl_passwd = 0;
+ }
+ if (state->sasl_mechanism_list) {
+ myfree(state->sasl_mechanism_list); /* allocated in lmtp_helo */
+ state->sasl_mechanism_list = 0;
+ }
+ if (state->sasl_conn) {
+ if (msg_verbose)
+ msg_info("disposing SASL state information");
+ sasl_dispose(&state->sasl_conn);
+ }
+ if (state->sasl_callbacks) {
+ myfree((char *) state->sasl_callbacks);
+ state->sasl_callbacks = 0;
+ }
+ if (state->sasl_encoded) {
+ vstring_free(state->sasl_encoded);
+ state->sasl_encoded = 0;
+ }
+ if (state->sasl_decoded) {
+ vstring_free(state->sasl_decoded);
+ state->sasl_decoded = 0;
+ }
+}
+
+#endif
--- /dev/null
+/*++
+/* NAME
+/* lmtp_sasl_proto 3
+/* SUMMARY
+/* Postfix SASL interface for LMTP client
+/* SYNOPSIS
+/* #include lmtp_sasl.h
+/*
+/* void lmtp_sasl_helo_auth(state, words)
+/* LMTP_STATE *state;
+/* const char *words;
+/*
+/* int lmtp_sasl_helo_login(state)
+/* LMTP_STATE *state;
+/* DESCRIPTION
+/* This module contains random chunks of code that implement
+/* the SMTP protocol interface for SASL negotiation. The goal
+/* is to reduce clutter in the main LMTP client source code.
+/*
+/* lmtp_sasl_helo_auth() processes the AUTH option in the
+/* SMTP server's EHLO response.
+/*
+/* lmtp_sasl_helo_login() authenticates the LMTP client to the
+/* SMTP server, using the authentication mechanism information
+/* given by the server. The result is a Postfix delivery status
+/* code in case of trouble.
+/*
+/* Arguments:
+/* .IP state
+/* Session context.
+/* .IP words
+/* List of SASL authentication mechanisms (separated by blanks)
+/* DIAGNOSTICS
+/* All errors are fatal.
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Original author:
+/* Till Franke
+/* SuSE Rhein/Main AG
+/* 65760 Eschborn, Germany
+/*
+/* Adopted by:
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+
+/* Global library. */
+
+#include <mail_params.h>
+
+/* Application-specific. */
+
+#include "lmtp.h"
+#include "lmtp_sasl.h"
+
+#ifdef USE_SASL_AUTH
+
+/* lmtp_sasl_helo_auth - handle AUTH option in EHLO reply */
+
+void lmtp_sasl_helo_auth(LMTP_STATE *state, const char *words)
+{
+
+ /*
+ * XXX If the server offers a null list of authentication mechanisms,
+ * then pretend that the server doesn't support SASL authentication.
+ */
+ if (state->sasl_mechanism_list) {
+ myfree(state->sasl_mechanism_list);
+ msg_warn("%s offered AUTH option multiple times",
+ state->session->namaddr);
+ state->sasl_mechanism_list = 0;
+ state->features &= ~SMTP_FEATURE_AUTH;
+ }
+ if (strlen(words) > 0) {
+ state->sasl_mechanism_list = mystrdup(words);
+ state->features |= SMTP_FEATURE_AUTH;
+ } else {
+ msg_warn("%s offered null AUTH mechanism list",
+ state->session->namaddr);
+ }
+}
+
+/* lmtp_sasl_helo_login - perform SASL login */
+
+int lmtp_sasl_helo_login(LMTP_STATE *state)
+{
+ VSTRING *why = vstring_alloc(10);
+ int ret = 0;
+
+ /*
+ * Skip authentication when no authentication info exists for this
+ * server, so that we talk to each other like strangers. Otherwise, if
+ * authentication information exists, assume that authentication is
+ * required, and assume that an authentication error is recoverable.
+ */
+ if (lmtp_sasl_passwd_lookup(state) != 0) {
+ lmtp_sasl_start(state);
+ if (lmtp_sasl_authenticate(state, why) <= 0)
+ ret = lmtp_site_fail(state, 450, "Authentication failed: %s",
+ vstring_str(why));
+ }
+ vstring_free(why);
+ return (ret);
+}
+
+#endif
/* Global library. */
-#include <config.h>
+#include <mail_conf.h>
/* Application-specific. */
#include "lmtp.h"
+#include "lmtp_sasl.h"
/* lmtp_state_alloc - initialize */
state->features = 0;
state->history = 0;
state->error_mask = 0;
+#ifdef USE_SASL_AUTH
+ lmtp_sasl_connect(state);
+#endif
state->sndbufsize = 0;
state->sndbuffree = 0;
state->reuse = 0;
vstring_free(state->buffer);
vstring_free(state->scratch);
vstring_free(state->scratch2);
+#ifdef USE_SASL_AUTH
+ lmtp_sasl_cleanup(state);
+#endif
myfree((char *) state);
}
/* RECIPIENT *recipient;
/* char *format;
/*
-/* int lmtp_stream_except(state, exception, description)
+/* int smtp_stream_except(state, exception, description)
/* LMTP_STATE *state;
/* int exception;
/* char *description;
/* recipient limit is reached. The policy is: soft error: defer
/* delivery to this recipient; hard error: bounce this recipient.
/*
-/* lmtp_stream_except() handles the exceptions generated by
+/* smtp_stream_except() handles the exceptions generated by
/* the smtp_stream(3) module (i.e. timeouts and I/O errors).
/* The \fIexception\fR argument specifies the type of problem.
/* The \fIdescription\fR argument describes at what stage of
state->status |= status;
}
-/* lmtp_stream_except - defer domain after I/O problem */
+/* smtp_stream_except - defer domain after I/O problem */
-int lmtp_stream_except(LMTP_STATE *state, int code, char *description)
+int smtp_stream_except(LMTP_STATE *state, int code, char *description)
{
DELIVER_REQUEST *request = state->request;
LMTP_SESSION *session = state->session;
*/
switch (code) {
default:
- msg_panic("lmtp_stream_except: unknown exception %d", code);
+ msg_panic("smtp_stream_except: unknown exception %d", code);
case SMTP_ERR_EOF:
vstring_sprintf(why, "lost connection with %s while %s",
session->namaddr, description);
+++ /dev/null
-../../.indent.pro
\ No newline at end of file
+++ /dev/null
-been_here_xt 2 0
-bounce_append 5 0
-cleanup_out_format 1 0
-defer_append 5 0
-mail_command 1 0
-mail_print 1 0
-msg_error 0 0
-msg_fatal 0 0
-msg_info 0 0
-msg_panic 0 0
-msg_warn 0 0
-opened 4 0
-post_mail_fprintf 1 0
-qmgr_message_bounce 2 0
-rec_fprintf 2 0
-sent 4 0
-smtp_cmd 1 0
-smtp_mesg_fail 2 0
-smtp_printf 1 0
-smtp_rcpt_fail 3 0
-smtp_site_fail 2 0
-udp_syslog 1 0
-vstream_fprintf 1 0
-vstream_printf 0 0
-vstring_sprintf 1 0
+++ /dev/null
-SHELL = /bin/sh
-SRCS = qmgr.c qmgr_active.c qmgr_transport.c qmgr_queue.c qmgr_entry.c \
- qmgr_message.c qmgr_deliver.c qmgr_move.c qmgr_rcpt_list.c \
- qmgr_job.c qmgr_peer.c \
- qmgr_defer.c qmgr_enable.c qmgr_scan.c qmgr_bounce.c
-OBJS = qmgr.o qmgr_active.o qmgr_transport.o qmgr_queue.o qmgr_entry.o \
- qmgr_message.o qmgr_deliver.o qmgr_move.o qmgr_rcpt_list.o \
- qmgr_job.o qmgr_peer.o \
- qmgr_defer.o qmgr_enable.o qmgr_scan.o qmgr_bounce.o
-HDRS = qmgr.h
-TESTSRC =
-WARN = -W -Wformat -Wimplicit -Wmissing-prototypes \
- -Wparentheses -Wstrict-prototypes -Wswitch -Wuninitialized \
- -Wunused
-DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE)
-CFLAGS = $(DEBUG) $(OPT) $(DEFS)
-TESTPROG=
-PROG = qmgr
-INC_DIR = ../../include
-LIBS = ../../lib/libmaster.a ../../lib/libglobal.a ../../lib/libutil.a
-
-.c.o:; $(CC) $(CFLAGS) -c $*.c
-
-$(PROG): $(OBJS) $(LIBS)
- $(CC) $(CFLAGS) -o $@ $(OBJS) $(LIBS) $(SYSLIBS)
-
-Makefile: Makefile.in
- (set -e; echo "# DO NOT EDIT"; $(OPTS) $(SHELL) ../../makedefs && cat $?) >$@
-
-test: $(TESTPROG)
-
-update: ../../libexec/n$(PROG)
-
-../../libexec/n$(PROG): $(PROG)
- cp $(PROG) ../../libexec/n$(PROG)
-
-printfck: $(OBJS) $(PROG)
- rm -rf printfck
- mkdir printfck
- cp *.h printfck
- sed '1,/^# do not edit/!d' Makefile >printfck/Makefile
- set -e; for i in *.c; do printfck -f .printfck $$i >printfck/$$i; done
- cd printfck; make "INC_DIR=../../../include" `cd ..; ls *.o`
-
-lint:
- lint $(DEFS) $(SRCS) $(LINTFIX)
-
-clean:
- rm -f *.o *core $(PROG) $(TESTPROG) junk
- rm -rf printfck
-
-tidy: clean
-
-depend: $(MAKES)
- (sed '1,/^# do not edit/!d' Makefile.in; \
- set -e; for i in [a-z][a-z0-9]*.c; do \
- $(CC) -E $(DEFS) $(INCL) $$i | sed -n -e '/^# *1 *"\([^"]*\)".*/{' \
- -e 's//'`echo $$i|sed 's/c$$/o/'`': \1/' -e 'p' -e '}'; \
- done) | grep -v '[.][o][:][ ][/]' >$$$$ && mv $$$$ Makefile.in
- @$(EXPORT) make -f Makefile.in Makefile 1>&2
-
-# do not edit below this line - it is generated by 'make depend'
-qmgr.o: qmgr.c
-qmgr.o: ../../include/sys_defs.h
-qmgr.o: ../../include/msg.h
-qmgr.o: ../../include/events.h
-qmgr.o: ../../include/vstream.h
-qmgr.o: ../../include/vbuf.h
-qmgr.o: ../../include/dict.h
-qmgr.o: ../../include/argv.h
-qmgr.o: ../../include/mail_queue.h
-qmgr.o: ../../include/vstring.h
-qmgr.o: ../../include/recipient_list.h
-qmgr.o: ../../include/mail_conf.h
-qmgr.o: ../../include/mail_params.h
-qmgr.o: ../../include/mail_proto.h
-qmgr.o: ../../include/iostuff.h
-qmgr.o: ../../include/master_proto.h
-qmgr.o: ../../include/mail_server.h
-qmgr.o: qmgr.h
-qmgr.o: ../../include/scan_dir.h
-qmgr.o: ../../include/maps.h
-qmgr_active.o: qmgr_active.c
-qmgr_active.o: ../../include/sys_defs.h
-qmgr_active.o: ../../include/msg.h
-qmgr_active.o: ../../include/events.h
-qmgr_active.o: ../../include/mymalloc.h
-qmgr_active.o: ../../include/vstream.h
-qmgr_active.o: ../../include/vbuf.h
-qmgr_active.o: ../../include/mail_params.h
-qmgr_active.o: ../../include/mail_open_ok.h
-qmgr_active.o: ../../include/mail_queue.h
-qmgr_active.o: ../../include/vstring.h
-qmgr_active.o: ../../include/recipient_list.h
-qmgr_active.o: ../../include/bounce.h
-qmgr_active.o: ../../include/defer.h
-qmgr_active.o: ../../include/abounce.h
-qmgr_active.o: ../../include/rec_type.h
-qmgr_active.o: qmgr.h
-qmgr_active.o: ../../include/scan_dir.h
-qmgr_active.o: ../../include/maps.h
-qmgr_active.o: ../../include/dict.h
-qmgr_active.o: ../../include/argv.h
-qmgr_bounce.o: qmgr_bounce.c
-qmgr_bounce.o: ../../include/sys_defs.h
-qmgr_bounce.o: ../../include/bounce.h
-qmgr_bounce.o: ../../include/deliver_completed.h
-qmgr_bounce.o: ../../include/vstream.h
-qmgr_bounce.o: ../../include/vbuf.h
-qmgr_bounce.o: qmgr.h
-qmgr_bounce.o: ../../include/scan_dir.h
-qmgr_bounce.o: ../../include/maps.h
-qmgr_bounce.o: ../../include/dict.h
-qmgr_bounce.o: ../../include/argv.h
-qmgr_defer.o: qmgr_defer.c
-qmgr_defer.o: ../../include/sys_defs.h
-qmgr_defer.o: ../../include/msg.h
-qmgr_defer.o: ../../include/vstream.h
-qmgr_defer.o: ../../include/vbuf.h
-qmgr_defer.o: ../../include/defer.h
-qmgr_defer.o: ../../include/bounce.h
-qmgr_defer.o: qmgr.h
-qmgr_defer.o: ../../include/scan_dir.h
-qmgr_defer.o: ../../include/maps.h
-qmgr_defer.o: ../../include/dict.h
-qmgr_defer.o: ../../include/argv.h
-qmgr_deliver.o: qmgr_deliver.c
-qmgr_deliver.o: ../../include/sys_defs.h
-qmgr_deliver.o: ../../include/msg.h
-qmgr_deliver.o: ../../include/vstring.h
-qmgr_deliver.o: ../../include/vbuf.h
-qmgr_deliver.o: ../../include/vstream.h
-qmgr_deliver.o: ../../include/vstring_vstream.h
-qmgr_deliver.o: ../../include/events.h
-qmgr_deliver.o: ../../include/iostuff.h
-qmgr_deliver.o: ../../include/mail_queue.h
-qmgr_deliver.o: ../../include/mail_proto.h
-qmgr_deliver.o: ../../include/recipient_list.h
-qmgr_deliver.o: ../../include/mail_params.h
-qmgr_deliver.o: ../../include/deliver_request.h
-qmgr_deliver.o: qmgr.h
-qmgr_deliver.o: ../../include/scan_dir.h
-qmgr_deliver.o: ../../include/maps.h
-qmgr_deliver.o: ../../include/dict.h
-qmgr_deliver.o: ../../include/argv.h
-qmgr_enable.o: qmgr_enable.c
-qmgr_enable.o: ../../include/sys_defs.h
-qmgr_enable.o: ../../include/msg.h
-qmgr_enable.o: ../../include/vstream.h
-qmgr_enable.o: ../../include/vbuf.h
-qmgr_enable.o: qmgr.h
-qmgr_enable.o: ../../include/scan_dir.h
-qmgr_enable.o: ../../include/maps.h
-qmgr_enable.o: ../../include/dict.h
-qmgr_enable.o: ../../include/argv.h
-qmgr_entry.o: qmgr_entry.c
-qmgr_entry.o: ../../include/sys_defs.h
-qmgr_entry.o: ../../include/msg.h
-qmgr_entry.o: ../../include/mymalloc.h
-qmgr_entry.o: ../../include/events.h
-qmgr_entry.o: ../../include/vstream.h
-qmgr_entry.o: ../../include/vbuf.h
-qmgr_entry.o: ../../include/mail_params.h
-qmgr_entry.o: qmgr.h
-qmgr_entry.o: ../../include/scan_dir.h
-qmgr_entry.o: ../../include/maps.h
-qmgr_entry.o: ../../include/dict.h
-qmgr_entry.o: ../../include/argv.h
-qmgr_job.o: qmgr_job.c
-qmgr_job.o: ../../include/sys_defs.h
-qmgr_job.o: ../../include/msg.h
-qmgr_job.o: ../../include/events.h
-qmgr_job.o: ../../include/htable.h
-qmgr_job.o: ../../include/mymalloc.h
-qmgr_job.o: qmgr.h
-qmgr_job.o: ../../include/vstream.h
-qmgr_job.o: ../../include/vbuf.h
-qmgr_job.o: ../../include/scan_dir.h
-qmgr_job.o: ../../include/maps.h
-qmgr_job.o: ../../include/dict.h
-qmgr_job.o: ../../include/argv.h
-qmgr_message.o: qmgr_message.c
-qmgr_message.o: ../../include/sys_defs.h
-qmgr_message.o: ../../include/msg.h
-qmgr_message.o: ../../include/events.h
-qmgr_message.o: ../../include/mymalloc.h
-qmgr_message.o: ../../include/vstring.h
-qmgr_message.o: ../../include/vbuf.h
-qmgr_message.o: ../../include/vstream.h
-qmgr_message.o: ../../include/split_at.h
-qmgr_message.o: ../../include/valid_hostname.h
-qmgr_message.o: ../../include/argv.h
-qmgr_message.o: ../../include/stringops.h
-qmgr_message.o: ../../include/myflock.h
-qmgr_message.o: ../../include/dict.h
-qmgr_message.o: ../../include/mail_queue.h
-qmgr_message.o: ../../include/mail_params.h
-qmgr_message.o: ../../include/canon_addr.h
-qmgr_message.o: ../../include/record.h
-qmgr_message.o: ../../include/rec_type.h
-qmgr_message.o: ../../include/sent.h
-qmgr_message.o: ../../include/deliver_completed.h
-qmgr_message.o: ../../include/mail_addr_find.h
-qmgr_message.o: ../../include/maps.h
-qmgr_message.o: ../../include/opened.h
-qmgr_message.o: ../../include/resolve_local.h
-qmgr_message.o: ../../include/resolve_clnt.h
-qmgr_message.o: qmgr.h
-qmgr_message.o: ../../include/scan_dir.h
-qmgr_move.o: qmgr_move.c
-qmgr_move.o: ../../include/sys_defs.h
-qmgr_move.o: ../../include/msg.h
-qmgr_move.o: ../../include/scan_dir.h
-qmgr_move.o: ../../include/recipient_list.h
-qmgr_move.o: ../../include/mail_queue.h
-qmgr_move.o: ../../include/vstring.h
-qmgr_move.o: ../../include/vbuf.h
-qmgr_move.o: ../../include/vstream.h
-qmgr_move.o: ../../include/mail_scan_dir.h
-qmgr_move.o: qmgr.h
-qmgr_move.o: ../../include/maps.h
-qmgr_move.o: ../../include/dict.h
-qmgr_move.o: ../../include/argv.h
-qmgr_peer.o: qmgr_peer.c
-qmgr_peer.o: ../../include/sys_defs.h
-qmgr_peer.o: ../../include/msg.h
-qmgr_peer.o: ../../include/htable.h
-qmgr_peer.o: ../../include/mymalloc.h
-qmgr_peer.o: qmgr.h
-qmgr_peer.o: ../../include/vstream.h
-qmgr_peer.o: ../../include/vbuf.h
-qmgr_peer.o: ../../include/scan_dir.h
-qmgr_peer.o: ../../include/maps.h
-qmgr_peer.o: ../../include/dict.h
-qmgr_peer.o: ../../include/argv.h
-qmgr_queue.o: qmgr_queue.c
-qmgr_queue.o: ../../include/sys_defs.h
-qmgr_queue.o: ../../include/msg.h
-qmgr_queue.o: ../../include/mymalloc.h
-qmgr_queue.o: ../../include/events.h
-qmgr_queue.o: ../../include/htable.h
-qmgr_queue.o: ../../include/mail_params.h
-qmgr_queue.o: ../../include/recipient_list.h
-qmgr_queue.o: qmgr.h
-qmgr_queue.o: ../../include/vstream.h
-qmgr_queue.o: ../../include/vbuf.h
-qmgr_queue.o: ../../include/scan_dir.h
-qmgr_queue.o: ../../include/maps.h
-qmgr_queue.o: ../../include/dict.h
-qmgr_queue.o: ../../include/argv.h
-qmgr_rcpt_list.o: qmgr_rcpt_list.c
-qmgr_rcpt_list.o: ../../include/sys_defs.h
-qmgr_rcpt_list.o: ../../include/mymalloc.h
-qmgr_rcpt_list.o: qmgr.h
-qmgr_rcpt_list.o: ../../include/vstream.h
-qmgr_rcpt_list.o: ../../include/vbuf.h
-qmgr_rcpt_list.o: ../../include/scan_dir.h
-qmgr_rcpt_list.o: ../../include/maps.h
-qmgr_rcpt_list.o: ../../include/dict.h
-qmgr_rcpt_list.o: ../../include/argv.h
-qmgr_scan.o: qmgr_scan.c
-qmgr_scan.o: ../../include/sys_defs.h
-qmgr_scan.o: ../../include/msg.h
-qmgr_scan.o: ../../include/mymalloc.h
-qmgr_scan.o: ../../include/scan_dir.h
-qmgr_scan.o: ../../include/mail_scan_dir.h
-qmgr_scan.o: qmgr.h
-qmgr_scan.o: ../../include/vstream.h
-qmgr_scan.o: ../../include/vbuf.h
-qmgr_scan.o: ../../include/maps.h
-qmgr_scan.o: ../../include/dict.h
-qmgr_scan.o: ../../include/argv.h
-qmgr_transport.o: qmgr_transport.c
-qmgr_transport.o: ../../include/sys_defs.h
-qmgr_transport.o: ../../include/msg.h
-qmgr_transport.o: ../../include/htable.h
-qmgr_transport.o: ../../include/events.h
-qmgr_transport.o: ../../include/mymalloc.h
-qmgr_transport.o: ../../include/vstream.h
-qmgr_transport.o: ../../include/vbuf.h
-qmgr_transport.o: ../../include/iostuff.h
-qmgr_transport.o: ../../include/mail_proto.h
-qmgr_transport.o: ../../include/recipient_list.h
-qmgr_transport.o: ../../include/mail_conf.h
-qmgr_transport.o: ../../include/mail_params.h
-qmgr_transport.o: qmgr.h
-qmgr_transport.o: ../../include/scan_dir.h
-qmgr_transport.o: ../../include/maps.h
-qmgr_transport.o: ../../include/dict.h
-qmgr_transport.o: ../../include/argv.h
+++ /dev/null
-/*++
-/* NAME
-/* qmgr 8
-/* SUMMARY
-/* Postfix queue manager
-/* SYNOPSIS
-/* \fBqmgr\fR [generic Postfix daemon options]
-/* DESCRIPTION
-/* The \fBqmgr\fR daemon awaits the arrival of incoming mail
-/* and arranges for its delivery via Postfix delivery processes.
-/* The actual mail routing strategy is delegated to the
-/* \fBtrivial-rewrite\fR(8) daemon.
-/* This program expects to be run from the \fBmaster\fR(8) process
-/* manager.
-/*
-/* Mail addressed to the local \fBdouble-bounce\fR address is silently
-/* discarded. This stops potential loops caused by undeliverable
-/* bounce notifications.
-/*
-/* Mail addressed to a user listed in the optional \fBrelocated\fR
-/* database is bounced with a "user has moved to \fInew_location\fR"
-/* message. See \fBrelocated\fR(5) for a precise description.
-/* MAIL QUEUES
-/* .ad
-/* .fi
-/* The \fBqmgr\fR daemon maintains the following queues:
-/* .IP \fBincoming\fR
-/* Inbound mail from the network, or mail picked up by the
-/* local \fBpickup\fR agent from the \fBmaildrop\fR directory.
-/* .IP \fBactive\fR
-/* Messages that the queue manager has opened for delivery. Only
-/* a limited number of messages is allowed to enter the \fBactive\fR
-/* queue (leaky bucket strategy, for a fixed delivery rate).
-/* .IP \fBdeferred\fR
-/* Mail that could not be delivered upon the first attempt. The queue
-/* manager implements exponential backoff by doubling the time between
-/* delivery attempts.
-/* .IP \fBcorrupt\fR
-/* Unreadable or damaged queue files are moved here for inspection.
-/* DELIVERY STATUS REPORTS
-/* .ad
-/* .fi
-/* The \fBqmgr\fR daemon keeps an eye on per-message delivery status
-/* reports in the following directories. Each status report file has
-/* the same name as the corresponding message file:
-/* .IP \fBbounce\fR
-/* Per-recipient status information about why mail is bounced.
-/* These files are maintained by the \fBbounce\fR(8) daemon.
-/* .IP \fBdefer\fR
-/* Per-recipient status information about why mail is delayed.
-/* These files are maintained by the \fBdefer\fR(8) daemon.
-/* .PP
-/* The \fBqmgr\fR daemon is responsible for asking the
-/* \fBbounce\fR(8) or \fBdefer\fR(8) daemons to send non-delivery
-/* reports.
-/* STRATEGIES
-/* .ad
-/* .fi
-/* The queue manager implements a variety of strategies for
-/* either opening queue files (input) or for message delivery (output).
-/* .IP "\fBleaky bucket\fR"
-/* This strategy limits the number of messages in the \fBactive\fR queue
-/* and prevents the queue manager from running out of memory under
-/* heavy load.
-/* .IP \fBfairness\fR
-/* When the \fBactive\fR queue has room, the queue manager takes one
-/* message from the \fBincoming\fR queue and one from the \fBdeferred\fR
-/* queue. This prevents a large mail backlog from blocking the delivery
-/* of new mail.
-/* .IP "\fBslow start\fR"
-/* This strategy eliminates "thundering herd" problems by slowly
-/* adjusting the number of parallel deliveries to the same destination.
-/* .IP "\fBround robin\fR
-/* The queue manager sorts delivery requests by destination.
-/* Round-robin selection prevents one destination from dominating
-/* deliveries to other destinations.
-/* .IP "\fBexponential backoff\fR"
-/* Mail that cannot be delivered upon the first attempt is deferred.
-/* The time interval between delivery attempts is doubled after each
-/* attempt.
-/* .IP "\fBdestination status cache\fR"
-/* The queue manager avoids unnecessary delivery attempts by
-/* maintaining a short-term, in-memory list of unreachable destinations.
-/* .IP "\fBpreemptive message scheduling\fR"
-/* The queue manager attempts to minimize the average per-recipient delay
-/* while still preserving the correct per-message delays, using
-/* a sophisticated preemptive message scheduling.
-/* TRIGGERS
-/* .ad
-/* .fi
-/* On an idle system, the queue manager waits for the arrival of
-/* trigger events, or it waits for a timer to go off. A trigger
-/* is a one-byte message.
-/* Depending on the message received, the queue manager performs
-/* one of the following actions (the message is followed by the
-/* symbolic constant used internally by the software):
-/* .IP "\fBD (QMGR_REQ_SCAN_DEFERRED)\fR"
-/* Start a deferred queue scan. If a deferred queue scan is already
-/* in progress, that scan will be restarted as soon as it finishes.
-/* .IP "\fBI (QMGR_REQ_SCAN_INCOMING)\fR"
-/* Start an incoming queue scan. If an incoming queue scan is already
-/* in progress, that scan will be restarted as soon as it finishes.
-/* .IP "\fBA (QMGR_REQ_SCAN_ALL)\fR"
-/* Ignore deferred queue file time stamps. The request affects
-/* the next deferred queue scan.
-/* .IP "\fBF (QMGR_REQ_FLUSH_DEAD)\fR"
-/* Purge all information about dead transports and destinations.
-/* .IP "\fBW (TRIGGER_REQ_WAKEUP)\fR"
-/* Wakeup call, This is used by the master server to instantiate
-/* servers that should not go away forever. The action is to start
-/* an incoming queue scan.
-/* .PP
-/* The \fBqmgr\fR daemon reads an entire buffer worth of triggers.
-/* Multiple identical trigger requests are collapsed into one, and
-/* trigger requests are sorted so that \fBA\fR and \fBF\fR precede
-/* \fBD\fR and \fBI\fR. Thus, in order to force a deferred queue run,
-/* one would request \fBA F D\fR; in order to notify the queue manager
-/* of the arrival of new mail one would request \fBI\fR.
-/* STANDARDS
-/* .ad
-/* .fi
-/* None. The \fBqmgr\fR daemon does not interact with the outside world.
-/* SECURITY
-/* .ad
-/* .fi
-/* The \fBqmgr\fR daemon is not security sensitive. It reads
-/* single-character messages from untrusted local users, and thus may
-/* be susceptible to denial of service attacks. The \fBqmgr\fR daemon
-/* does not talk to the outside world, and it can be run at fixed low
-/* privilege in a chrooted environment.
-/* DIAGNOSTICS
-/* Problems and transactions are logged to the syslog daemon.
-/* Corrupted message files are saved to the \fBcorrupt\fR queue
-/* for further inspection.
-/*
-/* Depending on the setting of the \fBnotify_classes\fR parameter,
-/* the postmaster is notified of bounces and of other trouble.
-/* BUGS
-/* A single queue manager process has to compete for disk access with
-/* multiple front-end processes such as \fBsmtpd\fR. A sudden burst of
-/* inbound mail can negatively impact outbound delivery rates.
-/* CONFIGURATION PARAMETERS
-/* .ad
-/* .fi
-/* The following \fBmain.cf\fR parameters are especially relevant to
-/* this program. See the Postfix \fBmain.cf\fR file for syntax details
-/* and for default values. Use the \fBpostfix reload\fR command after
-/* a configuration change.
-/* .SH Miscellaneous
-/* .ad
-/* .fi
-/* .IP \fBallow_min_user\fR
-/* Do not bounce recipient addresses that begin with '-'.
-/* .IP \fBrelocated_maps\fR
-/* Tables with contact information for users, hosts or domains
-/* that no longer exist. See \fBrelocated\fR(5).
-/* .IP \fBqueue_directory\fR
-/* Top-level directory of the Postfix queue.
-/* .SH "Active queue controls"
-/* .ad
-/* .fi
-/* In the text below, \fItransport\fR is the first field in a
-/* \fBmaster.cf\fR entry.
-/* .IP \fBqmgr_message_active_limit\fR
-/* Limit the number of messages in the active queue.
-/* .IP \fBqmgr_message_recipient_limit\fR
-/* Limit the number of in-memory recipients.
-/* .sp
-/* This parameter also limits the size of the short-term, in-memory
-/* destination cache.
-/* .IP \fBqmgr_message_recipient_minimum\fR
-/* Per message minimum of in-memory recipients.
-/* .IP \fBdefault_recipient_limit\fR
-/* Default limit on the number of in-memory recipients per transport.
-/* .IP \fItransport\fB_recipient_limit\fR
-/* Limit on the number of in-memory recipients, for the named
-/* message \fItransport\fR.
-/* .IP \fBdefault_extra_recipient_limit\fR
-/* Default limit on the total number of per transport in-memory
-/* recipients that the preempting messages can have.
-/* .IP \fItransport\fB_extra_recipient_limit\fR
-/* Limit on the number of in-memory recipients which all preempting
-/* messages delivered by the transport \fItransport\fR can have.
-/* .SH "Timing controls"
-/* .ad
-/* .fi
-/* .IP \fBmin_backoff\fR
-/* Minimal time in seconds between delivery attempts
-/* of a deferred message.
-/* .sp
-/* This parameter also limits the time an unreachable destination
-/* is kept in the short-term, in-memory destination status cache.
-/* .IP \fBmax_backoff\fR
-/* Maximal time in seconds between delivery attempts
-/* of a deferred message.
-/* .IP \fBmaximal_queue_lifetime\fR
-/* Maximal time in days a message is queued
-/* before it is sent back as undeliverable.
-/* .IP \fBqueue_run_delay\fR
-/* Time in seconds between deferred queue scans. Queue scans do
-/* not overlap.
-/* .IP \fBtransport_retry_time\fR
-/* Time in seconds between attempts to contact a broken
-/* delivery transport.
-/* .SH "Concurrency controls"
-/* .ad
-/* .fi
-/* .IP \fBinitial_destination_concurrency\fR
-/* Initial per-destination concurrency level for parallel delivery
-/* to the same destination.
-/* .IP \fBdefault_destination_concurrency_limit\fR
-/* Default limit on the number of parallel deliveries to the same
-/* destination.
-/* .IP \fItransport\fB_destination_concurrency_limit\fR
-/* Limit on the number of parallel deliveries to the same destination,
-/* for delivery via the named message \fItransport\fR.
-/* .SH "Recipient controls"
-/* .ad
-/* .fi
-/* .IP \fBdefault_destination_recipient_limit\fR
-/* Default limit on the number of recipients per message transfer.
-/* .IP \fItransport\fB_destination_recipient_limit\fR
-/* Limit on the number of recipients per message transfer, for the
-/* named message \fItransport\fR.
-/* .SH "Message scheduling"
-/* .ad
-/* .fi
-/* .IP "\fItransport\fB_delivery_slot_cost\fR (valid range: 0,2,3...)
-/* This parameter basically controls how often a message
-/* delivered by \fItransport\fR can be preempted by another
-/* message.
-/* An internal per-message/transport counter is incremented by one
-/* for each \fItransport\fB_delivery_slot_cost\fR
-/* deliveries handled by \fItransport\fR. This counter represents
-/* the number of "available delivery slots" for use by other messages.
-/* Current message can be preempted by another message when that
-/* other message can be delivered using less \fItransport\fR agents
-/* than the value of the "available delivery slots" counter.
-/* .sp
-/* Value equal to 0 disables the message preemption for \fItransport\fR.
-/* .IP \fItransport\fB_minimum_delivery_slots\fR
-/* Message preemption is not attempted at all whenever a message
-/* that can't ever accumulate at least \fItransport\fB_minimum_delivery_slots\fR
-/* available delivery slots is being delivered by \fItransport\fR.
-/* .IP "\fItransport\fB_delivery_slot_discount\fR (valid range: 0..100)"
-/* .IP \fItransport\fB_delivery_slot_loan\fR
-/* These parameters speed up the moment when a message preemption can happen.
-/* Instead of waiting until the full amount of delivery slots
-/* required is available, the preemption can happen when
-/* \fItransport\fB_delivery_slot_discount\fR percent of the required
-/* amount plus \fItransport\fB_delivery_slot_loan\fR still remains to
-/* be accumulated. Note that the full amount will still have to be
-/* accumulated before another preemption can take place later.
-/* .IP \fBdefault_delivery_slot_cost\fR
-/* .IP \fBdefault_minimum_delivery_slots\fR
-/* .IP \fBdefault_delivery_slot_discount\fR
-/* .IP \fBdefault_delivery_slot_loan\fR
-/* Default values for the transport specific parameters described above.
-/* SEE ALSO
-/* master(8), process manager
-/* relocated(5), format of the "user has moved" table
-/* syslogd(8) system logging
-/* trivial-rewrite(8), address routing
-/* 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
-/*
-/* Scheduler enhancements:
-/* Patrik Rak
-/* Modra 6
-/* 155 00, Prague, Czech Republic
-/*--*/
-
-/* System library. */
-
-#include <sys_defs.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <ctype.h>
-
-/* Utility library. */
-
-#include <msg.h>
-#include <events.h>
-#include <vstream.h>
-#include <dict.h>
-
-/* Global library. */
-
-#include <mail_queue.h>
-#include <recipient_list.h>
-#include <mail_conf.h>
-#include <mail_params.h>
-#include <mail_proto.h> /* QMGR_SCAN constants */
-
-/* Master process interface */
-
-#include <master_proto.h>
-#include <mail_server.h>
-
-/* Application-specific. */
-
-#include "qmgr.h"
-
- /*
- * Tunables.
- */
-int var_queue_run_delay;
-int var_min_backoff_time;
-int var_max_backoff_time;
-int var_max_queue_time;
-int var_qmgr_active_limit;
-int var_qmgr_rcpt_limit;
-int var_qmgr_msg_rcpt_limit;
-int var_xport_rcpt_limit;
-int var_stack_rcpt_limit;
-int var_delivery_slot_cost;
-int var_delivery_slot_loan;
-int var_delivery_slot_discount;
-int var_min_delivery_slots;
-int var_init_dest_concurrency;
-int var_transport_retry_time;
-int var_dest_con_limit;
-int var_dest_rcpt_limit;
-char *var_relocated_maps;
-char *var_virtual_maps;
-char *var_defer_xports;
-bool var_allow_min_user;
-
-static QMGR_SCAN *qmgr_incoming;
-static QMGR_SCAN *qmgr_deferred;
-
-MAPS *qmgr_relocated;
-MAPS *qmgr_virtual;
-
-/* qmgr_deferred_run_event - queue manager heartbeat */
-
-static void qmgr_deferred_run_event(int unused_event, char *dummy)
-{
-
- /*
- * This routine runs when it is time for another deferred queue scan.
- * Make sure this routine gets called again in the future.
- */
- qmgr_scan_request(qmgr_deferred, QMGR_SCAN_START);
- event_request_timer(qmgr_deferred_run_event, dummy, var_queue_run_delay);
-}
-
-/* qmgr_trigger_event - respond to external trigger(s) */
-
-static void qmgr_trigger_event(char *buf, int len,
- char *unused_service, char **argv)
-{
- int incoming_flag = 0;
- int deferred_flag = 0;
- int i;
-
- /*
- * Sanity check. This service takes no command-line arguments.
- */
- if (argv[0])
- msg_fatal("unexpected command-line argument: %s", argv[0]);
-
- /*
- * Collapse identical requests that have arrived since we looked last
- * time. There is no client feedback so there is no need to process each
- * request in order. And as long as we don't have conflicting requests we
- * are free to sort them into the most suitable order.
- */
- for (i = 0; i < len; i++) {
- if (msg_verbose)
- msg_info("request: %d (%c)",
- buf[i], ISALNUM(buf[i]) ? buf[i] : '?');
- switch (buf[i]) {
- case TRIGGER_REQ_WAKEUP:
- case QMGR_REQ_SCAN_INCOMING:
- incoming_flag |= QMGR_SCAN_START;
- break;
- case QMGR_REQ_SCAN_DEFERRED:
- deferred_flag |= QMGR_SCAN_START;
- break;
- case QMGR_REQ_FLUSH_DEAD:
- deferred_flag |= QMGR_FLUSH_DEAD;
- incoming_flag |= QMGR_FLUSH_DEAD;
- break;
- case QMGR_REQ_SCAN_ALL:
- deferred_flag |= QMGR_SCAN_ALL;
- incoming_flag |= QMGR_SCAN_ALL;
- break;
- default:
- if (msg_verbose)
- msg_info("request ignored");
- break;
- }
- }
-
- /*
- * Process each request type at most once. Modifiers take effect upon the
- * next queue run. If no queue run is in progress, and a queue scan is
- * requested, the request takes effect immediately.
- */
- if (incoming_flag != 0)
- qmgr_scan_request(qmgr_incoming, incoming_flag);
- if (deferred_flag != 0)
- qmgr_scan_request(qmgr_deferred, deferred_flag);
-}
-
-/* qmgr_loop - queue manager main loop */
-
-static int qmgr_loop(char *unused_name, char **unused_argv)
-{
- char *in_path = 0;
- char *df_path = 0;
-
- /*
- * This routine runs as part of the event handling loop, after the event
- * manager has delivered a timer or I/O event (including the completion
- * of a connection to a delivery process), or after it has waited for a
- * specified amount of time. The result value of qmgr_loop() specifies
- * how long the event manager should wait for the next event.
- */
-#define DONT_WAIT 0
-#define WAIT_FOR_EVENT (-1)
-
- /*
- * Attempt to drain the active queue by allocating a suitable delivery
- * process and by delivering mail via it. Delivery process allocation and
- * mail delivery are asynchronous.
- */
- qmgr_active_drain();
-
- /*
- * Let some new blood into the active queue when the queue size is
- * smaller than some configurable limit. When the system is under heavy
- * load, favor new mail over old mail.
- */
- if (qmgr_message_count < var_qmgr_active_limit)
- if ((in_path = qmgr_scan_next(qmgr_incoming)) != 0)
- qmgr_active_feed(qmgr_incoming, in_path);
- if (qmgr_message_count < var_qmgr_active_limit)
- if ((df_path = qmgr_scan_next(qmgr_deferred)) != 0)
- qmgr_active_feed(qmgr_deferred, df_path);
- if (in_path || df_path)
- return (DONT_WAIT);
- return (WAIT_FOR_EVENT);
-}
-
-/* pre_accept - see if tables have changed */
-
-static void pre_accept(char *unused_name, char **unused_argv)
-{
- if (dict_changed()) {
- msg_info("table has changed -- exiting");
- exit(0);
- }
-}
-
-/* qmgr_pre_init - pre-jail initialization */
-
-static void qmgr_pre_init(char *unused_name, char **unused_argv)
-{
- if (*var_relocated_maps)
- qmgr_relocated = maps_create("relocated", var_relocated_maps,
- DICT_FLAG_LOCK);
- if (*var_virtual_maps)
- qmgr_virtual = maps_create("virtual", var_virtual_maps,
- DICT_FLAG_LOCK);
-}
-
-/* qmgr_post_init - post-jail initialization */
-
-static void qmgr_post_init(char *unused_name, char **unused_argv)
-{
-
- /*
- * This routine runs after the skeleton code has entered the chroot jail.
- * Prevent automatic process suicide after a limited number of client
- * requests or after a limited amount of idle time. Move any left-over
- * entries from the active queue to the incoming queue, and give them a
- * time stamp into the future, in order to allow ongoing deliveries to
- * finish first. Start scanning the incoming and deferred queues.
- * Left-over active queue entries are moved to the incoming queue because
- * the incoming queue has priority; moving left-overs to the deferred
- * queue could cause anomalous delays when "postfix reload/start" are
- * issued often.
- */
- var_use_limit = 0;
- var_idle_limit = 0;
- qmgr_move(MAIL_QUEUE_ACTIVE, MAIL_QUEUE_INCOMING, event_time());
- qmgr_incoming = qmgr_scan_create(MAIL_QUEUE_INCOMING);
- qmgr_deferred = qmgr_scan_create(MAIL_QUEUE_DEFERRED);
- qmgr_scan_request(qmgr_incoming, QMGR_SCAN_START);
- qmgr_deferred_run_event(0, (char *) 0);
-}
-
-/* main - the main program */
-
-int main(int argc, char **argv)
-{
- static CONFIG_STR_TABLE str_table[] = {
- VAR_RELOCATED_MAPS, DEF_RELOCATED_MAPS, &var_relocated_maps, 0, 0,
- VAR_VIRTUAL_MAPS, DEF_VIRTUAL_MAPS, &var_virtual_maps, 0, 0,
- VAR_DEFER_XPORTS, DEF_DEFER_XPORTS, &var_defer_xports, 0, 0,
- 0,
- };
- static CONFIG_TIME_TABLE time_table[] = {
- VAR_QUEUE_RUN_DELAY, DEF_QUEUE_RUN_DELAY, &var_queue_run_delay, 1, 0,
- VAR_MIN_BACKOFF_TIME, DEF_MIN_BACKOFF_TIME, &var_min_backoff_time, 1, 0,
- VAR_MAX_BACKOFF_TIME, DEF_MAX_BACKOFF_TIME, &var_max_backoff_time, 1, 0,
- VAR_MAX_QUEUE_TIME, DEF_MAX_QUEUE_TIME, &var_max_queue_time, 1, 8640000,
- VAR_XPORT_RETRY_TIME, DEF_XPORT_RETRY_TIME, &var_transport_retry_time, 1, 0,
- 0,
- };
- static CONFIG_INT_TABLE int_table[] = {
- VAR_QMGR_ACT_LIMIT, DEF_QMGR_ACT_LIMIT, &var_qmgr_active_limit, 1, 0,
- VAR_QMGR_RCPT_LIMIT, DEF_QMGR_RCPT_LIMIT, &var_qmgr_rcpt_limit, 1, 0,
- VAR_QMGR_MSG_RCPT_LIMIT, DEF_QMGR_MSG_RCPT_LIMIT, &var_qmgr_msg_rcpt_limit, 1, 0,
- VAR_XPORT_RCPT_LIMIT, DEF_XPORT_RCPT_LIMIT, &var_xport_rcpt_limit, 0, 0,
- VAR_STACK_RCPT_LIMIT, DEF_STACK_RCPT_LIMIT, &var_stack_rcpt_limit, 0, 0,
- VAR_DELIVERY_SLOT_COST, DEF_DELIVERY_SLOT_COST, &var_delivery_slot_cost, 0, 0,
- VAR_DELIVERY_SLOT_LOAN, DEF_DELIVERY_SLOT_LOAN, &var_delivery_slot_loan, 0, 0,
- VAR_DELIVERY_SLOT_DISCOUNT, DEF_DELIVERY_SLOT_DISCOUNT, &var_delivery_slot_discount, 0, 100,
- VAR_MIN_DELIVERY_SLOTS, DEF_MIN_DELIVERY_SLOTS, &var_min_delivery_slots, 0, 0,
- VAR_INIT_DEST_CON, DEF_INIT_DEST_CON, &var_init_dest_concurrency, 1, 0,
- VAR_DEST_CON_LIMIT, DEF_DEST_CON_LIMIT, &var_dest_con_limit, 0, 0,
- VAR_DEST_RCPT_LIMIT, DEF_DEST_RCPT_LIMIT, &var_dest_rcpt_limit, 0, 0,
- 0,
- };
- static CONFIG_BOOL_TABLE bool_table[] = {
- VAR_ALLOW_MIN_USER, DEF_ALLOW_MIN_USER, &var_allow_min_user,
- 0,
- };
-
- /*
- * Use the trigger service skeleton, because no-one else should be
- * monitoring our service port while this process runs, and because we do
- * not talk back to the client.
- */
- trigger_server_main(argc, argv, qmgr_trigger_event,
- MAIL_SERVER_INT_TABLE, int_table,
- MAIL_SERVER_STR_TABLE, str_table,
- MAIL_SERVER_BOOL_TABLE, bool_table,
- MAIL_SERVER_TIME_TABLE, time_table,
- MAIL_SERVER_PRE_INIT, qmgr_pre_init,
- MAIL_SERVER_POST_INIT, qmgr_post_init,
- MAIL_SERVER_LOOP, qmgr_loop,
- MAIL_SERVER_PRE_ACCEPT, pre_accept,
- 0);
-}
+++ /dev/null
-/*++
-/* NAME
-/* qmgr 3h
-/* SUMMARY
-/* queue manager data structures
-/* SYNOPSIS
-/* #include "qmgr.h"
-/* DESCRIPTION
-/* .nf
-
- /*
- * Utility library.
- */
-#include <vstream.h>
-#include <scan_dir.h>
-
- /*
- * Global library.
- */
-#include <maps.h>
-
- /*
- * The queue manager is built around lots of mutually-referring structures.
- * These typedefs save some typing.
- */
-typedef struct QMGR_TRANSPORT QMGR_TRANSPORT;
-typedef struct QMGR_QUEUE QMGR_QUEUE;
-typedef struct QMGR_ENTRY QMGR_ENTRY;
-typedef struct QMGR_MESSAGE QMGR_MESSAGE;
-typedef struct QMGR_JOB QMGR_JOB;
-typedef struct QMGR_PEER QMGR_PEER;
-typedef struct QMGR_TRANSPORT_LIST QMGR_TRANSPORT_LIST;
-typedef struct QMGR_QUEUE_LIST QMGR_QUEUE_LIST;
-typedef struct QMGR_ENTRY_LIST QMGR_ENTRY_LIST;
-typedef struct QMGR_JOB_LIST QMGR_JOB_LIST;
-typedef struct QMGR_PEER_LIST QMGR_PEER_LIST;
-typedef struct QMGR_RCPT QMGR_RCPT;
-typedef struct QMGR_RCPT_LIST QMGR_RCPT_LIST;
-typedef struct QMGR_SCAN QMGR_SCAN;
-
- /*
- * Hairy macros to update doubly-linked lists.
- */
-#define QMGR_LIST_ROTATE(head, object, peers) { \
- head.next->peers.prev = head.prev; \
- head.prev->peers.next = head.next; \
- head.next = object->peers.next; \
- head.next->peers.prev = 0; \
- head.prev = object; \
- object->peers.next = 0; \
-}
-
-#define QMGR_LIST_UNLINK(head, type, object, peers) { \
- type next = object->peers.next; \
- type prev = object->peers.prev; \
- if (prev) prev->peers.next = next; \
- else head.next = next; \
- if (next) next->peers.prev = prev; \
- else head.prev = prev; \
- object->peers.next = object->peers.prev = 0; \
-}
-
-#define QMGR_LIST_PREPEND(head, object, peers) { \
- object->peers.next = head.next; \
- object->peers.prev = 0; \
- if (head.next) { \
- head.next->peers.prev = object; \
- } else { \
- head.prev = object; \
- } \
- head.next = object; \
-}
-
-#define QMGR_LIST_APPEND(head, object, peers) { \
- object->peers.prev = head.prev; \
- object->peers.next = 0; \
- if (head.prev) { \
- head.prev->peers.next = object; \
- } else { \
- head.next = object; \
- } \
- head.prev = object; \
-}
-
-#define QMGR_LIST_INIT(head) { \
- head.prev = 0; \
- head.next = 0; \
-}
-
- /*
- * Transports are looked up by name (when we have resolved a message), or
- * round-robin wise (when we want to distribute resources fairly).
- */
-struct QMGR_TRANSPORT_LIST {
- QMGR_TRANSPORT *next;
- QMGR_TRANSPORT *prev;
-};
-
-extern struct HTABLE *qmgr_transport_byname; /* transport by name */
-extern QMGR_TRANSPORT_LIST qmgr_transport_list; /* transports, round robin */
-
- /*
- * Each transport (local, smtp-out, bounce) can have one queue per next hop
- * name. Queues are looked up by next hop name (when we have resolved a
- * message destination), or round-robin wise (when we want to deliver
- * messages fairly).
- */
-struct QMGR_QUEUE_LIST {
- QMGR_QUEUE *next;
- QMGR_QUEUE *prev;
-};
-
-struct QMGR_JOB_LIST {
- QMGR_JOB *next;
- QMGR_JOB *prev;
-};
-
-struct QMGR_TRANSPORT {
- int flags; /* blocked, etc. */
- char *name; /* transport name */
- int dest_concurrency_limit; /* concurrency per domain */
- int init_dest_concurrency; /* init. per-domain concurrency */
- int recipient_limit; /* recipients per transaction */
- int rcpt_per_stack; /* extra slots reserved for jobs on
- * the job stack */
- int rcpt_unused; /* available in-core recipient slots */
- int slot_cost; /* cost of new preemption slot (# of
- * selected entries) */
- int slot_loan; /* preemption boost offset and */
- int slot_loan_factor; /* factor, see qmgr_job_preempt() */
- int min_slots; /* when preemption can take effect at
- * all */
- struct HTABLE *queue_byname; /* queues indexed by domain */
- QMGR_QUEUE_LIST queue_list; /* queues, round robin order */
- struct HTABLE *job_byname; /* jobs indexed by queue id */
- QMGR_JOB_LIST job_list; /* list of message jobs (1 per
- * message) */
- QMGR_JOB_LIST job_stack; /* job stack for tracking preemption */
- QMGR_JOB *job_next_unread; /* next job with unread recipients */
- QMGR_JOB *candidate_cache; /* cached result from
- * qmgr_job_candidate() */
- time_t candidate_cache_time; /* when candidate_cache was last
- * updated */
- QMGR_TRANSPORT_LIST peers; /* linkage */
- char *reason; /* why unavailable */
-};
-
-#define QMGR_TRANSPORT_STAT_DEAD (1<<1)
-#define QMGR_TRANSPORT_STAT_BUSY (1<<2)
-
-typedef void (*QMGR_TRANSPORT_ALLOC_NOTIFY) (QMGR_TRANSPORT *, VSTREAM *);
-extern QMGR_TRANSPORT *qmgr_transport_select(void);
-extern void qmgr_transport_alloc(QMGR_TRANSPORT *, QMGR_TRANSPORT_ALLOC_NOTIFY);
-extern void qmgr_transport_throttle(QMGR_TRANSPORT *, const char *);
-extern void qmgr_transport_unthrottle(QMGR_TRANSPORT *);
-extern QMGR_TRANSPORT *qmgr_transport_create(const char *);
-extern QMGR_TRANSPORT *qmgr_transport_find(const char *);
-
- /*
- * Each next hop (e.g., a domain name) has its own queue of pending message
- * transactions. The "todo" queue contains messages that are to be delivered
- * to this next hop. When a message is elected for transmission, it is moved
- * from the "todo" queue to the "busy" queue. Messages are taken from the
- * "todo" queue in round-robin order.
- */
-struct QMGR_ENTRY_LIST {
- QMGR_ENTRY *next;
- QMGR_ENTRY *prev;
-};
-
-struct QMGR_QUEUE {
- char *name; /* domain name */
- int todo_refcount; /* queue entries (todo list) */
- int busy_refcount; /* queue entries (busy list) */
- int window; /* slow open algorithm */
- QMGR_TRANSPORT *transport; /* transport linkage */
- QMGR_ENTRY_LIST todo; /* todo queue entries */
- QMGR_ENTRY_LIST busy; /* messages on the wire */
- QMGR_QUEUE_LIST peers; /* neighbor queues */
- char *reason; /* why unavailable */
-};
-
-#define QMGR_QUEUE_TODO 1 /* waiting for service */
-#define QMGR_QUEUE_BUSY 2 /* recipients on the wire */
-
-extern int qmgr_queue_count;
-
-extern QMGR_QUEUE *qmgr_queue_create(QMGR_TRANSPORT *, const char *);
-extern void qmgr_queue_done(QMGR_QUEUE *);
-extern void qmgr_queue_throttle(QMGR_QUEUE *, const char *);
-extern void qmgr_queue_unthrottle(QMGR_QUEUE *);
-extern QMGR_QUEUE *qmgr_queue_find(QMGR_TRANSPORT *, const char *);
-
- /*
- * Structure for a recipient list. Initially, it just contains recipient
- * addresses and file offsets. After the address resolver has done its work,
- * each recipient is accompanied by a reference to a specific queues (which
- * implies a specific transport). This is an extended version of similar
- * information maintained by the recipient_list(3) module.
- */
-struct QMGR_RCPT {
- long offset; /* REC_TYPE_RCPT byte */
- char *address; /* complete address */
- QMGR_QUEUE *queue; /* resolved queue */
-};
-
-struct QMGR_RCPT_LIST {
- QMGR_RCPT *info;
- int len;
- int avail;
-};
-
-extern void qmgr_rcpt_list_init(QMGR_RCPT_LIST *);
-extern void qmgr_rcpt_list_add(QMGR_RCPT_LIST *, long, const char *);
-extern void qmgr_rcpt_list_free(QMGR_RCPT_LIST *);
-
- /*
- * Structure of one next-hop queue entry. In order to save some copying
- * effort we allow multiple recipients per transaction.
- */
-struct QMGR_ENTRY {
- VSTREAM *stream; /* delivery process */
- QMGR_MESSAGE *message; /* message info */
- QMGR_RCPT_LIST rcpt_list; /* as many as it takes */
- QMGR_QUEUE *queue; /* parent linkage */
- QMGR_PEER *peer; /* parent linkage */
- QMGR_ENTRY_LIST queue_peers; /* per queue neighbor entries */
- QMGR_ENTRY_LIST peer_peers; /* per peer neighbor entries */
-};
-
-extern QMGR_ENTRY *qmgr_entry_select(QMGR_PEER *);
-extern void qmgr_entry_unselect(QMGR_ENTRY *);
-extern void qmgr_entry_done(QMGR_ENTRY *, int);
-extern QMGR_ENTRY *qmgr_entry_create(QMGR_PEER *, QMGR_MESSAGE *);
-
- /*
- * All common in-core information about a message is kept here. When all
- * recipients have been tried the message file is linked to the "deferred"
- * queue (some hosts not reachable), to the "bounce" queue (some recipients
- * were rejected), and is then removed from the "active" queue.
- */
-struct QMGR_MESSAGE {
- int flags; /* delivery problems */
- int qflags; /* queuing flags */
- VSTREAM *fp; /* open queue file or null */
- int refcount; /* queue entries */
- int single_rcpt; /* send one rcpt at a time */
- long arrival_time; /* time when queued */
- time_t queued_time; /* time when moved to the active
- * queue */
- long warn_offset; /* warning bounce flag offset */
- time_t warn_time; /* time next warning to be sent */
- long data_offset; /* data seek offset */
- char *queue_name; /* queue name */
- char *queue_id; /* queue file */
- char *sender; /* complete address */
- char *errors_to; /* error report address */
- char *return_receipt; /* confirm receipt address */
- char *filter_xport; /* filtering transport */
- char *inspect_xport; /* inspecting transport */
- long data_size; /* message content size */
- long rcpt_offset; /* more recipients here */
- long unread_offset; /* more unread recipients here */
- QMGR_RCPT_LIST rcpt_list; /* complete addresses */
- int rcpt_count; /* used recipient slots */
- int rcpt_limit; /* maximum read in-core */
- int rcpt_unread; /* # of recipients left in queue file */
- QMGR_JOB_LIST job_list; /* jobs delivering this message (1
- * per transport) */
-};
-
-#define QMGR_MESSAGE_LOCKED ((QMGR_MESSAGE *) 1)
-
-extern int qmgr_message_count;
-extern int qmgr_recipient_count;
-extern MAPS *qmgr_relocated;
-extern MAPS *qmgr_virtual;
-
-extern void qmgr_message_free(QMGR_MESSAGE *);
-extern void qmgr_message_update_warn(QMGR_MESSAGE *);
-extern QMGR_MESSAGE *qmgr_message_alloc(const char *, const char *, int);
-extern QMGR_MESSAGE *qmgr_message_realloc(QMGR_MESSAGE *);
-
- /*
- * Sometimes it's required to access the transport queues and entries on per
- * message basis. That's what the QMGR_JOB structure is for - it groups all
- * per message information within each transport using a list of QMGR_PEER
- * structures. These structures in turn correspond with per message
- * QMGR_QUEUE structure and list all per message QMGR_ENTRY structures.
- */
-struct QMGR_PEER_LIST {
- QMGR_PEER *next;
- QMGR_PEER *prev;
-};
-
-struct QMGR_JOB {
- QMGR_MESSAGE *message; /* message delivered by this job */
- QMGR_TRANSPORT *transport; /* transport this job belongs to */
- QMGR_JOB_LIST message_peers; /* per message neighbor linkage */
- QMGR_JOB_LIST transport_peers; /* per transport neighbor linkage */
- QMGR_JOB_LIST stack_peers; /* transport stack linkage */
- int stack_level; /* job stack nesting level (-1 means
- * retired) */
- struct HTABLE *peer_byname; /* message job peers, indexed by
- * domain */
- QMGR_PEER_LIST peer_list; /* list of message job peers */
- int slots_used; /* slots used during preemption */
- int slots_available; /* slots available for preemption (in
- * multiples of slot_cost) */
- int selected_entries; /* # of entries selected for delivery
- * so far */
- int read_entries; /* # of entries read in-core so far */
- int rcpt_count; /* used recipient slots */
- int rcpt_limit; /* available recipient slots */
-};
-
-struct QMGR_PEER {
- QMGR_JOB *job; /* job handling this peer */
- QMGR_QUEUE *queue; /* queue corresponding with this peer */
- int refcount; /* peer entries */
- QMGR_ENTRY_LIST entry_list; /* todo message entries queued for
- * this peer */
- QMGR_PEER_LIST peers; /* neighbor linkage */
-};
-
-extern QMGR_ENTRY *qmgr_job_entry_select(QMGR_TRANSPORT *);
-extern QMGR_PEER *qmgr_peer_select(QMGR_JOB *);
-
-extern QMGR_JOB *qmgr_job_obtain(QMGR_MESSAGE *, QMGR_TRANSPORT *);
-extern void qmgr_job_free(QMGR_JOB *);
-extern void qmgr_job_move_limits(QMGR_JOB *);
-
-extern QMGR_PEER *qmgr_peer_create(QMGR_JOB *, QMGR_QUEUE *);
-extern QMGR_PEER *qmgr_peer_find(QMGR_JOB *, QMGR_QUEUE *);
-extern void qmgr_peer_free(QMGR_PEER *);
-
- /*
- * qmgr_defer.c
- */
-extern void qmgr_defer_transport(QMGR_TRANSPORT *, const char *);
-extern void qmgr_defer_todo(QMGR_QUEUE *, const char *);
-extern void qmgr_defer_recipient(QMGR_MESSAGE *, const char *, const char *);
-
- /*
- * qmgr_bounce.c
- */
-extern void PRINTFLIKE(3, 4) qmgr_bounce_recipient(QMGR_MESSAGE *, QMGR_RCPT *, const char *,...);
-
- /*
- * qmgr_deliver.c
- */
-extern int qmgr_deliver_concurrency;
-extern void qmgr_deliver(QMGR_TRANSPORT *, VSTREAM *);
-
- /*
- * qmgr_active.c
- */
-extern void qmgr_active_feed(QMGR_SCAN *, const char *);
-extern void qmgr_active_drain(void);
-extern void qmgr_active_done(QMGR_MESSAGE *);
-
- /*
- * qmgr_move.c
- */
-extern void qmgr_move(const char *, const char *, time_t);
-
- /*
- * qmgr_enable.c
- */
-extern void qmgr_enable_all(void);
-extern void qmgr_enable_transport(QMGR_TRANSPORT *);
-extern void qmgr_enable_queue(QMGR_QUEUE *);
-
- /*
- * Queue scan context.
- */
-struct QMGR_SCAN {
- char *queue; /* queue name */
- int flags; /* private, this run */
- int nflags; /* private, next run */
- struct SCAN_DIR *handle; /* scan */
-};
-
- /*
- * Flags that control queue scans or destination selection. These are
- * similar to the QMGR_REQ_XXX request codes.
- */
-#define QMGR_SCAN_START (1<<0) /* start now/restart when done */
-#define QMGR_SCAN_ALL (1<<1) /* all queue file time stamps */
-#define QMGR_FLUSH_DEAD (1<<2) /* all sites, all transports */
-
- /*
- * qmgr_scan.c
- */
-extern QMGR_SCAN *qmgr_scan_create(const char *);
-extern void qmgr_scan_request(QMGR_SCAN *, int);
-extern char *qmgr_scan_next(QMGR_SCAN *);
-
-/* 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
-/*
-/* Scheduler enhancements:
-/* Patrik Rak
-/* Modra 6
-/* 155 00, Prague, Czech Republic
-/*--*/
+++ /dev/null
-/*++
-/* NAME
-/* qmgr_active 3
-/* SUMMARY
-/* active queue management
-/* SYNOPSIS
-/* #include "qmgr.h"
-/*
-/* void qmgr_active_feed(scan_info, queue_id)
-/* QMGR_SCAN *scan_info;
-/* const char *queue_id;
-/*
-/* void qmgr_active_drain()
-/*
-/* int qmgr_active_done(message)
-/* QMGR_MESSAGE *message;
-/* DESCRIPTION
-/* These functions maintain the active message queue: the set
-/* of messages that the queue manager is actually working on.
-/* The active queue is limited in size. Messages are drained
-/* from the active queue by allocating a delivery process and
-/* by delivering mail via that process. Messages leak into the
-/* active queue only when the active queue is small enough.
-/* Damaged message files are saved to the "corrupt" directory.
-/*
-/* qmgr_active_feed() inserts the named message file into
-/* the active queue. Message files with the wrong name or
-/* with other wrong properties are skipped but not removed.
-/* The following queue flags are recognized, other flags being
-/* ignored:
-/* .IP QMGR_SCAN_ALL
-/* Examine all queue files. Normally, deferred queue files with
-/* future time stamps are ignored, and incoming queue files with
-/* future time stamps are frowned upon.
-/* .PP
-/* qmgr_active_drain() allocates one delivery process.
-/* Process allocation is asynchronous. Once the delivery
-/* process is available, an attempt is made to deliver
-/* a message via it. Message delivery is asynchronous, too.
-/*
-/* qmgr_active_done() deals with a message after delivery
-/* has been tried for all in-core recipients. If the message
-/* was bounced, a bounce message is sent to the sender, or
-/* to the Errors-To: address if one was specified.
-/* If there are more on-file recipients, a new batch of
-/* in-core recipients is read from the queue file. Otherwise,
-/* if a delivery agent marked the queue file as corrupt,
-/* the queue file is moved to the "corrupt" queue (surprise);
-/* if at least one delivery failed, the message is moved
-/* to the deferred queue. The time stamps of a deferred queue
-/* file are set to the nearest wakeup time of its recipient
-/* sites (if delivery failed due to a problem with a next-hop
-/* host), are set into the future by the amount of time the
-/* message was queued (per-message exponential backoff), or are set
-/* into the future by a minimal backoff time, whichever is more.
-/* The minimal_backoff_time parameter specifies the minimal
-/* amount of time between delivery attempts; maximal_backoff_time
-/* specifies an upper limit.
-/* DIAGNOSTICS
-/* Fatal: queue file access failures, out of memory.
-/* Panic: interface violations, internal consistency errors.
-/* Warnings: corrupt message file. A corrupt message is saved
-/* to the "corrupt" queue for further inspection.
-/* 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 <sys/stat.h>
-#include <dirent.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <string.h>
-#include <utime.h>
-#include <errno.h>
-
-#ifndef S_IRWXU /* What? no POSIX system? */
-#define S_IRWXU 0700
-#endif
-
-/* Utility library. */
-
-#include <msg.h>
-#include <events.h>
-#include <mymalloc.h>
-#include <vstream.h>
-
-/* Global library. */
-
-#include <mail_params.h>
-#include <mail_open_ok.h>
-#include <mail_queue.h>
-#include <recipient_list.h>
-#include <bounce.h>
-#include <defer.h>
-#include <abounce.h>
-#include <rec_type.h>
-
-/* Application-specific. */
-
-#include "qmgr.h"
-
- /*
- * A bunch of call-back routines.
- */
-static void qmgr_active_done_2_bounce_flush(int, char *);
-static void qmgr_active_done_2_generic(QMGR_MESSAGE *);
-static void qmgr_active_done_3_defer_flush(int, char *);
-static void qmgr_active_done_3_defer_warn(int, char *);
-static void qmgr_active_done_3_generic(QMGR_MESSAGE *);
-
-/* qmgr_active_corrupt - move corrupted file out of the way */
-
-static void qmgr_active_corrupt(const char *queue_id)
-{
- char *myname = "qmgr_active_corrupt";
-
- if (mail_queue_rename(queue_id, MAIL_QUEUE_ACTIVE, MAIL_QUEUE_CORRUPT)) {
- if (errno != ENOENT)
- msg_fatal("%s: save corrupt file queue %s id %s: %m",
- myname, MAIL_QUEUE_ACTIVE, queue_id);
- msg_warn("%s: save corrupt file queue %s id %s: %m",
- myname, MAIL_QUEUE_ACTIVE, queue_id);
- } else {
- msg_warn("corrupt file queue %s id %s", MAIL_QUEUE_ACTIVE, queue_id);
- }
-}
-
-/* qmgr_active_defer - defer queue file */
-
-static void qmgr_active_defer(const char *queue_name, const char *queue_id,
- const char *dest_queue, int delay)
-{
- char *myname = "qmgr_active_defer";
- const char *path;
- struct utimbuf tbuf;
-
- if (msg_verbose)
- msg_info("wakeup %s after %ld secs", queue_id, (long) delay);
-
- tbuf.actime = tbuf.modtime = event_time() + delay;
- path = mail_queue_path((VSTRING *) 0, queue_name, queue_id);
- if (utime(path, &tbuf) < 0)
- msg_fatal("%s: update %s time stamps: %m", myname, path);
- if (mail_queue_rename(queue_id, queue_name, dest_queue)) {
- if (errno != ENOENT)
- msg_fatal("%s: rename %s from %s to %s: %m", myname,
- queue_id, queue_name, dest_queue);
- msg_warn("%s: rename %s from %s to %s: %m", myname,
- queue_id, queue_name, dest_queue);
- } else if (msg_verbose) {
- msg_info("%s: defer %s", myname, queue_id);
- }
-}
-
-/* qmgr_active_feed - feed one message into active queue */
-
-void qmgr_active_feed(QMGR_SCAN *scan_info, const char *queue_id)
-{
- char *myname = "qmgr_active_feed";
- QMGR_MESSAGE *message;
- struct stat st;
- const char *path;
-
- if (strcmp(scan_info->queue, MAIL_QUEUE_ACTIVE) == 0)
- msg_panic("%s: bad queue %s", myname, scan_info->queue);
- if (msg_verbose)
- msg_info("%s: queue %s", myname, scan_info->queue);
-
- /*
- * Make sure this is something we are willing to open.
- */
- if (mail_open_ok(scan_info->queue, queue_id, &st, &path) == MAIL_OPEN_NO)
- return;
-
- if (msg_verbose)
- msg_info("%s: %s", myname, path);
-
- /*
- * Skip files that have time stamps into the future. They need to cool
- * down. Incoming and deferred files can have future time stamps.
- */
- if ((scan_info->flags & QMGR_SCAN_ALL) == 0
- && st.st_mtime > time((time_t *) 0) + 1) {
- if (msg_verbose)
- msg_info("%s: skip %s (%ld seconds)", myname, queue_id,
- (long) (st.st_mtime - event_time()));
- return;
- }
-
- /*
- * Move the message to the active queue. File access errors are fatal.
- */
- if (mail_queue_rename(queue_id, scan_info->queue, MAIL_QUEUE_ACTIVE)) {
- if (errno != ENOENT)
- msg_fatal("%s: %s: rename from %s to %s: %m", myname,
- queue_id, scan_info->queue, MAIL_QUEUE_ACTIVE);
- msg_warn("%s: %s: rename from %s to %s: %m", myname,
- queue_id, scan_info->queue, MAIL_QUEUE_ACTIVE);
- return;
- }
-
- /*
- * Extract envelope information: sender and recipients. At this point,
- * mail addresses have been processed by the cleanup service so they
- * should be in canonical form. Generate requests to deliver this
- * message.
- *
- * Throwing away queue files seems bad, especially when they made it this
- * far into the mail system. Therefore we save bad files to a separate
- * directory for further inspection.
- *
- * After queue manager restart it is possible that a queue file is still
- * being delivered. In that case (the file is locked), defer delivery by
- * a minimal amount of time.
- */
- if ((message = qmgr_message_alloc(MAIL_QUEUE_ACTIVE, queue_id,
- scan_info->flags)) == 0) {
- qmgr_active_corrupt(queue_id);
- } else if (message == QMGR_MESSAGE_LOCKED) {
- qmgr_active_defer(MAIL_QUEUE_ACTIVE, queue_id, MAIL_QUEUE_INCOMING, 60);
- } else {
-
- /*
- * Special case if all recipients were already delivered. Send any
- * bounces and clean up.
- */
- if (message->refcount == 0)
- qmgr_active_done(message);
- }
-}
-
-/* qmgr_active_done - dispose of message after recipients have been tried */
-
-void qmgr_active_done(QMGR_MESSAGE *message)
-{
- char *myname = "qmgr_active_done";
- struct stat st;
-
- if (msg_verbose)
- msg_info("%s: %s", myname, message->queue_id);
-
- /*
- * During a previous iteration, an attempt to bounce this message may
- * have failed, so there may still be a bounce log lying around. XXX By
- * groping around in the bounce queue, we're trespassing on the bounce
- * service's territory. But doing so is more robust than depending on the
- * bounce daemon to do the lookup for us, and for us to do the deleting
- * after we have received a successful status from the bounce service.
- * The bounce queue directory blocks are most likely in memory anyway. If
- * these lookups become a performance problem we will have to build an
- * in-core cache into the bounce daemon.
- *
- * Don't bounce when the bounce log is empty. The bounce process obviously
- * failed, and the delivery agent will have requested that the message be
- * deferred.
- *
- * Bounces are sent asynchronously to avoid stalling while the cleanup
- * daemon waits for the qmgr to accept the "new mail" trigger.
- */
- if (stat(mail_queue_path((VSTRING *) 0, MAIL_QUEUE_BOUNCE, message->queue_id), &st) == 0) {
- if (st.st_size == 0) {
- if (mail_queue_remove(MAIL_QUEUE_BOUNCE, message->queue_id))
- msg_fatal("remove %s %s: %m",
- MAIL_QUEUE_BOUNCE, message->queue_id);
- } else {
- if (msg_verbose)
- msg_info("%s: bounce %s", myname, message->queue_id);
- abounce_flush(BOUNCE_FLAG_KEEP,
- message->queue_name,
- message->queue_id,
- message->errors_to,
- qmgr_active_done_2_bounce_flush,
- (char *) message);
- return;
- }
- }
-
- /*
- * Asynchronous processing does not reach this point.
- */
- qmgr_active_done_2_generic(message);
-}
-
-/* qmgr_active_done_2_bounce_flush - process abounce_flush() status */
-
-static void qmgr_active_done_2_bounce_flush(int status, char *context)
-{
- QMGR_MESSAGE *message = (QMGR_MESSAGE *) context;
-
- /*
- * Process abounce_flush() status and continue processing.
- */
- message->flags |= status;
- qmgr_active_done_2_generic(message);
-}
-
-/* qmgr_active_done_2_generic - continue processing */
-
-static void qmgr_active_done_2_generic(QMGR_MESSAGE *message)
-{
- char *myname = "qmgr_active_done_2_generic";
- const char *path;
- struct stat st;
-
- /*
- * A delivery agent marks a queue file as corrupt by changing its
- * attributes, and by pretending that delivery was deferred.
- */
- if (message->flags
- && !mail_open_ok(MAIL_QUEUE_ACTIVE, message->queue_id, &st, &path)) {
- qmgr_active_corrupt(message->queue_id);
- qmgr_message_free(message);
- return;
- }
-
- /*
- * If we did not read all recipients from this file, go read some more,
- * but remember whether some recipients have to be tried again.
- *
- * Throwing away queue files seems bad, especially when they made it this
- * far into the mail system. Therefore we save bad files to a separate
- * directory for further inspection by a human being.
- */
- if (message->rcpt_offset > 0) {
- if (qmgr_message_realloc(message) == 0) {
- qmgr_active_corrupt(message->queue_id);
- qmgr_message_free(message);
- } else {
- if (message->refcount == 0)
- qmgr_active_done(message); /* recurse for consistency */
- }
- return;
- }
-
- /*
- * If we get to this point we have tried all recipients for this message.
- * If the message is too old, try to bounce it.
- *
- * Bounces are sent asynchronously to avoid stalling while the cleanup
- * daemon waits for the qmgr to accept the "new mail" trigger.
- */
- if (message->flags) {
- if (event_time() > message->arrival_time + var_max_queue_time) {
- if (msg_verbose)
- msg_info("%s: too old, bouncing %s", myname, message->queue_id);
- adefer_flush(BOUNCE_FLAG_KEEP,
- message->queue_name,
- message->queue_id,
- message->errors_to,
- qmgr_active_done_3_defer_flush,
- (char *) message);
- return;
- } else if (message->warn_time > 0
- && event_time() > message->warn_time) {
- if (msg_verbose)
- msg_info("%s: sending defer warning for %s", myname, message->queue_id);
- adefer_warn(BOUNCE_FLAG_KEEP,
- message->queue_name,
- message->queue_id,
- message->errors_to,
- qmgr_active_done_3_defer_warn,
- (char *) message);
- return;
- }
- }
-
- /*
- * Asynchronous processing does not reach this point.
- */
- qmgr_active_done_3_generic(message);
-}
-
-/* qmgr_active_done_3_defer_warn - continue after adefer_warn() completion */
-
-static void qmgr_active_done_3_defer_warn(int status, char *context)
-{
- QMGR_MESSAGE *message = (QMGR_MESSAGE *) context;
-
- /*
- * Process adefer_warn() completion status and continue processing.
- */
- if (status == 0)
- qmgr_message_update_warn(message);
- qmgr_active_done_3_generic(message);
-}
-
-/* qmgr_active_done_3_defer_flush - continue after adefer_flush() completion */
-
-static void qmgr_active_done_3_defer_flush(int status, char *context)
-{
- QMGR_MESSAGE *message = (QMGR_MESSAGE *) context;
-
- /*
- * Process adefer_flush() status and continue processing.
- */
- message->flags = status;
- qmgr_active_done_3_generic(message);
-}
-
-/* qmgr_active_done_3_generic - continue processing */
-
-static void qmgr_active_done_3_generic(QMGR_MESSAGE *message)
-{
- char *myname = "qmgr_active_done_3_generic";
- int delay;
-
- /*
- * Some recipients need to be tried again. Move the queue file time
- * stamps into the future by the amount of time that the message is
- * delayed, and move the message to the deferred queue. Impose minimal
- * and maximal backoff times.
- *
- * Since we look at actual time in queue, not time since last delivery
- * attempt, backoff times will be distributed. However, we can still see
- * spikes in delivery activity because the interval between deferred
- * queue scans is finite.
- */
- if (message->flags) {
- if (message->arrival_time > 0) {
- delay = event_time() - message->arrival_time;
- if (delay > var_max_backoff_time)
- delay = var_max_backoff_time;
- if (delay < var_min_backoff_time)
- delay = var_min_backoff_time;
- } else {
- delay = var_min_backoff_time;
- }
- qmgr_active_defer(message->queue_name, message->queue_id,
- MAIL_QUEUE_DEFERRED, delay);
- }
-
- /*
- * All recipients done. Remove the queue file.
- */
- else {
- if (mail_queue_remove(message->queue_name, message->queue_id)) {
- if (errno != ENOENT)
- msg_fatal("%s: remove %s from %s: %m", myname,
- message->queue_id, message->queue_name);
- msg_warn("%s: remove %s from %s: %m", myname,
- message->queue_id, message->queue_name);
- } else if (msg_verbose) {
- msg_info("%s: remove %s", myname, message->queue_id);
- }
- }
-
- /*
- * Finally, delete the in-core message structure.
- */
- qmgr_message_free(message);
-}
-
-/* qmgr_active_drain - drain active queue by allocating a delivery process */
-
-void qmgr_active_drain(void)
-{
- QMGR_TRANSPORT *transport;
-
- /*
- * Allocate one delivery process for every transport with pending mail.
- * The process allocation completes asynchronously.
- */
- while ((transport = qmgr_transport_select()) != 0) {
- if (msg_verbose)
- msg_info("qmgr_active_drain: allocate %s", transport->name);
- qmgr_transport_alloc(transport, qmgr_deliver);
- }
-}
+++ /dev/null
-/*++
-/* NAME
-/* qmgr_bounce
-/* SUMMARY
-/* deal with mail that will not be delivered
-/* SYNOPSIS
-/* #include "qmgr.h"
-/*
-/* QMGR_QUEUE *qmgr_bounce_recipient(message, recipient, format, ...)
-/* QMGR_MESSAGE *message;
-/* QMGR_RCPT *recipient;
-/* const char *format;
-/* DESCRIPTION
-/* qmgr_bounce_recipient() produces a bounce log record.
-/* Once the bounce record is written successfully, the recipient
-/* is marked as done. When the bounce record cannot be written,
-/* the message structure is updated to reflect that the mail is
-/* deferred.
-/*
-/* Arguments:
-/* .IP message
-/* Open queue file with the message being bounced.
-/* .IP recipient
-/* The recipient that will not be delivered.
-/* .IP format
-/* Free-format text that describes why delivery will not happen.
-/* DIAGNOSTICS
-/* Panic: consistency check failure. Fatal: out of memory.
-/* 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
-/*
-/* Scheduler enhancements:
-/* Patrik Rak
-/* Modra 6
-/* 155 00, Prague, Czech Republic
-/*--*/
-
-/* System library. */
-
-#include <sys_defs.h>
-#include <stdarg.h>
-
-/* Utility library. */
-
-/* Global library. */
-
-#include <bounce.h>
-#include <deliver_completed.h>
-
-/* Application-specific. */
-
-#include "qmgr.h"
-
-/* qmgr_bounce_recipient - bounce one message recipient */
-
-void qmgr_bounce_recipient(QMGR_MESSAGE *message, QMGR_RCPT *recipient,
- const char *format,...)
-{
- va_list ap;
- int status;
-
- va_start(ap, format);
- status = vbounce_append(BOUNCE_FLAG_KEEP, message->queue_id,
- recipient->address, "none",
- message->arrival_time, format, ap);
- va_end(ap);
-
- if (status == 0)
- deliver_completed(message->fp, recipient->offset);
- else
- message->flags |= status;
-}
+++ /dev/null
-/*++
-/* NAME
-/* qmgr_defer
-/* SUMMARY
-/* deal with mail that must be delivered later
-/* SYNOPSIS
-/* #include "qmgr.h"
-/*
-/* void qmgr_defer_recipient(message, address, reason)
-/* QMGR_MESSAGE *message;
-/* const char *address;
-/* const char *reason;
-/*
-/* void qmgr_defer_todo(queue, reason)
-/* QMGR_QUEUE *queue;
-/* const char *reason;
-/*
-/* QMGR_QUEUE *qmgr_defer_transport(transport, reason)
-/* QMGR_TRANSPORT *transport;
-/* const char *reason;
-/* DESCRIPTION
-/* qmgr_defer_recipient() defers delivery of the named message to
-/* the named recipient. It updates the message structure and writes
-/* a log entry.
-/*
-/* qmgr_defer_todo() iterates over all "todo" deliveries queued for
-/* the named site, and calls qmgr_defer_recipient() for each recipient
-/* found. Side effects caused by qmgr_entry_done(), qmgr_queue_done(),
-/* and by qmgr_active_done(): in-core queue entries will disappear,
-/* in-core queues may disappear, in-core and on-disk messages may
-/* disappear, bounces may be sent, new in-core queues, queue entries
-/* and recipients may appear.
-/*
-/* qmgr_defer_transport() calls qmgr_defer_todo() for each queue
-/* that depends on the named transport. See there for side effects.
-/*
-/* Arguments:
-/* .IP recipient
-/* A recipient address; used for logging purposes, and for updating
-/* the message-specific \fIdefer\fR log.
-/* .IP queue
-/* Specifies a queue with delivery requests for a specific next-hop
-/* host (or local user).
-/* .IP transport
-/* Specifies a message delivery transport.
-/* .IP reason
-/* Free-format text that describes why delivery is deferred; this
-/* used for logging purposes, and for updating the message-specific
-/* \fIdefer\fR log.
-/* BUGS
-/* The side effects of calling this routine are quite dramatic.
-/* DIAGNOSTICS
-/* Panic: consistency check failure. Fatal: out of memory.
-/* 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
-/*
-/* Scheduler enhancements:
-/* Patrik Rak
-/* Modra 6
-/* 155 00, Prague, Czech Republic
-/*--*/
-
-/* System library. */
-
-#include <sys_defs.h>
-
-/* Utility library. */
-
-#include <msg.h>
-#include <vstream.h>
-
-/* Global library. */
-
-#include <defer.h>
-
-/* Application-specific. */
-
-#include "qmgr.h"
-
-/* qmgr_defer_transport - defer todo entries for named transport */
-
-void qmgr_defer_transport(QMGR_TRANSPORT *transport, const char *reason)
-{
- char *myname = "qmgr_defer_transport";
- QMGR_QUEUE *queue;
- QMGR_QUEUE *next;
-
- /*
- * Sanity checks.
- */
- if (reason == 0)
- msg_panic("%s: null reason", myname);
- if (msg_verbose)
- msg_info("defer transport %s: %s", transport->name, reason);
-
- /*
- * Proceed carefully. Queues may disappear as a side effect.
- */
- for (queue = transport->queue_list.next; queue; queue = next) {
- next = queue->peers.next;
- qmgr_defer_todo(queue, reason);
- }
-}
-
-/* qmgr_defer_todo - defer all todo queue entries for specific site */
-
-void qmgr_defer_todo(QMGR_QUEUE *queue, const char *reason)
-{
- char *myname = "qmgr_defer_todo";
- QMGR_ENTRY *entry;
- QMGR_ENTRY *next;
- QMGR_MESSAGE *message;
- QMGR_RCPT *recipient;
- int nrcpt;
-
- /*
- * Sanity checks.
- */
- if (reason == 0)
- msg_panic("%s: null reason", myname);
- if (msg_verbose)
- msg_info("defer site %s: %s", queue->name, reason);
-
- /*
- * Proceed carefully. Queue entries will disappear as a side effect.
- */
- for (entry = queue->todo.next; entry != 0; entry = next) {
- next = entry->queue_peers.next;
- message = entry->message;
- for (nrcpt = 0; nrcpt < entry->rcpt_list.len; nrcpt++) {
- recipient = entry->rcpt_list.info + nrcpt;
- qmgr_defer_recipient(message, recipient->address, reason);
- }
- qmgr_entry_done(entry, QMGR_QUEUE_TODO);
- }
-}
-
-/* qmgr_defer_recipient - defer delivery of specific recipient */
-
-void qmgr_defer_recipient(QMGR_MESSAGE *message, const char *address,
- const char *reason)
-{
- char *myname = "qmgr_defer_recipient";
-
- /*
- * Sanity checks.
- */
- if (reason == 0)
- msg_panic("%s: reason 0", myname);
-
- /*
- * Update the message structure and log the message disposition.
- */
- message->flags |= defer_append(BOUNCE_FLAG_KEEP, message->queue_id,
- address, "none", message->arrival_time,
- "%s", reason);
-}
+++ /dev/null
-/*++
-/* NAME
-/* qmgr_deliver 3
-/* SUMMARY
-/* deliver one pe-site queue entry to that site
-/* SYNOPSIS
-/* #include "qmgr.h"
-/*
-/* int qmgr_deliver_concurrency;
-/*
-/* int qmgr_deliver(transport, fp)
-/* QMGR_TRANSPORT *transport;
-/* VSTREAM *fp;
-/* DESCRIPTION
-/* This module implements the client side of the `queue manager
-/* to delivery agent' protocol. The queue manager uses
-/* asynchronous I/O so that it can drive multiple delivery
-/* agents in parallel. Depending on the outcome of a delivery
-/* attempt, the status of messages, queues and transports is
-/* updated.
-/*
-/* qmgr_deliver_concurrency is a global counter that says how
-/* many delivery processes are in use. This can be used, for
-/* example, to control the size of the `active' message queue.
-/*
-/* qmgr_deliver() executes when a delivery process announces its
-/* availability for the named transport. It arranges for delivery
-/* of a suitable queue entry. The \fIfp\fR argument specifies a
-/* stream that is connected to the delivery process. Upon completion
-/* of delivery (successful or not), the stream is closed, so that the
-/* delivery process is released.
-/* DIAGNOSTICS
-/* 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
-/*
-/* Scheduler enhancements:
-/* Patrik Rak
-/* Modra 6
-/* 155 00, Prague, Czech Republic
-/*--*/
-
-/* System library. */
-
-#include <sys_defs.h>
-#include <time.h>
-#include <string.h>
-
-/* Utility library. */
-
-#include <msg.h>
-#include <vstring.h>
-#include <vstream.h>
-#include <vstring_vstream.h>
-#include <events.h>
-#include <iostuff.h>
-
-/* Global library. */
-
-#include <mail_queue.h>
-#include <mail_proto.h>
-#include <recipient_list.h>
-#include <mail_params.h>
-#include <deliver_request.h>
-
-/* Application-specific. */
-
-#include "qmgr.h"
-
-int qmgr_deliver_concurrency;
-
- /*
- * Message delivery status codes.
- */
-#define DELIVER_STAT_OK 0 /* all recipients delivered */
-#define DELIVER_STAT_DEFER 1 /* try some recipients later */
-#define DELIVER_STAT_CRASH 2 /* mailer internal problem */
-
-/* qmgr_deliver_initial_reply - retrieve initial delivery process response */
-
-static int qmgr_deliver_initial_reply(VSTREAM *stream)
-{
- int stat;
-
- if (peekfd(vstream_fileno(stream)) < 0) {
- msg_warn("%s: premature disconnect", VSTREAM_PATH(stream));
- return (DELIVER_STAT_CRASH);
- } else if (mail_scan(stream, "%d", &stat) != 1) {
- msg_warn("%s: malformed response", VSTREAM_PATH(stream));
- return (DELIVER_STAT_CRASH);
- } else {
- return (stat ? DELIVER_STAT_DEFER : 0);
- }
-}
-
-/* qmgr_deliver_final_reply - retrieve final delivery process response */
-
-static int qmgr_deliver_final_reply(VSTREAM *stream, VSTRING *reason)
-{
- int stat;
-
- if (peekfd(vstream_fileno(stream)) < 0) {
- msg_warn("%s: premature disconnect", VSTREAM_PATH(stream));
- return (DELIVER_STAT_CRASH);
- } else if (mail_scan(stream, "%s %d", reason, &stat) != 2) {
- msg_warn("%s: malformed response", VSTREAM_PATH(stream));
- return (DELIVER_STAT_CRASH);
- } else {
- return (stat ? DELIVER_STAT_DEFER : 0);
- }
-}
-
-/* qmgr_deliver_send_request - send delivery request to delivery process */
-
-static int qmgr_deliver_send_request(QMGR_ENTRY *entry, VSTREAM *stream)
-{
- QMGR_RCPT_LIST list = entry->rcpt_list;
- QMGR_RCPT *recipient;
- QMGR_MESSAGE *message = entry->message;
- char *cp;
-
- /*
- * With local delivery, the queue name is user@nexthop, so that we can
- * implement per-recipient concurrency limits. The delivery agent
- * protocol expects nexthop only.
- */
- mail_print(stream, "%d %s %s %ld %ld %s %s %s %s %ld",
- message->inspect_xport ? DEL_REQ_FLAG_BOUNCE : DEL_REQ_FLAG_DEFLT,
- message->queue_name, message->queue_id,
- message->data_offset, message->data_size,
- (cp = strrchr(entry->queue->name, '@')) != 0 && cp[1] ? cp + 1 :
- entry->queue->name, message->sender,
- message->errors_to, message->return_receipt,
- message->arrival_time);
- for (recipient = list.info; recipient < list.info + list.len; recipient++)
- mail_print(stream, "%ld %s", recipient->offset, recipient->address);
- mail_print(stream, "%s", "0");
- if (vstream_fflush(stream) != 0) {
- msg_warn("write to process (%s): %m", entry->queue->transport->name);
- return (-1);
- } else {
- if (msg_verbose)
- msg_info("qmgr_deliver: site `%s'", entry->queue->name);
- return (0);
- }
-}
-
-/* qmgr_deliver_abort - transport response watchdog */
-
-static void qmgr_deliver_abort(int unused_event, char *context)
-{
- QMGR_ENTRY *entry = (QMGR_ENTRY *) context;
- QMGR_QUEUE *queue = entry->queue;
- QMGR_TRANSPORT *transport = queue->transport;
- QMGR_MESSAGE *message = entry->message;
-
- msg_fatal("%s: timeout receiving delivery status from transport: %s",
- message->queue_id, transport->name);
-}
-
-/* qmgr_deliver_update - process delivery status report */
-
-static void qmgr_deliver_update(int unused_event, char *context)
-{
- QMGR_ENTRY *entry = (QMGR_ENTRY *) context;
- QMGR_QUEUE *queue = entry->queue;
- QMGR_TRANSPORT *transport = queue->transport;
- QMGR_MESSAGE *message = entry->message;
- VSTRING *reason = vstring_alloc(1);
- int status;
-
- /*
- * The message transport has responded. Stop the watchdog timer.
- */
- event_cancel_timer(qmgr_deliver_abort, context);
-
- /*
- * Retrieve the delivery agent status report. The numerical status code
- * indicates if delivery should be tried again. The reason text is sent
- * only when a site should be avoided for a while, so that the queue
- * manager can log why it does not even try to schedule delivery to the
- * affected recipients.
- */
- status = qmgr_deliver_final_reply(entry->stream, reason);
-
- /*
- * The mail delivery process failed for some reason (although delivery
- * may have been successful). Back off with this transport type for a
- * while. Dispose of queue entries for this transport that await
- * selection (the todo lists). Stay away from queue entries that have
- * been selected (the busy lists), or we would have dangling pointers.
- * The queue itself won't go away before we dispose of the current queue
- * entry.
- */
- if (status == DELIVER_STAT_CRASH) {
- message->flags |= DELIVER_STAT_DEFER;
- qmgr_transport_throttle(transport, "unknown mail transport error");
- qmgr_defer_transport(transport, transport->reason);
- }
-
- /*
- * This message must be tried again.
- *
- * If we have a problem talking to this site, back off with this site for a
- * while; dispose of queue entries for this site that await selection
- * (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.
- */
- if (status == DELIVER_STAT_DEFER) {
- message->flags |= DELIVER_STAT_DEFER;
- if (VSTRING_LEN(reason)) {
- qmgr_queue_throttle(queue, vstring_str(reason));
- if (queue->window == 0)
- qmgr_defer_todo(queue, queue->reason);
- }
- }
-
- /*
- * No problems detected. Mark the transport and queue as alive. The queue
- * itself won't go away before we dispose of the current queue entry.
- */
- if (status == 0) {
- qmgr_transport_unthrottle(transport);
- qmgr_queue_unthrottle(queue);
- }
-
- /*
- * Release the delivery process, and give some other queue entry a chance
- * to be delivered. When all recipients for a message have been tried,
- * decide what to do next with this message: defer, bounce, delete.
- */
- event_disable_readwrite(vstream_fileno(entry->stream));
- if (vstream_fclose(entry->stream) != 0)
- msg_warn("qmgr_deliver_update: close delivery stream: %m");
- entry->stream = 0;
- qmgr_deliver_concurrency--;
- qmgr_entry_done(entry, QMGR_QUEUE_BUSY);
- vstring_free(reason);
-}
-
-/* qmgr_deliver - deliver one per-site queue entry */
-
-void qmgr_deliver(QMGR_TRANSPORT *transport, VSTREAM *stream)
-{
- QMGR_ENTRY *entry;
-
- /*
- * Find out if this delivery process is really available. Once elected,
- * the delivery process is supposed to express its happiness. If there is
- * a problem, wipe the pending deliveries for this transport. This
- * routine runs in response to an external event, so it does not run
- * while some other queue manipulation is happening.
- */
- if (qmgr_deliver_initial_reply(stream) != 0) {
- qmgr_transport_throttle(transport, "mail transport unavailable");
- qmgr_defer_transport(transport, transport->reason);
- (void) vstream_fclose(stream);
- return;
- }
-
- /*
- * Find a suitable queue entry. Things may have changed since this
- * transport was allocated. If no suitable entry is found,
- * unceremoniously disconnect from the delivery process. The delivery
- * agent request reading routine is prepared for the queue manager to
- * change its mind for no apparent reason.
- */
- if ((entry = qmgr_job_entry_select(transport)) == 0) {
- (void) vstream_fclose(stream);
- return;
- }
-
- /*
- * Send the queue file info and recipient info to the delivery process.
- * If there is a problem, wipe the pending deliveries for this transport.
- * This routine runs in response to an external event, so it does not run
- * while some other queue manipulation is happening.
- */
- if (qmgr_deliver_send_request(entry, stream) < 0) {
- qmgr_entry_unselect(entry);
- qmgr_transport_throttle(transport, "mail transport unavailable");
- qmgr_defer_transport(transport, transport->reason);
- /* warning: entry may be a dangling pointer here */
- (void) vstream_fclose(stream);
- return;
- }
-
- /*
- * If we get this far, go wait for the delivery status report.
- */
- qmgr_deliver_concurrency++;
- entry->stream = stream;
- event_enable_read(vstream_fileno(stream),
- qmgr_deliver_update, (char *) entry);
-
- /*
- * Guard against broken systems.
- */
- event_request_timer(qmgr_deliver_abort, (char *) entry, var_daemon_timeout);
-}
+++ /dev/null
-/*++
-/* NAME
-/* qmgr_enable
-/* SUMMARY
-/* enable dead transports or sites
-/* SYNOPSIS
-/* #include "qmgr.h"
-/*
-/* void qmgr_enable_queue(queue)
-/* QMGR_QUEUE *queue;
-/*
-/* QMGR_QUEUE *qmgr_enable_transport(transport)
-/* QMGR_TRANSPORT *transport;
-/*
-/* void qmgr_enable_all(void)
-/* DESCRIPTION
-/* This module purges dead in-core state information, effectively
-/* re-enabling delivery.
-/*
-/* qmgr_enable_queue() enables deliveries to the named dead site.
-/* Empty queues are destroyed. The existed solely to indicate that
-/* a site is dead.
-/*
-/* qmgr_enable_transport() enables deliveries via the specified
-/* transport, and calls qmgr_enable_queue() for each destination
-/* on that transport. Empty queues are destroyed.
-/*
-/* qmgr_enable_all() enables all transports and queues.
-/* See above for the side effects caused by doing this.
-/* BUGS
-/* The side effects of calling this module can be quite dramatic.
-/* DIAGNOSTICS
-/* Panic: consistency check failure. Fatal: out of memory.
-/* 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>
-
-/* Utility library. */
-
-#include <msg.h>
-#include <vstream.h>
-
-/* Application-specific. */
-
-#include "qmgr.h"
-
-/* qmgr_enable_all - enable transports and queues */
-
-void qmgr_enable_all(void)
-{
- QMGR_TRANSPORT *xport;
-
- if (msg_verbose)
- msg_info("qmgr_enable_all");
-
- /*
- * The number of transports does not change as a side effect, so this can
- * be a straightforward loop.
- */
- for (xport = qmgr_transport_list.next; xport; xport = xport->peers.next)
- qmgr_enable_transport(xport);
-}
-
-/* qmgr_enable_transport - defer todo entries for named transport */
-
-void qmgr_enable_transport(QMGR_TRANSPORT *transport)
-{
- QMGR_QUEUE *queue;
- QMGR_QUEUE *next;
-
- /*
- * Proceed carefully. Queues may disappear as a side effect.
- */
- if (transport->flags & QMGR_TRANSPORT_STAT_DEAD) {
- if (msg_verbose)
- msg_info("enable transport %s", transport->name);
- qmgr_transport_unthrottle(transport);
- }
- for (queue = transport->queue_list.next; queue; queue = next) {
- next = queue->peers.next;
- qmgr_enable_queue(queue);
- }
-}
-
-/* qmgr_enable_queue - enable and possibly delete queue */
-
-void qmgr_enable_queue(QMGR_QUEUE *queue)
-{
- if (queue->window == 0) {
- if (msg_verbose)
- msg_info("enable site %s/%s", queue->transport->name, queue->name);
- qmgr_queue_unthrottle(queue);
- }
- if (queue->todo.next == 0 && queue->busy.next == 0)
- qmgr_queue_done(queue);
-}
+++ /dev/null
-/*++
-/* NAME
-/* qmgr_entry 3
-/* SUMMARY
-/* per-site queue entries
-/* SYNOPSIS
-/* #include "qmgr.h"
-/*
-/* QMGR_ENTRY *qmgr_entry_create(peer, message)
-/* QMGR_PEER *peer;
-/* QMGR_MESSAGE *message;
-/*
-/* void qmgr_entry_done(entry, which)
-/* QMGR_ENTRY *entry;
-/* int which;
-/*
-/* QMGR_ENTRY *qmgr_entry_select(queue)
-/* QMGR_QUEUE *queue;
-/*
-/* void qmgr_entry_unselect(queue, entry)
-/* QMGR_QUEUE *queue;
-/* QMGR_ENTRY *entry;
-/* DESCRIPTION
-/* These routines add/delete/manipulate per-site message
-/* delivery requests.
-/*
-/* qmgr_entry_create() creates an entry for the named peer and message,
-/* and appends the entry to the peer's list and its queue's todo list.
-/* Filling in and cleaning up the recipients is the responsibility
-/* of the caller.
-/*
-/* qmgr_entry_done() discards a per-site queue entry. The
-/* \fIwhich\fR argument is either QMGR_QUEUE_BUSY for an entry
-/* of the site's `busy' list (i.e. queue entries that have been
-/* selected for actual delivery), or QMGR_QUEUE_TODO for an entry
-/* of the site's `todo' list (i.e. queue entries awaiting selection
-/* for actual delivery).
-/*
-/* qmgr_entry_done() discards its peer structure when the peer
-/* is not referenced anymore.
-/*
-/* qmgr_entry_done() triggers cleanup of the per-site queue when
-/* the site has no pending deliveries, and the site is either
-/* alive, or the site is dead and the number of in-core queues
-/* exceeds a configurable limit (see qmgr_queue_done()).
-/*
-/* qmgr_entry_done() triggers special action when the last in-core
-/* queue entry for a message is done with: either read more
-/* recipients from the queue file, delete the queue file, or move
-/* the queue file to the deferred queue; send bounce reports to the
-/* message originator (see qmgr_active_done()).
-/*
-/* qmgr_entry_select() selects first entry from the named
-/* per-site queue's `todo' list for actual delivery. The entry is
-/* moved to the queue's `busy' list: the list of messages being
-/* delivered. The entry is also removed from its peer list.
-/*
-/* qmgr_entry_unselect() takes the named entry off the named
-/* per-site queue's `busy' list and moves it to the queue's
-/* `todo' list. The entry is also appended to its peer list again.
-/* DIAGNOSTICS
-/* Panic: interface violations, internal inconsistencies.
-/* 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
-/*
-/* Scheduler enhancements:
-/* Patrik Rak
-/* Modra 6
-/* 155 00, Prague, Czech Republic
-/*--*/
-
-/* System library. */
-
-#include <sys_defs.h>
-#include <stdlib.h>
-#include <time.h>
-
-/* Utility library. */
-
-#include <msg.h>
-#include <mymalloc.h>
-#include <events.h>
-#include <vstream.h>
-
-/* Global library. */
-
-#include <mail_params.h>
-
-/* Application-specific. */
-
-#include "qmgr.h"
-
-/* qmgr_entry_select - select queue entry for delivery */
-
-QMGR_ENTRY *qmgr_entry_select(QMGR_PEER *peer)
-{
- QMGR_ENTRY *entry;
- QMGR_QUEUE *queue;
-
- if ((entry = peer->entry_list.next) != 0) {
- queue = entry->queue;
- QMGR_LIST_UNLINK(queue->todo, QMGR_ENTRY *, entry, queue_peers);
- queue->todo_refcount--;
- QMGR_LIST_APPEND(queue->busy, entry, queue_peers);
- queue->busy_refcount++;
- QMGR_LIST_UNLINK(peer->entry_list, QMGR_ENTRY *, entry, peer_peers);
- peer->job->selected_entries++;
- }
- return (entry);
-}
-
-/* qmgr_entry_unselect - unselect queue entry for delivery */
-
-void qmgr_entry_unselect(QMGR_ENTRY *entry)
-{
- QMGR_PEER *peer = entry->peer;
- QMGR_QUEUE *queue = entry->queue;
-
- QMGR_LIST_UNLINK(queue->busy, QMGR_ENTRY *, entry, queue_peers);
- queue->busy_refcount--;
- QMGR_LIST_APPEND(queue->todo, entry, queue_peers);
- queue->todo_refcount++;
- QMGR_LIST_APPEND(peer->entry_list, entry, peer_peers);
- peer->job->selected_entries--;
-}
-
-/* qmgr_entry_done - dispose of queue entry */
-
-void qmgr_entry_done(QMGR_ENTRY *entry, int which)
-{
- QMGR_QUEUE *queue = entry->queue;
- QMGR_MESSAGE *message = entry->message;
- QMGR_PEER *peer = entry->peer;
- QMGR_JOB *sponsor,
- *job = peer->job;
-
- /*
- * Take this entry off the in-core queue.
- */
- if (entry->stream != 0)
- msg_panic("qmgr_entry_done: file is open");
- if (which == QMGR_QUEUE_BUSY) {
- QMGR_LIST_UNLINK(queue->busy, QMGR_ENTRY *, entry, queue_peers);
- queue->busy_refcount--;
- } else if (which == QMGR_QUEUE_TODO) {
- QMGR_LIST_UNLINK(peer->entry_list, QMGR_ENTRY *, entry, peer_peers);
- job->selected_entries++;
- QMGR_LIST_UNLINK(queue->todo, QMGR_ENTRY *, entry, queue_peers);
- queue->todo_refcount--;
- } else {
- msg_panic("qmgr_entry_done: bad queue spec: %d", which);
- }
-
- /*
- * Decrease the in-core recipient counts and free the recipient list and
- * the structure itself.
- */
- job->rcpt_count -= entry->rcpt_list.len;
- message->rcpt_count -= entry->rcpt_list.len;
- qmgr_recipient_count -= entry->rcpt_list.len;
- qmgr_rcpt_list_free(&entry->rcpt_list);
- myfree((char *) entry);
-
- /*
- * Make sure that the transport of any retired or finishing job that
- * donated recipient slots to this message gets them back first. Then, if
- * possible, pass the remaining unused recipient slots to the next job in
- * the job list.
- */
- for (sponsor = message->job_list.next; sponsor; sponsor = sponsor->message_peers.next) {
- if (sponsor->rcpt_count >= sponsor->rcpt_limit || sponsor == job)
- continue;
- if (sponsor->stack_level < 0 || message->rcpt_offset == 0)
- qmgr_job_move_limits(sponsor);
- }
- if (message->rcpt_offset == 0) {
- qmgr_job_move_limits(job);
- }
-
- /*
- * When there are no more entries for this peer, discard the peer
- * structure.
- */
- peer->refcount--;
- if (peer->refcount == 0)
- qmgr_peer_free(peer);
-
- /*
- * When the in-core queue for this site is empty and when this site is
- * not dead, discard the in-core queue. When this site is dead, but the
- * number of in-core queues exceeds some threshold, get rid of this
- * in-core queue anyway, in order to avoid running out of memory.
- */
- if (queue->todo.next == 0 && queue->busy.next == 0) {
- if (queue->window == 0 && qmgr_queue_count > 2 * var_qmgr_rcpt_limit)
- qmgr_queue_unthrottle(queue);
- if (queue->window > 0)
- qmgr_queue_done(queue);
- }
-
- /*
- * Update the in-core message reference count. When the in-core message
- * structure has no more references, dispose of the message.
- */
- message->refcount--;
- if (message->refcount == 0)
- qmgr_active_done(message);
-}
-
-/* qmgr_entry_create - create queue todo entry */
-
-QMGR_ENTRY *qmgr_entry_create(QMGR_PEER *peer, QMGR_MESSAGE *message)
-{
- QMGR_ENTRY *entry;
- QMGR_QUEUE *queue = peer->queue;
-
- /*
- * Sanity check.
- */
- if (queue->window == 0)
- msg_panic("qmgr_entry_create: dead queue: %s", queue->name);
-
- entry = (QMGR_ENTRY *) mymalloc(sizeof(QMGR_ENTRY));
- entry->stream = 0;
- entry->message = message;
- qmgr_rcpt_list_init(&entry->rcpt_list);
- message->refcount++;
- entry->peer = peer;
- QMGR_LIST_APPEND(peer->entry_list, entry, peer_peers);
- peer->refcount++;
- entry->queue = queue;
- QMGR_LIST_APPEND(queue->todo, entry, queue_peers);
- queue->todo_refcount++;
- return (entry);
-}
+++ /dev/null
-/*++
-/* NAME
-/* qmgr_job 3
-/* SUMMARY
-/* per-transport jobs
-/* SYNOPSIS
-/* #include "qmgr.h"
-/*
-/* QMGR_JOB *qmgr_job_obtain(message, transport)
-/* QMGR_MESSAGE *message;
-/* QMGR_TRANSPORT *transport;
-/*
-/* void qmgr_job_free(job)
-/* QMGR_JOB *job;
-/*
-/* void qmgr_job_move_limits(job)
-/* QMGR_JOB *job;
-/*
-/* QMGR_ENTRY *qmgr_job_entry_select(transport)
-/* QMGR_TRANSPORT *transport;
-/* DESCRIPTION
-/* These routines add/delete/manipulate per-transport jobs.
-/* Each job corresponds to a specific transport and message.
-/* Each job has a peer list containing all pending delivery
-/* requests for that message.
-/*
-/* qmgr_job_obtain() finds an existing job for named message and
-/* transport combination. New empty job is created if no existing can
-/* be found. In either case, the job is prepared for assignement of
-/* (more) message recipients.
-/*
-/* qmgr_job_free() disposes of a per-transport job after all
-/* its entries have been taken care of. It is an error to dispose
-/* of a job that is still in use.
-/*
-/* qmgr_job_entry_select() attempts to find the next entry suitable
-/* for delivery. The job preempting algorithm is also exercised.
-/* If necessary, an attempt to read more recipients into core is made.
-/* This can result in creation of more job, queue and entry structures.
-/*
-/* qmgr_job_move_limits() takes care of proper distribution of the
-/* per-transport recipients limit among the per-transport jobs.
-/* Should be called whenever a job's recipient slot becomes available.
-/* DIAGNOSTICS
-/* Panic: consistency check failure.
-/* LICENSE
-/* .ad
-/* .fi
-/* The Secure Mailer license must be distributed with this software.
-/* AUTHOR(S)
-/* Patrik Rak
-/* patrik@raxoft.cz
-/*--*/
-
-/* System library. */
-
-#include <sys_defs.h>
-
-/* Utility library. */
-
-#include <msg.h>
-#include <events.h>
-#include <htable.h>
-#include <mymalloc.h>
-
-/* Application-specific. */
-
-#include "qmgr.h"
-
-/* Forward declarations */
-
-static void qmgr_job_pop(QMGR_JOB *);
-
-/* Helper macros */
-
-#define HAS_ENTRIES(job) ((job)->selected_entries < (job)->read_entries)
-
-/*
- * The MIN_ENTRIES macro may underestimate a lot but we can't use message->rcpt_unread
- * because we don't know if all those unread recipients go to our transport yet.
- */
-
-#define MIN_ENTRIES(job) ((job)->read_entries)
-#define MAX_ENTRIES(job) ((job)->read_entries + (job)->message->rcpt_unread)
-
-#define RESET_CANDIDATE_CACHE(transport) do { \
- (transport)->candidate_cache_time = (time_t) 0; \
- (transport)->candidate_cache = 0; \
- } while(0)
-
-/* qmgr_job_create - create and initialize message job structure */
-
-static QMGR_JOB *qmgr_job_create(QMGR_MESSAGE *message, QMGR_TRANSPORT *transport)
-{
- QMGR_JOB *job;
-
- job = (QMGR_JOB *) mymalloc(sizeof(QMGR_JOB));
- job->message = message;
- QMGR_LIST_APPEND(message->job_list, job, message_peers);
- htable_enter(transport->job_byname, message->queue_id, (char *) job);
- job->transport = transport;
- QMGR_LIST_INIT(job->transport_peers);
- job->peer_byname = htable_create(0);
- QMGR_LIST_INIT(job->peer_list);
- job->stack_level = 0;
- job->slots_used = 0;
- job->slots_available = 0;
- job->selected_entries = 0;
- job->read_entries = 0;
- job->rcpt_count = 0;
- job->rcpt_limit = 0;
- return (job);
-}
-
-/* qmgr_job_link - append the job to the job list, according to the time it was queued */
-
-static void qmgr_job_link(QMGR_JOB *job)
-{
- QMGR_TRANSPORT *transport = job->transport;
- QMGR_MESSAGE *message = job->message;
- QMGR_JOB *prev,
- *next,
- *unread;
- int delay;
-
- unread = transport->job_next_unread;
-
- /*
- * This may look inefficient but under normal operation it is expected
- * that the loop will stop right away, resulting in normal list append
- * below. However, this code is necessary for reviving retired jobs and
- * for jobs which are created long after the first chunk of recipients
- * was read in-core (either of these can happen only for multi-transport
- * messages).
- *
- * In case this is found unsatisfactory one day, it's possible to deploy
- * some smarter technique (using some form of lookup trees perhaps).
- */
- for (next = 0, prev = transport->job_list.prev; prev;
- next = prev, prev = prev->transport_peers.prev) {
- delay = message->queued_time - prev->message->queued_time;
- if (delay >= 0)
- break;
- if (unread == prev)
- unread = 0;
- }
-
- /*
- * Don't link the new job in front of the first job on the job list if
- * that job was already used for the regular delivery. This seems like a
- * subtle difference but it helps many invariants used at various other
- * places to remain true.
- */
- if (prev == 0 && next != 0 && next->slots_used != 0) {
- prev = next;
- next = next->transport_peers.next;
-
- /*
- * The following is not currently necessary but is done anyway for
- * the sake of consistency.
- */
- if (prev == transport->job_next_unread)
- unread = prev;
- }
-
- /*
- * Link the job into the proper place on the job list.
- */
- job->transport_peers.prev = prev;
- job->transport_peers.next = next;
- if (prev != 0)
- prev->transport_peers.next = job;
- else
- transport->job_list.next = job;
- if (next != 0)
- next->transport_peers.prev = job;
- else
- transport->job_list.prev = job;
-
- /*
- * Update the pointer to the first unread job on the job list and steal
- * the unused recipient slots from the old one.
- */
- if (unread == 0) {
- unread = transport->job_next_unread;
- transport->job_next_unread = job;
- if (unread != 0)
- qmgr_job_move_limits(unread);
- }
-
- /*
- * Get as much recipient slots as possible. The excess will be returned
- * to the transport pool as soon as the exact amount required is known
- * (which is usually after all recipients have been read in core).
- */
- if (transport->rcpt_unused > 0) {
- job->rcpt_limit += transport->rcpt_unused;
- message->rcpt_limit += transport->rcpt_unused;
- transport->rcpt_unused = 0;
- }
-}
-
-/* qmgr_job_find - lookup job associated with named message and transport */
-
-static QMGR_JOB *qmgr_job_find(QMGR_MESSAGE *message, QMGR_TRANSPORT *transport)
-{
-
- /*
- * Instead of traversing the message job list, we use single per
- * transport hash table. This is better (at least with respect to memory
- * usage) than having single hash table (usually almost empty) for each
- * message.
- */
- return ((QMGR_JOB *) htable_find(transport->job_byname, message->queue_id));
-}
-
-/* qmgr_job_obtain - find/create the appropriate job and make it ready for new recipients */
-
-QMGR_JOB *qmgr_job_obtain(QMGR_MESSAGE *message, QMGR_TRANSPORT *transport)
-{
- QMGR_JOB *job;
-
- /*
- * Try finding an existing job and revive it if it was already retired.
- * Create a new job for this transport/message combination otherwise.
- */
- if ((job = qmgr_job_find(message, transport)) != 0) {
- if (job->stack_level < 0) {
- job->stack_level = 0;
- qmgr_job_link(job);
- }
- } else {
- job = qmgr_job_create(message, transport);
- qmgr_job_link(job);
- }
-
- /*
- * Reset the candidate cache because of the new expected recipients.
- */
- RESET_CANDIDATE_CACHE(transport);
-
- return (job);
-}
-
-/* qmgr_job_move_limits - move unused recipient slots to the next job */
-
-void qmgr_job_move_limits(QMGR_JOB *job)
-{
- QMGR_TRANSPORT *transport = job->transport;
- QMGR_MESSAGE *message = job->message;
- QMGR_JOB *next = transport->job_next_unread;
- int rcpt_unused,
- msg_rcpt_unused;
-
- /*
- * Find next unread job on the job list if necessary. Cache it for later.
- * This makes the amortized efficiency of this routine O(1) per job.
- */
- if (job == next) {
- for (next = next->transport_peers.next; next; next = next->transport_peers.next)
- if (next->message->rcpt_offset != 0)
- break;
- transport->job_next_unread = next;
- }
-
- /*
- * Calculate the number of available unused slots.
- */
- rcpt_unused = job->rcpt_limit - job->rcpt_count;
- msg_rcpt_unused = message->rcpt_limit - message->rcpt_count;
- if (msg_rcpt_unused < rcpt_unused)
- rcpt_unused = msg_rcpt_unused;
-
- /*
- * Transfer the unused recipient slots back to the transport pool and to
- * the next not-fully-read job. Job's message limits are adjusted
- * accordingly.
- */
- if (rcpt_unused > 0) {
- job->rcpt_limit -= rcpt_unused;
- message->rcpt_limit -= rcpt_unused;
- transport->rcpt_unused += rcpt_unused;
- if (next != 0 && (rcpt_unused = transport->rcpt_unused) > 0) {
- next->rcpt_limit += rcpt_unused;
- next->message->rcpt_limit += rcpt_unused;
- transport->rcpt_unused = 0;
- }
- }
-}
-
-/* qmgr_job_retire - remove the job from the job list while waiting for recipients to deliver */
-
-static void qmgr_job_retire(QMGR_JOB *job)
-{
- char *myname = "qmgr_job_retire";
- QMGR_TRANSPORT *transport = job->transport;
-
- if (msg_verbose)
- msg_info("%s: %s", myname, job->message->queue_id);
-
- /*
- * Sanity checks.
- */
- if (job->stack_level != 0)
- msg_panic("%s: non-zero stack level (%d)", myname, job->stack_level);
-
- /*
- * Make sure this job is not cached as the next unread job for this
- * transport. The qmgr_entry_done() will make sure that the slots donated
- * by this job are moved back to the transport pool as soon as possible.
- */
- qmgr_job_move_limits(job);
-
- /*
- * Invalidate the candidate selection cache if necessary.
- */
- if (job == transport->candidate_cache
- || (transport->job_stack.next == 0 && job == transport->job_list.next))
- RESET_CANDIDATE_CACHE(transport);
-
- /*
- * Remove the job from the job list and mark it as retired.
- */
- QMGR_LIST_UNLINK(transport->job_list, QMGR_JOB *, job, transport_peers);
- job->stack_level = -1;
-}
-
-/* qmgr_job_free - release the job structure */
-
-void qmgr_job_free(QMGR_JOB *job)
-{
- char *myname = "qmgr_job_free";
- QMGR_MESSAGE *message = job->message;
- QMGR_TRANSPORT *transport = job->transport;
-
- if (msg_verbose)
- msg_info("%s: %s %s", myname, message->queue_id, transport->name);
-
- /*
- * Sanity checks.
- */
- if (job->rcpt_count)
- msg_panic("%s: non-zero recipient count (%d)", myname, job->rcpt_count);
-
- /*
- * Remove the job from the job stack if necessary.
- */
- if (job->stack_level > 0)
- qmgr_job_pop(job);
-
- /*
- * Return any remaining recipient slots back to the recipient slots pool.
- */
- qmgr_job_move_limits(job);
- if (job->rcpt_limit)
- msg_panic("%s: recipient slots leak (%d)", myname, job->rcpt_limit);
-
- /*
- * Invalidate the candidate selection cache if necessary.
- */
- if (job == transport->candidate_cache
- || (transport->job_stack.next == 0 && job == transport->job_list.next))
- RESET_CANDIDATE_CACHE(transport);
-
- /*
- * Unlink and discard the structure. Check if the job is still on the
- * transport job list or if it was already retired before unlinking it.
- */
- if (job->stack_level >= 0)
- QMGR_LIST_UNLINK(transport->job_list, QMGR_JOB *, job, transport_peers);
- QMGR_LIST_UNLINK(message->job_list, QMGR_JOB *, job, message_peers);
- htable_delete(transport->job_byname, message->queue_id, (void (*) (char *)) 0);
- htable_free(job->peer_byname, (void (*) (char *)) 0);
- myfree((char *) job);
-}
-
-/* qmgr_job_count_slots - maintain the delivery slot counters */
-
-static void qmgr_job_count_slots(QMGR_JOB *current, QMGR_JOB *job)
-{
-
- /*
- * Count the number of delivery slots used during the delivery of the
- * selected job. Also count the number of delivery slots available for
- * preemption.
- *
- * However, suppress any slot counting if we didn't start regular delivery
- * of the selected job yet.
- */
- if (job == current || job->slots_used > 0) {
- job->slots_used++;
- job->slots_available++;
- }
-
- /*
- * If the selected job is not the current job, its chance to be chosen by
- * qmgr_job_candidate() has slightly changed. If we would like to make
- * the candidate cache completely transparent, we should invalidate it
- * now.
- *
- * However, this case should usually happen only at "end of current job"
- * phase, when it's unlikely that the current job can be preempted
- * anyway. And because it's likely to happen quite often then, we
- * intentionally don't reset the cache, to safe some cycles. Furthermore,
- * the cache times out every second anyway.
- */
-#if 0
- if (job != current)
- RESET_CANDIDATE_CACHE(job->transport);
-#endif
-}
-
-/* qmgr_job_candidate - find best job candidate for preempting given job */
-
-static QMGR_JOB *qmgr_job_candidate(QMGR_JOB *current)
-{
- QMGR_TRANSPORT *transport = current->transport;
- QMGR_JOB *job,
- *best_job = 0;
- float score,
- best_score = 0.0;
- int max_slots,
- max_needed_entries,
- max_total_entries;
- int delay;
- time_t now = event_time();
-
- /*
- * Fetch the result directly from the cache if the cache is still valid.
- *
- * Note that we cache negative results too, so the cache must be invalidated
- * by resetting the cache time, not the candidate pointer itself.
- */
- if (transport->candidate_cache_time == now)
- return (transport->candidate_cache);
-
- /*
- * Estimate the minimum amount of delivery slots that can ever be
- * accumulated for the given job. All jobs that won't fit into these
- * slots are excluded from the candidate selection.
- */
- max_slots = (MIN_ENTRIES(current) - current->selected_entries
- + current->slots_available) / transport->slot_cost;
-
- /*
- * Select the candidate with best time_since_queued/total_recipients
- * score. In addition to jobs which don't meet the max_slots limit, skip
- * also jobs which don't have any selectable entries at the moment.
- *
- * By the way, the selection is reasonably resistant to OS time warping,
- * too.
- *
- * However, don't bother searching if we can't find anything suitable
- * anyway.
- */
- if (max_slots > 0) {
- for (job = transport->job_list.next; job; job = job->transport_peers.next) {
- if (job->stack_level != 0 || job == current)
- continue;
- max_total_entries = MAX_ENTRIES(job);
- max_needed_entries = max_total_entries - job->selected_entries;
- delay = now - job->message->queued_time + 1;
- if (max_needed_entries > 0 && max_needed_entries <= max_slots) {
- score = (float) delay / max_total_entries;
- if (score > best_score) {
- best_score = score;
- best_job = job;
- }
- }
-
- /*
- * Stop early if the best score is as good as it can get.
- */
- if (delay <= best_score)
- break;
- }
- }
-
- /*
- * Cache the result for later use.
- */
- transport->candidate_cache = best_job;
- transport->candidate_cache_time = now;
-
- return (best_job);
-}
-
-/* qmgr_job_preempt - preempt large message with smaller one */
-
-static QMGR_JOB *qmgr_job_preempt(QMGR_JOB *current)
-{
- char *myname = "qmgr_job_preempt";
- QMGR_TRANSPORT *transport = current->transport;
- QMGR_JOB *job;
- int rcpt_slots;
-
- /*
- * Suppress preempting completely if the current job is not big enough to
- * accumulate even the mimimal number of slots required.
- *
- * Also, don't look for better job candidate if there are no available slots
- * yet (the count can get negative due to the slot loans below).
- */
- if (current->slots_available <= 0
- || MAX_ENTRIES(current) < transport->min_slots * transport->slot_cost)
- return (current);
-
- /*
- * Find best candidate for preempting the current job.
- *
- * Note that the function also takes care that the candidate fits within the
- * number of delivery slots which the current job is still able to
- * accumulate.
- */
- if ((job = qmgr_job_candidate(current)) == 0)
- return (current);
-
- /*
- * Sanity checks.
- */
- if (job == current)
- msg_panic("%s: attempt to preempt itself", myname);
- if (job->stack_level != 0)
- msg_panic("%s: already on the job stack (%d)", myname, job->stack_level);
-
- /*
- * Check if there is enough available delivery slots accumulated to
- * preempt the current job.
- *
- * The slot loaning scheme improves the average message response time. Note
- * that the loan only allows the preemption happen earlier, though. It
- * doesn't affect how many slots have to be "paid" - the full number of
- * slots required has to be accumulated later before next preemption on
- * the same stack level can happen in either case.
- */
- if (current->slots_available / transport->slot_cost
- + transport->slot_loan
- < (MAX_ENTRIES(job) - job->selected_entries)
- * transport->slot_loan_factor / 100.0)
- return (current);
-
- /*
- * Preempt the current job.
- */
- QMGR_LIST_PREPEND(transport->job_stack, job, stack_peers);
- job->stack_level = current->stack_level + 1;
-
- /*
- * Add part of extra recipient slots reserved for preempting jobs to the
- * new current job if necessary.
- *
- * Note that transport->rcpt_unused is within <-rcpt_per_stack,0> in such
- * case.
- */
- if (job->message->rcpt_offset != 0) {
- rcpt_slots = (transport->rcpt_per_stack + transport->rcpt_unused + 1) / 2;
- job->rcpt_limit += rcpt_slots;
- job->message->rcpt_limit += rcpt_slots;
- transport->rcpt_unused -= rcpt_slots;
- }
-
- /*
- * Candidate cache must be reset because the current job has changed
- * completely.
- */
- RESET_CANDIDATE_CACHE(transport);
-
- if (msg_verbose)
- msg_info("%s: %s by %s", myname, current->message->queue_id,
- job->message->queue_id);
-
- return (job);
-}
-
-/* qmgr_job_pop - remove the job from the job preemption stack */
-
-static void qmgr_job_pop(QMGR_JOB *job)
-{
- QMGR_TRANSPORT *transport = job->transport;
- QMGR_JOB *parent;
-
- if (msg_verbose)
- msg_info("qmgr_job_pop: %s", job->message->queue_id);
-
- /*
- * Adjust the number of delivery slots available to preempt job's parent.
- *
- * Note that we intentionally do not adjust slots_used of the parent. Doing
- * so would decrease the maximum per message inflation factor if the
- * preemption appeared near the end of parent delivery.
- *
- * For the same reason we do not adjust parent's slots_available if the
- * parent is not the original parent preempted by the selected job (i.e.,
- * the original parent job has already completed).
- *
- * The special case when the head of the job list was preempted and then
- * delivered before the preempting job itself is taken care of too.
- * Otherwise we would decrease available slot counter of some job that
- * was not in fact preempted yet.
- */
- if (((parent = job->stack_peers.next) != 0
- || ((parent = transport->job_list.next) != 0 && parent->slots_used > 0))
- && job->stack_level == parent->stack_level + 1)
- parent->slots_available -= job->slots_used * transport->slot_cost;
-
- /*
- * Invalidate the candidate selection cache if necessary.
- */
- if (job == transport->job_stack.next)
- RESET_CANDIDATE_CACHE(transport);
-
- /*
- * Remove the job from the job stack and reinitialize the slot counters.
- */
- QMGR_LIST_UNLINK(transport->job_stack, QMGR_JOB *, job, stack_peers);
- job->stack_level = 0;
- job->slots_used = 0;
- job->slots_available = 0;
-}
-
-/* qmgr_job_peer_select - select next peer suitable for delivery */
-
-static QMGR_PEER *qmgr_job_peer_select(QMGR_JOB *job)
-{
- QMGR_PEER *peer;
- QMGR_MESSAGE *message = job->message;
-
- if (HAS_ENTRIES(job) && (peer = qmgr_peer_select(job)) != 0)
- return (peer);
-
- /*
- * Try reading in more recipients. Note that we do not try to read them
- * as soon as possible as that would decrease the chance of per-site
- * recipient grouping. We waited until reading more is really necessary.
- */
- if (message->rcpt_offset != 0 && message->rcpt_limit > message->rcpt_count) {
- qmgr_message_realloc(message);
- if (HAS_ENTRIES(job))
- return (qmgr_peer_select(job));
- }
- return (0);
-}
-
-/* qmgr_job_entry_select - select next entry suitable for delivery */
-
-QMGR_ENTRY *qmgr_job_entry_select(QMGR_TRANSPORT *transport)
-{
- QMGR_JOB *job,
- *current,
- *next;
- QMGR_PEER *peer;
- QMGR_ENTRY *entry;
-
- /*
- * Select the "current" job.
- */
- if ((current = transport->job_stack.next) == 0
- && (current = transport->job_list.next) == 0)
- return (0);
-
- /*
- * Exercise the preempting algorithm if enabled.
- *
- * The slot_cost equal to 1 causes the algorithm to degenerate and is
- * therefore disabled too.
- */
- if (transport->slot_cost >= 2)
- current = qmgr_job_preempt(current);
-
- /*
- * Select next entry suitable for delivery. First check the stack of
- * preempting jobs, then the list of all remaining jobs in FIFO order.
- *
- * Note that although the loops may look inefficient, they only serve as a
- * recovery mechanism when an entry of the current job itself can't be
- * selected due peer concurrency restrictions. In most cases some entry
- * of the current job itself is selected.
- *
- * Note that both loops also take care of getting the "stall" current job
- * (job with no entries currently available) out of the way if necessary.
- * Stall jobs can appear in case of multi-transport messages whose
- * recipients don't fit in-core at once. Some jobs created by such
- * message may have only few recipients and would block the job queue
- * until all other jobs of the message are delivered. Trying to read in
- * more recipients of such jobs each selection would also break the per
- * peer recipient grouping of the other jobs. That's why we retire such
- * jobs below.
- */
- for (job = transport->job_stack.next; job; job = next) {
- next = job->stack_peers.next;
- if ((peer = qmgr_job_peer_select(job)) != 0) {
- entry = qmgr_entry_select(peer);
- qmgr_job_count_slots(current, job);
-
- /*
- * In case we selected the very last job entry, remove the job
- * from the job stack and the job list right now.
- *
- * This action uses the assumption that once the job entry has been
- * selected, it can be unselected only before the message ifself
- * is deferred. Thus the job with all entries selected can't
- * re-appear with more entries available for selection again
- * (without reading in more entries from the queue file, which in
- * turn invokes qmgr_job_obtain() which re-links the job back on
- * the list if necessary).
- *
- * Note that qmgr_job_move_limits() transfers the recipients slots
- * correctly even if the job is unlinked from the job list thanks
- * to the job_next_unread caching.
- */
- if (!HAS_ENTRIES(job) && job->message->rcpt_offset == 0) {
- qmgr_job_pop(job);
- qmgr_job_retire(job);
- }
- return (entry);
- } else if (job == current && !HAS_ENTRIES(job)) {
- qmgr_job_pop(job);
- qmgr_job_retire(job);
- current = next ? next : transport->job_list.next;
- }
- }
-
- /*
- * Try the regular job list if there is nothing (suitable) on the job
- * stack.
- */
- for (job = transport->job_list.next; job; job = next) {
- next = job->transport_peers.next;
- if (job->stack_level != 0)
- continue;
- if ((peer = qmgr_job_peer_select(job)) != 0) {
- entry = qmgr_entry_select(peer);
- qmgr_job_count_slots(current, job);
-
- /*
- * In case we selected the very last job entry, remove the job
- * from the job list right away.
- */
- if (!HAS_ENTRIES(job) && job->message->rcpt_offset == 0)
- qmgr_job_retire(job);
- return (entry);
- } else if (job == current && !HAS_ENTRIES(job)) {
- qmgr_job_retire(job);
- current = next;
- }
- }
- return (0);
-}
+++ /dev/null
-/*++
-/* NAME
-/* qmgr_message 3
-/* SUMMARY
-/* in-core message structures
-/* SYNOPSIS
-/* #include "qmgr.h"
-/*
-/* int qmgr_message_count;
-/* int qmgr_recipient_count;
-/*
-/* QMGR_MESSAGE *qmgr_message_alloc(class, name, qflags)
-/* const char *class;
-/* const char *name;
-/* int qflags;
-/*
-/* QMGR_MESSAGE *qmgr_message_realloc(message)
-/* QMGR_MESSAGE *message;
-/*
-/* void qmgr_message_free(message)
-/* QMGR_MESSAGE *message;
-/*
-/* void qmgr_message_update_warn(message)
-/* QMGR_MESSAGE *message;
-/* DESCRIPTION
-/* This module performs en-gross operations on queue messages.
-/*
-/* qmgr_message_count is a global counter for the total number
-/* of in-core message structures (i.e. the total size of the
-/* `active' message queue).
-/*
-/* qmgr_recipient_count is a global counter for the total number
-/* of in-core recipient structures (i.e. the sum of all recipients
-/* in all in-core message structures).
-/*
-/* qmgr_message_alloc() creates an in-core message structure
-/* with sender and recipient information taken from the named queue
-/* file. A null result means the queue file could not be read or
-/* that the queue file contained incorrect information. A result
-/* QMGR_MESSAGE_LOCKED means delivery must be deferred. The number
-/* of recipients read from a queue file is limited by the global
-/* var_qmgr_rcpt_limit configuration parameter. When the limit
-/* is reached, the \fIrcpt_offset\fR structure member is set to
-/* the position where the read was terminated. Recipients are
-/* run through the resolver, and are assigned to destination
-/* queues. Recipients that cannot be assigned are deferred or
-/* bounced. Mail that has bounced twice is silently absorbed.
-/*
-/* qmgr_message_realloc() resumes reading recipients from the queue
-/* file, and updates the recipient list and \fIrcpt_offset\fR message
-/* structure members. A null result means that the file could not be
-/* read or that the file contained incorrect information. Recipient
-/* limit imposed this time is based on the position of the message
-/* job(s) on corresponding transport job list(s). It's considered
-/* an error to call this when the recipient slots can't be allocated.
-/*
-/* qmgr_message_free() destroys an in-core message structure and makes
-/* the resources available for reuse. It is an error to destroy
-/* a message structure that is still referenced by queue entry structures.
-/*
-/* qmgr_message_update_warn() takes a closed message, opens it, updates
-/* the warning field, and closes it again.
-/* DIAGNOSTICS
-/* Warnings: malformed message file. Fatal errors: out of memory.
-/* SEE ALSO
-/* envelope(3) message envelope parser
-/* 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
-/*
-/* Scheduler enhancements:
-/* Patrik Rak
-/* Modra 6
-/* 155 00, Prague, Czech Republic
-/*--*/
-
-/* System library. */
-
-#include <sys_defs.h>
-#include <sys/stat.h>
-#include <stdlib.h>
-#include <stdio.h> /* sscanf() */
-#include <fcntl.h>
-#include <errno.h>
-#include <unistd.h>
-#include <string.h>
-
-#ifdef STRCASECMP_IN_STRINGS_H
-#include <strings.h>
-#endif
-
-/* Utility library. */
-
-#include <msg.h>
-#include <events.h>
-#include <mymalloc.h>
-#include <vstring.h>
-#include <vstream.h>
-#include <split_at.h>
-#include <valid_hostname.h>
-#include <argv.h>
-#include <stringops.h>
-#include <myflock.h>
-
-/* Global library. */
-
-#include <dict.h>
-#include <mail_queue.h>
-#include <mail_params.h>
-#include <canon_addr.h>
-#include <record.h>
-#include <rec_type.h>
-#include <sent.h>
-#include <deliver_completed.h>
-#include <mail_addr_find.h>
-#include <opened.h>
-#include <resolve_local.h>
-
-/* Client stubs. */
-
-#include <resolve_clnt.h>
-
-/* Application-specific. */
-
-#include "qmgr.h"
-
-int qmgr_message_count;
-int qmgr_recipient_count;
-
-/* qmgr_message_create - create in-core message structure */
-
-static QMGR_MESSAGE *qmgr_message_create(const char *queue_name,
- const char *queue_id, int qflags)
-{
- QMGR_MESSAGE *message;
-
- message = (QMGR_MESSAGE *) mymalloc(sizeof(QMGR_MESSAGE));
- qmgr_message_count++;
- message->flags = 0;
- message->qflags = qflags;
- message->fp = 0;
- message->refcount = 0;
- message->single_rcpt = 0;
- message->arrival_time = 0;
- message->queued_time = event_time();
- message->data_offset = 0;
- message->queue_id = mystrdup(queue_id);
- message->queue_name = mystrdup(queue_name);
- message->sender = 0;
- message->errors_to = 0;
- message->return_receipt = 0;
- message->filter_xport = 0;
- message->inspect_xport = 0;
- message->data_size = 0;
- message->warn_offset = 0;
- message->warn_time = 0;
- message->rcpt_offset = 0;
- message->unread_offset = 0;
- qmgr_rcpt_list_init(&message->rcpt_list);
- message->rcpt_count = 0;
- message->rcpt_limit = var_qmgr_msg_rcpt_limit;
- message->rcpt_unread = 0;
- QMGR_LIST_INIT(message->job_list);
- return (message);
-}
-
-/* qmgr_message_close - close queue file */
-
-static void qmgr_message_close(QMGR_MESSAGE *message)
-{
- vstream_fclose(message->fp);
- message->fp = 0;
-}
-
-/* qmgr_message_open - open queue file */
-
-static int qmgr_message_open(QMGR_MESSAGE *message)
-{
-
- /*
- * Sanity check.
- */
- if (message->fp)
- msg_panic("%s: queue file is open", message->queue_id);
-
- /*
- * Open this queue file. Skip files that we cannot open. Back off when
- * the system appears to be running out of resources.
- */
- if ((message->fp = mail_queue_open(message->queue_name,
- message->queue_id,
- O_RDWR, 0)) == 0) {
- if (errno != ENOENT)
- msg_fatal("open %s %s: %m", message->queue_name, message->queue_id);
- msg_warn("open %s %s: %m", message->queue_name, message->queue_id);
- return (-1);
- }
- return (0);
-}
-
-/* qmgr_message_oldstyle_scan - extract required information from an old style queue file */
-
-static void qmgr_message_oldstyle_scan(QMGR_MESSAGE *message)
-{
- VSTRING *buf;
- long orig_offset,
- curr_offset,
- extra_offset;
- int rec_type;
- char *start;
-
- /*
- * Initialize. No early returns or we have a memory leak.
- */
- buf = vstring_alloc(100);
- if ((orig_offset = vstream_ftell(message->fp)) < 0)
- msg_fatal("vstream_ftell %s: %m", VSTREAM_PATH(message->fp));
-
- /*
- * Rewind to the very beginning to make sure we see all records.
- */
- if (vstream_fseek(message->fp, 0, SEEK_SET) < 0)
- msg_fatal("seek file %s: %m", VSTREAM_PATH(message->fp));
-
- /*
- * Scan through the old style queue file. Count the total number of
- * recipients and find the data/extra sections offsets. Note that the new
- * queue files require that data_size equals extra_offset - data_offset,
- * so we set data_size to this as well and ignore the size record itself
- * completely.
- */
- message->rcpt_unread = 0;
- do {
- if ((curr_offset = vstream_ftell(message->fp)) < 0)
- msg_fatal("vstream_ftell %s: %m", VSTREAM_PATH(message->fp));
- rec_type = rec_get(message->fp, buf, 0);
- start = vstring_str(buf);
- if (rec_type == REC_TYPE_DONE || rec_type == REC_TYPE_RCPT) {
- message->rcpt_unread++;
- } else if (rec_type == REC_TYPE_MESG) {
- if ((message->data_offset = vstream_ftell(message->fp)) < 0)
- msg_fatal("vstream_ftell %s: %m", VSTREAM_PATH(message->fp));
- if ((extra_offset = atol(start)) <= curr_offset)
- msg_fatal("bad extra offset %s file %s",
- start, VSTREAM_PATH(message->fp));
- if (vstream_fseek(message->fp, extra_offset, SEEK_SET) < 0)
- msg_fatal("seek file %s: %m", VSTREAM_PATH(message->fp));
- message->data_size = extra_offset - message->data_offset;
- }
- } while (rec_type > 0 && rec_type != REC_TYPE_END);
-
- /*
- * Clean up.
- */
- if (vstream_fseek(message->fp, orig_offset, SEEK_SET) < 0)
- msg_fatal("seek file %s: %m", VSTREAM_PATH(message->fp));
- vstring_free(buf);
-
- /*
- * Sanity checks. Verify that all required information was found,
- * including the queue file end marker.
- */
- if (message->data_offset == 0 || rec_type != REC_TYPE_END)
- msg_fatal("%s: envelope records out of order", message->queue_id);
-}
-
-/* qmgr_message_read - read envelope records */
-
-static int qmgr_message_read(QMGR_MESSAGE *message)
-{
- VSTRING *buf;
- long extra_offset;
- int rec_type;
- long curr_offset;
- long save_offset = message->rcpt_offset; /* save a flag */
- char *start;
- int recipient_limit;
-
- /*
- * Initialize. No early returns or we have a memory leak.
- */
- buf = vstring_alloc(100);
-
- /*
- * If we re-open this file, skip over on-file recipient records that we
- * already looked at, and refill the in-core recipient address list.
- *
- * For the first time, the message recipient limit is calculated from the
- * global recipient limit. This is to avoid reading little recipients
- * when the active queue is near empty. When the queue becomes full, only
- * the necessary amount is read in core. Such priming is necessary
- * because there are no message jobs yet.
- *
- * For the next time, the recipient limit is based solely on the message
- * jobs' positions in the job lists and/or job stacks.
- */
- if (message->rcpt_offset) {
- if (message->rcpt_list.len)
- msg_panic("%s: recipient list not empty on recipient reload", message->queue_id);
- if (message->rcpt_limit <= message->rcpt_count)
- msg_panic("%s: no recipient slots available", message->queue_id);
- if (vstream_fseek(message->fp, message->rcpt_offset, SEEK_SET) < 0)
- msg_fatal("seek file %s: %m", VSTREAM_PATH(message->fp));
- message->rcpt_offset = 0;
- recipient_limit = message->rcpt_limit - message->rcpt_count;
- } else {
- recipient_limit = var_qmgr_rcpt_limit - qmgr_recipient_count;
- if (recipient_limit < message->rcpt_limit)
- recipient_limit = message->rcpt_limit;
- }
- if (recipient_limit <= 0)
- msg_panic("%s: no recipient slots available", message->queue_id);
-
- /*
- * Read envelope records. XXX Rely on the front-end programs to enforce
- * record size limits. Read up to recipient_limit recipients from the
- * queue file, to protect against memory exhaustion. Recipient records
- * may appear before or after the message content, so we keep reading
- * from the queue file until we have enough recipients (rcpt_offset != 0)
- * and until we know where the message content starts (data_offset != 0).
- *
- * Note that the total recipient count record is accurate only for fresh
- * queue files. After some of the recipients are marked as done and the
- * queue file is deferred, it can be used as upper bound estimate only.
- * Fortunately, this poses no major problem on the scheduling algorithm,
- * as the only impact is that the already deferred messages are not
- * chosen by qmgr_job_candidate() as often as they could.
- */
- do {
- if ((curr_offset = vstream_ftell(message->fp)) < 0)
- msg_fatal("vstream_ftell %s: %m", VSTREAM_PATH(message->fp));
- if (curr_offset == message->data_offset && curr_offset > 0) {
- extra_offset = curr_offset + message->data_size;
- if (extra_offset <= curr_offset)
- msg_fatal("bad extra offset %ld file %s",
- extra_offset, VSTREAM_PATH(message->fp));
- if (vstream_fseek(message->fp, extra_offset, SEEK_SET) < 0)
- msg_fatal("seek file %s: %m", VSTREAM_PATH(message->fp));
- curr_offset = extra_offset;
- }
- rec_type = rec_get(message->fp, buf, 0);
- start = vstring_str(buf);
- if (rec_type == REC_TYPE_SIZE) {
- if (message->data_size == 0) {
- switch (sscanf(start, "%ld %ld %d", &message->data_size,
- &message->data_offset, &message->rcpt_unread)) {
- case 1:
-
- /*
- * Gather data_size, data_offset and rcpt_unread values
- * from the old style queue file.
- */
- qmgr_message_oldstyle_scan(message);
- break;
- case 3:
-
- /*
- * No extra work for new style queue files.
- */
- break;
- default:
- msg_fatal("%s: weird size record", message->queue_id);
- break;
- }
- }
- } else if (rec_type == REC_TYPE_TIME) {
- if (message->arrival_time == 0)
- message->arrival_time = atol(start);
- } else if (rec_type == REC_TYPE_FILT) {
- if (message->filter_xport == 0)
- message->filter_xport = mystrdup(start);
- } else if (rec_type == REC_TYPE_INSP) {
- if (message->inspect_xport == 0)
- message->inspect_xport = mystrdup(start);
- } else if (rec_type == REC_TYPE_FROM) {
- if (message->sender == 0) {
- message->sender = mystrdup(start);
- opened(message->queue_id, message->sender,
- message->data_size, message->rcpt_unread,
- "queue %s", message->queue_name);
- }
- } else if (rec_type == REC_TYPE_DONE) {
- if (curr_offset > message->unread_offset) {
- message->unread_offset = curr_offset;
- message->rcpt_unread--;
- }
- } else if (rec_type == REC_TYPE_RCPT) {
- if (message->rcpt_list.len < recipient_limit) {
- message->rcpt_unread--;
- qmgr_rcpt_list_add(&message->rcpt_list, curr_offset, start);
- if (message->rcpt_list.len >= recipient_limit) {
- if ((message->rcpt_offset = vstream_ftell(message->fp)) < 0)
- msg_fatal("vstream_ftell %s: %m",
- VSTREAM_PATH(message->fp));
- if (message->data_offset != 0
- && message->errors_to != 0
- && message->return_receipt != 0)
- break;
- }
- }
- } else if (rec_type == REC_TYPE_ERTO) {
- if (message->errors_to == 0) {
- message->errors_to = mystrdup(start);
- if (message->data_offset != 0
- && message->rcpt_offset != 0
- && message->return_receipt != 0)
- break;
- }
- } else if (rec_type == REC_TYPE_RRTO) {
- if (message->return_receipt == 0) {
- message->return_receipt = mystrdup(start);
- if (message->data_offset != 0
- && message->rcpt_offset != 0
- && message->errors_to != 0)
- break;
- }
- } else if (rec_type == REC_TYPE_WARN) {
- if (message->warn_offset == 0) {
- message->warn_offset = curr_offset;
- message->warn_time = atol(start);
- }
- }
- } while (rec_type > 0 && rec_type != REC_TYPE_END);
-
- /*
- * 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.
- */
- if (message->errors_to == 0)
- message->errors_to = mystrdup("");
- if (message->return_receipt == 0)
- message->return_receipt = mystrdup("");
-
- /*
- * Clean up.
- */
- vstring_free(buf);
-
- /*
- * Sanity checks. Verify that all required information was found,
- * including the queue file end marker.
- */
- if (message->rcpt_unread < 0
- || (message->rcpt_offset == 0 && message->rcpt_unread != 0)) {
- msg_warn("%s: rcpt count mismatch (%d)",
- message->queue_id, message->rcpt_unread);
- message->rcpt_unread = 0;
- }
- if (message->arrival_time == 0
- || message->sender == 0
- || message->data_offset == 0
- || (message->rcpt_offset == 0 && rec_type != REC_TYPE_END)) {
- msg_warn("%s: envelope records out of order", message->queue_id);
- message->rcpt_offset = save_offset; /* restore flag */
- message->rcpt_unread += message->rcpt_list.len;
- qmgr_rcpt_list_free(&message->rcpt_list);
- qmgr_rcpt_list_init(&message->rcpt_list);
- return (-1);
- } else {
- return (0);
- }
-}
-
-/* qmgr_message_update_warn - update the time of next delay warning */
-
-void qmgr_message_update_warn(QMGR_MESSAGE *message)
-{
-
- /*
- * XXX eventually this should let us schedule multiple warnings, right
- * now it just allows for one.
- */
- if (qmgr_message_open(message)
- || vstream_fseek(message->fp, message->warn_offset, SEEK_SET) < 0
- || rec_fprintf(message->fp, REC_TYPE_WARN, REC_TYPE_WARN_FORMAT, 0L) < 0
- || vstream_fflush(message->fp))
- msg_fatal("update queue file %s: %m", VSTREAM_PATH(message->fp));
- qmgr_message_close(message);
-}
-
-/* qmgr_message_sort_compare - compare recipient information */
-
-static int qmgr_message_sort_compare(const void *p1, const void *p2)
-{
- QMGR_RCPT *rcpt1 = (QMGR_RCPT *) p1;
- QMGR_RCPT *rcpt2 = (QMGR_RCPT *) p2;
- QMGR_QUEUE *queue1;
- QMGR_QUEUE *queue2;
- char *at1;
- char *at2;
- int result;
-
- /*
- * Compare most significant to least significant recipient attributes.
- */
- if ((queue1 = rcpt1->queue) != 0 && (queue2 = rcpt2->queue) != 0) {
-
- /*
- * Compare message transport.
- */
- if ((result = strcasecmp(queue1->transport->name,
- queue2->transport->name)) != 0)
- return (result);
-
- /*
- * Compare next-hop hostname.
- */
- if ((result = strcasecmp(queue1->name, queue2->name)) != 0)
- return (result);
- }
-
- /*
- * Compare recipient domain.
- */
- if ((at1 = strrchr(rcpt1->address, '@')) != 0
- && (at2 = strrchr(rcpt2->address, '@')) != 0
- && (result = strcasecmp(at1, at2)) != 0)
- return (result);
-
- /*
- * Compare recipient address.
- */
- return (strcasecmp(rcpt1->address, rcpt2->address));
-}
-
-/* qmgr_message_sort - sort message recipient addresses by domain */
-
-static void qmgr_message_sort(QMGR_MESSAGE *message)
-{
- qsort((char *) message->rcpt_list.info, message->rcpt_list.len,
- sizeof(message->rcpt_list.info[0]), qmgr_message_sort_compare);
- if (msg_verbose) {
- QMGR_RCPT_LIST list = message->rcpt_list;
- QMGR_RCPT *rcpt;
-
- msg_info("start sorted recipient list");
- for (rcpt = list.info; rcpt < list.info + list.len; rcpt++)
- msg_info("qmgr_message_sort: %s", rcpt->address);
- msg_info("end sorted recipient list");
- }
-}
-
-/* qmgr_message_resolve - resolve recipients */
-
-static void qmgr_message_resolve(QMGR_MESSAGE *message)
-{
- static ARGV *defer_xport_argv;
- QMGR_RCPT_LIST list = message->rcpt_list;
- QMGR_RCPT *recipient;
- QMGR_TRANSPORT *transport = 0;
- QMGR_QUEUE *queue = 0;
- RESOLVE_REPLY reply;
- const char *newloc;
- char *at;
- char **cpp;
- char *domain;
- const char *junk;
- char *nexthop;
- int len;
-
-#define STREQ(x,y) (strcasecmp(x,y) == 0)
-#define STR vstring_str
-#define LEN VSTRING_LEN
-#define UPDATE(ptr,new) { myfree(ptr); ptr = mystrdup(new); }
-
- resolve_clnt_init(&reply);
- for (recipient = list.info; recipient < list.info + list.len; recipient++) {
-
- /*
- * This may be a bit late in the game, but it is the most convenient
- * place to scrutinize the destination address syntax. We have a
- * complete queue file, so bouncing is easy. That luxury is not
- * available to the cleanup service. The main issue is that we want
- * to have this test in one place, instead of having to do this in
- * every front-ent program.
- */
- if ((at = strrchr(recipient->address, '@')) != 0
- && (at + 1)[strspn(at + 1, "[]0123456789.")] != 0
- && valid_hostname(at + 1) == 0) {
- qmgr_bounce_recipient(message, recipient,
- "bad host/domain syntax: \"%s\"", at + 1);
- continue;
- }
-
- /*
- * Resolve the destination to (transport, nexthop, address). The
- * result address may differ from the one specified by the sender.
- */
- resolve_clnt_query(recipient->address, &reply);
- if (message->filter_xport) {
- vstring_strcpy(reply.transport, message->filter_xport);
- if ((nexthop = split_at(STR(reply.transport), ':')) == 0
- || *nexthop == 0)
- nexthop = var_myhostname;
- vstring_strcpy(reply.nexthop, nexthop);
- } else {
- if (!STREQ(recipient->address, STR(reply.recipient)))
- UPDATE(recipient->address, STR(reply.recipient));
- }
-
- /*
- * XXX The nexthop destination is also used as lookup key for the
- * per-destination queue. Fold the nexthop to lower case so that we
- * don't have multiple queues for the same site.
- */
- lowercase(STR(reply.nexthop));
-
- /*
- * Bounce recipients that have moved. We do it here instead of in the
- * local delivery agent. The benefit is that we can bounce mail for
- * virtual addresses, not just local addresses only, and that there
- * is no need to run a local delivery agent just for the sake of
- * relocation notices. The downside is that this table has no effect
- * on local alias expansion results, so that mail will have to make
- * almost an entire iteration through the mail system.
- */
-#define IGNORE_ADDR_EXTENSION ((char **) 0)
-
- if (qmgr_relocated != 0) {
- if ((newloc = mail_addr_find(qmgr_relocated, recipient->address,
- IGNORE_ADDR_EXTENSION)) != 0) {
- qmgr_bounce_recipient(message, recipient,
- "user has moved to %s", newloc);
- continue;
- } else if (dict_errno != 0) {
- qmgr_defer_recipient(message, recipient->address,
- "relocated map lookup failure");
- continue;
- }
- }
-
- /*
- * Bounce mail to non-existent users in virtual domains.
- */
- if (qmgr_virtual != 0
- && (at = strrchr(recipient->address, '@')) != 0
- && !resolve_local(at + 1)) {
- domain = lowercase(mystrdup(at + 1));
- junk = maps_find(qmgr_virtual, domain, 0);
- myfree(domain);
- if (junk) {
- qmgr_bounce_recipient(message, recipient,
- "unknown user: \"%s\"", recipient->address);
- continue;
- }
- }
-
- /*
- * Bounce recipient addresses that start with `-'. External commands
- * may misinterpret such addresses as command-line options.
- *
- * In theory I could say people should always carefully set up their
- * master.cf pipe mailer entries with `--' before the first
- * non-option argument, but mistakes will happen regardless.
- *
- * Therefore the protection is put in place here, in the queue manager,
- * where it cannot be bypassed.
- */
- if (var_allow_min_user == 0 && recipient->address[0] == '-') {
- qmgr_bounce_recipient(message, recipient,
- "invalid recipient syntax: \"%s\"",
- recipient->address);
- continue;
- }
-
- /*
- * Queues are identified by the transport name and by the next-hop
- * hostname. When the destination is local (no next hop), derive the
- * queue name from the recipient name. XXX Should split the address
- * on the recipient delimiter if one is defined, but doing a proper
- * job requires knowledge of local aliases. Yuck! I don't want to
- * duplicate delivery-agent specific knowledge in the queue manager.
- * XXX The nexthop field is overloaded to serve as destination and as
- * queue name. Should have separate fields for queue name and for
- * destination.
- */
- if ((at = strrchr(STR(reply.recipient), '@')) == 0
- || resolve_local(at + 1)) {
- len = (at != 0 ? (at - STR(reply.recipient))
- : strlen(STR(reply.recipient)));
- VSTRING_SPACE(reply.nexthop, len + 1);
- memmove(STR(reply.nexthop) + len + 1, STR(reply.nexthop),
- LEN(reply.nexthop) + 1);
- memcpy(STR(reply.nexthop), STR(reply.recipient), len);
- STR(reply.nexthop)[len] = '@';
- lowercase(STR(reply.nexthop));
-
- /*
- * Discard mail to the local double bounce address here, so this
- * system can run without a local delivery agent. They'd still
- * have to configure something for mail directed to the local
- * postmaster, though, but that is an RFC requirement anyway.
- */
- if (strncasecmp(STR(reply.recipient), var_double_bounce_sender,
- at - STR(reply.recipient)) == 0
- && !var_double_bounce_sender[at - STR(reply.recipient)]) {
- sent(message->queue_id, recipient->address,
- "none", message->arrival_time, "discarded");
- deliver_completed(message->fp, recipient->offset);
- msg_warn("%s: undeliverable postmaster notification discarded",
- message->queue_id);
- continue;
- }
- }
-
- /*
- * Optionally defer deliveries over specific transports, unless the
- * restriction is lifted temporarily.
- */
- if (*var_defer_xports && (message->qflags & QMGR_FLUSH_DEAD) == 0) {
- if (defer_xport_argv == 0)
- defer_xport_argv = argv_split(var_defer_xports, " \t\r\n,");
- for (cpp = defer_xport_argv->argv; *cpp; cpp++)
- if (strcasecmp(*cpp, STR(reply.transport)) == 0)
- break;
- if (*cpp) {
- qmgr_defer_recipient(message, recipient->address,
- "deferred transport");
- continue;
- }
- }
-
- /*
- * XXX Gross hack alert. We want to group recipients by transport and
- * by next-hop hostname, in order to minimize the number of network
- * transactions. However, it would be wasteful to have an in-memory
- * resolver reply structure for each in-core recipient. Instead, we
- * bind each recipient to an in-core queue instance which is needed
- * anyway. That gives all information needed for recipient grouping.
- */
-
- /*
- * Look up or instantiate the proper transport.
- */
- if (transport == 0 || !STREQ(transport->name, STR(reply.transport))) {
- if ((transport = qmgr_transport_find(STR(reply.transport))) == 0)
- transport = qmgr_transport_create(STR(reply.transport));
- queue = 0;
- }
-
- /*
- * This transport is dead. Defer delivery to this recipient.
- */
- if ((transport->flags & QMGR_TRANSPORT_STAT_DEAD) != 0) {
- qmgr_defer_recipient(message, recipient->address, transport->reason);
- continue;
- }
-
- /*
- * This transport is alive. Find or instantiate a queue for this
- * recipient.
- */
- if (queue == 0 || !STREQ(queue->name, STR(reply.nexthop))) {
- if ((queue = qmgr_queue_find(transport, STR(reply.nexthop))) == 0)
- queue = qmgr_queue_create(transport, STR(reply.nexthop));
- }
-
- /*
- * This queue is dead. Defer delivery to this recipient.
- */
- if (queue->window == 0) {
- qmgr_defer_recipient(message, recipient->address, queue->reason);
- continue;
- }
-
- /*
- * This queue is alive. Bind this recipient to this queue instance.
- */
- recipient->queue = queue;
- }
- resolve_clnt_free(&reply);
-}
-
-/* qmgr_message_assign - assign recipients to specific delivery requests */
-
-static void qmgr_message_assign(QMGR_MESSAGE *message)
-{
- QMGR_RCPT_LIST list = message->rcpt_list;
- QMGR_RCPT *recipient;
- QMGR_ENTRY *entry = 0;
- QMGR_QUEUE *queue;
- QMGR_JOB *job = 0;
- QMGR_PEER *peer = 0;
-
- /*
- * Try to bundle as many recipients in a delivery request as we can. When
- * the recipient resolves to the same site and transport as the previous
- * recipient, do not create a new queue entry, just move that recipient
- * to the recipient list of the existing queue entry. All this provided
- * that we do not exceed the transport-specific limit on the number of
- * recipients per transaction. Skip recipients with a dead transport or
- * destination.
- */
-#define LIMIT_OK(limit, count) ((limit) == 0 || ((count) < (limit)))
-
- for (recipient = list.info; recipient < list.info + list.len; recipient++) {
- if ((queue = recipient->queue) != 0) {
- if (message->single_rcpt || entry == 0 || entry->queue != queue
- || !LIMIT_OK(queue->transport->recipient_limit,
- entry->rcpt_list.len)) {
- if (job == 0 || queue->transport != job->transport) {
- job = qmgr_job_obtain(message, queue->transport);
- peer = 0;
- }
- if (peer == 0 || queue != peer->queue) {
- if ((peer = qmgr_peer_find(job, queue)) == 0)
- peer = qmgr_peer_create(job, queue);
- }
- entry = qmgr_entry_create(peer, message);
- job->read_entries++;
- }
- qmgr_rcpt_list_add(&entry->rcpt_list, recipient->offset, recipient->address);
- job->rcpt_count++;
- message->rcpt_count++;
- qmgr_recipient_count++;
- }
- }
- qmgr_rcpt_list_free(&message->rcpt_list);
- qmgr_rcpt_list_init(&message->rcpt_list);
-}
-
-/* qmgr_message_move_limits - recycle unused recipient slots */
-
-static void qmgr_message_move_limits(QMGR_MESSAGE *message)
-{
- QMGR_JOB *job;
-
- for (job = message->job_list.next; job; job = job->message_peers.next)
- qmgr_job_move_limits(job);
-}
-
-/* qmgr_message_free - release memory for in-core message structure */
-
-void qmgr_message_free(QMGR_MESSAGE *message)
-{
- QMGR_JOB *job;
-
- if (message->refcount != 0)
- msg_panic("qmgr_message_free: reference len: %d", message->refcount);
- if (message->fp)
- msg_panic("qmgr_message_free: queue file is open");
- while ((job = message->job_list.next) != 0)
- qmgr_job_free(job);
- myfree(message->queue_id);
- myfree(message->queue_name);
- if (message->sender)
- myfree(message->sender);
- if (message->errors_to)
- myfree(message->errors_to);
- if (message->return_receipt)
- myfree(message->return_receipt);
- if (message->filter_xport)
- myfree(message->filter_xport);
- if (message->inspect_xport)
- myfree(message->inspect_xport);
- qmgr_rcpt_list_free(&message->rcpt_list);
- qmgr_message_count--;
- myfree((char *) message);
-}
-
-/* qmgr_message_alloc - create in-core message structure */
-
-QMGR_MESSAGE *qmgr_message_alloc(const char *queue_name, const char *queue_id,
- int qflags)
-{
- char *myname = "qmgr_message_alloc";
- QMGR_MESSAGE *message;
-
- if (msg_verbose)
- msg_info("%s: %s %s", myname, queue_name, queue_id);
-
- /*
- * Create an in-core message structure.
- */
- message = qmgr_message_create(queue_name, queue_id, qflags);
-
- /*
- * Extract message envelope information: time of arrival, sender address,
- * recipient addresses. Skip files with malformed envelope information.
- */
-#define QMGR_LOCK_MODE (MYFLOCK_OP_EXCLUSIVE | MYFLOCK_OP_NOWAIT)
-
- if (qmgr_message_open(message) < 0) {
- qmgr_message_free(message);
- return (0);
- }
- if (myflock(vstream_fileno(message->fp), INTERNAL_LOCK, QMGR_LOCK_MODE) < 0) {
- msg_info("%s: skipped, still being delivered", queue_id);
- qmgr_message_close(message);
- qmgr_message_free(message);
- return (QMGR_MESSAGE_LOCKED);
- }
- if (qmgr_message_read(message) < 0) {
- qmgr_message_close(message);
- qmgr_message_free(message);
- return (0);
- } else {
-
- /*
- * Reset the defer log. This code should not be here, but we must
- * reset the defer log *after* acquiring the exclusive lock on the
- * queue file and *before* resolving new recipients. Since all those
- * operations are encapsulated so nicely by this routine, the defer
- * log reset has to be done here as well.
- */
- if (mail_queue_remove(MAIL_QUEUE_DEFER, queue_id) && errno != ENOENT)
- msg_fatal("%s: %s: remove %s %s: %m", myname,
- queue_id, MAIL_QUEUE_DEFER, queue_id);
- qmgr_message_sort(message);
- qmgr_message_resolve(message);
- qmgr_message_sort(message);
- qmgr_message_assign(message);
- qmgr_message_close(message);
- if (message->rcpt_offset == 0)
- qmgr_message_move_limits(message);
- return (message);
- }
-}
-
-/* qmgr_message_realloc - refresh in-core message structure */
-
-QMGR_MESSAGE *qmgr_message_realloc(QMGR_MESSAGE *message)
-{
- char *myname = "qmgr_message_realloc";
-
- /*
- * Sanity checks.
- */
- if (message->rcpt_offset <= 0)
- msg_panic("%s: invalid offset: %ld", myname, message->rcpt_offset);
- if (msg_verbose)
- msg_info("%s: %s %s offset %ld", myname, message->queue_name,
- message->queue_id, message->rcpt_offset);
-
- /*
- * Extract recipient addresses. Skip files with malformed envelope
- * information.
- */
- if (qmgr_message_open(message) < 0)
- return (0);
- if (qmgr_message_read(message) < 0) {
- qmgr_message_close(message);
- return (0);
- } else {
- qmgr_message_sort(message);
- qmgr_message_resolve(message);
- qmgr_message_sort(message);
- qmgr_message_assign(message);
- qmgr_message_close(message);
- if (message->rcpt_offset == 0)
- qmgr_message_move_limits(message);
- return (message);
- }
-}
+++ /dev/null
-/*++
-/* NAME
-/* qmgr_move 3
-/* SUMMARY
-/* move queue entries to another queue
-/* SYNOPSIS
-/* #include "qmgr.h"
-/*
-/* void qmgr_move(from, to, time_stamp)
-/* const char *from;
-/* const char *to;
-/* time_t time_stamp;
-/* DESCRIPTION
-/* The \fBqmgr_move\fR routine scans the \fIfrom\fR queue for entries
-/* with valid queue names and moves them to the \fIto\fR queue.
-/* If \fItime_stamp\fR is non-zero, the queue file time stamps are
-/* set to the specified value.
-/* Entries with invalid names are left alone. No attempt is made to
-/* look for other badness such as multiple links or weird file types.
-/* These issues are dealt with when a queue file is actually opened.
-/* 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 <sys/stat.h>
-#include <string.h>
-#include <utime.h>
-
-/* Utility library. */
-
-#include <msg.h>
-#include <scan_dir.h>
-#include <recipient_list.h>
-
-/* Global library. */
-
-#include <mail_queue.h>
-#include <mail_scan_dir.h>
-
-/* Application-specific. */
-
-#include "qmgr.h"
-
-/* qmgr_move - move queue entries to another queue, leave bad files alone */
-
-void qmgr_move(const char *src_queue, const char *dst_queue,
- time_t time_stamp)
-{
- char *myname = "qmgr_move";
- SCAN_DIR *queue_dir;
- char *queue_id;
- struct utimbuf tbuf;
- const char *path;
-
- if (strcmp(src_queue, dst_queue) == 0)
- msg_panic("%s: source queue %s is destination", myname, src_queue);
- if (msg_verbose)
- msg_info("start move queue %s -> %s", src_queue, dst_queue);
-
- queue_dir = scan_dir_open(src_queue);
- while ((queue_id = mail_scan_dir_next(queue_dir)) != 0) {
- if (mail_queue_id_ok(queue_id)) {
- if (time_stamp > 0) {
- tbuf.actime = tbuf.modtime = time_stamp;
- path = mail_queue_path((VSTRING *) 0, src_queue, queue_id);
- if (utime(path, &tbuf) < 0)
- msg_fatal("%s: update %s time stamps: %m", myname, path);
- }
- if (mail_queue_rename(queue_id, src_queue, dst_queue))
- msg_fatal("%s: rename %s from %s to %s: %m",
- myname, queue_id, src_queue, dst_queue);
- if (msg_verbose)
- msg_info("%s: moved %s from %s to %s",
- myname, queue_id, src_queue, dst_queue);
- } else {
- msg_warn("%s: ignored: queue %s id %s",
- myname, src_queue, queue_id);
- }
- }
- scan_dir_close(queue_dir);
-
- if (msg_verbose)
- msg_info("end move queue %s -> %s", src_queue, dst_queue);
-}
+++ /dev/null
-/*++
-/* NAME
-/* qmgr_peer 3
-/* SUMMARY
-/* per-job peers
-/* SYNOPSIS
-/* #include "qmgr.h"
-/*
-/* QMGR_PEER *qmgr_peer_create(job, queue)
-/* QMGR_JOB *job;
-/* QMGR_QUEUE *queue;
-/*
-/* QMGR_PEER *qmgr_peer_find(job, queue)
-/* QMGR_JOB *job;
-/* QMGR_QUEUE *queue;
-/*
-/* void qmgr_peer_free(peer)
-/* QMGR_PEER *peer;
-/*
-/* QMGR_PEER *qmgr_peer_select(job)
-/* QMGR_JOB *job;
-/*
-/* DESCRIPTION
-/* These routines add/delete/manipulate per-job peers.
-/* Each queue corresponds to a specific job and destination.
-/* It is similar to per-transport queue structure, but groups
-/* only the entries of the given job.
-/*
-/* qmgr_peer_create() creates an empty peer structure for the named
-/* job and destination. It is an error to call this function
-/* if a peer for given combination already exists.
-/*
-/* qmgr_peer_find() looks up the peer for the named destination
-/* for the named job. A null result means that the peer
-/* was not found.
-/*
-/* qmgr_peer_free() disposes of a per-job peer after all
-/* its entries have been taken care of. It is an error to dispose
-/* of a peer still in use.
-/*
-/* qmgr_peer_select() attempts to find a peer of named job that
-/* has messages pending delivery. This routine implements
-/* round-robin search among job's peers.
-/* DIAGNOSTICS
-/* Panic: consistency check failure.
-/* LICENSE
-/* .ad
-/* .fi
-/* The Secure Mailer license must be distributed with this software.
-/* AUTHOR(S)
-/* Patrik Rak
-/* patrik@raxoft.cz
-/*--*/
-
-/* System library. */
-
-#include <sys_defs.h>
-
-/* Utility library. */
-
-#include <msg.h>
-#include <htable.h>
-#include <mymalloc.h>
-
-/* Application-specific. */
-
-#include "qmgr.h"
-
-/* qmgr_peer_create - create and initialize message peer structure */
-
-QMGR_PEER *qmgr_peer_create(QMGR_JOB *job, QMGR_QUEUE *queue)
-{
- QMGR_PEER *peer;
-
- peer = (QMGR_PEER *) mymalloc(sizeof(QMGR_PEER));
- peer->queue = queue;
- peer->job = job;
- QMGR_LIST_APPEND(job->peer_list, peer, peers);
- htable_enter(job->peer_byname, queue->name, (char *) peer);
- peer->refcount = 0;
- QMGR_LIST_INIT(peer->entry_list);
- return (peer);
-}
-
-/* qmgr_peer_free - release peer structure */
-
-void qmgr_peer_free(QMGR_PEER *peer)
-{
- char *myname = "qmgr_peer_free";
- QMGR_JOB *job = peer->job;
- QMGR_QUEUE *queue = peer->queue;
-
- /*
- * Sanity checks. It is an error to delete a referenced peer structure.
- */
- if (peer->refcount != 0)
- msg_panic("%s: refcount: %d", myname, peer->refcount);
- if (peer->entry_list.next != 0)
- msg_panic("%s: entry list not empty: %s", myname, queue->name);
-
- QMGR_LIST_UNLINK(job->peer_list, QMGR_PEER *, peer, peers);
- htable_delete(job->peer_byname, queue->name, (void (*) (char *)) 0);
- myfree((char *) peer);
-}
-
-/* qmgr_peer_find - lookup peer associated with given job and queue */
-
-QMGR_PEER *qmgr_peer_find(QMGR_JOB *job, QMGR_QUEUE *queue)
-{
- return ((QMGR_PEER *) htable_find(job->peer_byname, queue->name));
-}
-
-/* qmgr_peer_select - select next peer suitable for delivery within given job */
-
-QMGR_PEER *qmgr_peer_select(QMGR_JOB *job)
-{
- QMGR_PEER *peer;
- QMGR_QUEUE *queue;
-
- /*
- * If we find a suitable site, rotate the list to enforce round-robin
- * selection. See similar selection code in qmgr_transport_select().
- */
- for (peer = job->peer_list.next; peer; peer = peer->peers.next) {
- queue = peer->queue;
- if (queue->window > queue->busy_refcount && peer->entry_list.next != 0) {
- QMGR_LIST_ROTATE(job->peer_list, peer, peers);
- if (msg_verbose)
- msg_info("qmgr_peer_select: %s %s %s (%d of %d)",
- job->message->queue_id, queue->transport->name, queue->name,
- queue->busy_refcount + 1, queue->window);
- return (peer);
- }
- }
- return (0);
-}
+++ /dev/null
-/*++
-/* NAME
-/* qmgr_queue 3
-/* SUMMARY
-/* per-destination queues
-/* SYNOPSIS
-/* #include "qmgr.h"
-/*
-/* int qmgr_queue_count;
-/*
-/* QMGR_QUEUE *qmgr_queue_create(transport, site)
-/* QMGR_TRANSPORT *transport;
-/* const char *site;
-/*
-/* void qmgr_queue_done(queue)
-/* QMGR_QUEUE *queue;
-/*
-/* QMGR_QUEUE *qmgr_queue_find(transport, site)
-/* QMGR_TRANSPORT *transport;
-/* const char *site;
-/*
-/* void qmgr_queue_throttle(queue, reason)
-/* QMGR_QUEUE *queue;
-/* const char *reason;
-/*
-/* void qmgr_queue_unthrottle(queue)
-/* QMGR_QUEUE *queue;
-/* DESCRIPTION
-/* These routines add/delete/manipulate per-destination queues.
-/* Each queue corresponds to a specific transport and destination.
-/* Each queue has a `todo' list of delivery requests for that
-/* destination, and a `busy' list of delivery requests in progress.
-/*
-/* qmgr_queue_count is a global counter for the total number
-/* of in-core queue structures.
-/*
-/* qmgr_queue_create() creates an empty queue for the named
-/* transport and destination. The queue is given an initial
-/* concurrency limit as specified with the
-/* \fIinitial_destination_concurrency\fR configuration parameter,
-/* provided that it does not exceed the transport-specific
-/* concurrency limit.
-/*
-/* qmgr_queue_done() disposes of a per-destination queue after all
-/* its entries have been taken care of. It is an error to dispose
-/* of a dead queue.
-/*
-/* qmgr_queue_find() looks up the queue for the named destination
-/* for the named transport. A null result means that the queue
-/* was not found.
-/*
-/* qmgr_queue_throttle() handles a delivery error, and decrements the
-/* concurrency limit for the destination. When the concurrency limit
-/* for a destination becomes zero, qmgr_queue_throttle() starts a timer
-/* to re-enable delivery to the destination after a configurable delay.
-/*
-/* qmgr_queue_unthrottle() undoes qmgr_queue_throttle()'s effects.
-/* The concurrency limit for the destination is incremented,
-/* provided that it does not exceed the destination concurrency
-/* limit specified for the transport. This routine implements
-/* "slow open" mode, and eliminates the "thundering herd" problem.
-/* DIAGNOSTICS
-/* Panic: consistency check failure.
-/* 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
-/*
-/* Scheduler enhancements:
-/* Patrik Rak
-/* Modra 6
-/* 155 00, Prague, Czech Republic
-/*--*/
-
-/* System library. */
-
-#include <sys_defs.h>
-#include <time.h>
-
-/* Utility library. */
-
-#include <msg.h>
-#include <mymalloc.h>
-#include <events.h>
-#include <htable.h>
-
-/* Global library. */
-
-#include <mail_params.h>
-#include <recipient_list.h>
-
-/* Application-specific. */
-
-#include "qmgr.h"
-
-int qmgr_queue_count;
-
-/* qmgr_queue_unthrottle_wrapper - in case (char *) != (struct *) */
-
-static void qmgr_queue_unthrottle_wrapper(int unused_event, char *context)
-{
- QMGR_QUEUE *queue = (QMGR_QUEUE *) context;
-
- /*
- * This routine runs when a wakeup timer goes off; it does not run in the
- * context of some queue manipulation. Therefore, it is safe to discard
- * this in-core queue when it is empty and when this site is not dead.
- */
- qmgr_queue_unthrottle(queue);
- if (queue->window > 0 && queue->todo.next == 0 && queue->busy.next == 0)
- qmgr_queue_done(queue);
-}
-
-/* qmgr_queue_unthrottle - give this destination another chance */
-
-void qmgr_queue_unthrottle(QMGR_QUEUE *queue)
-{
- char *myname = "qmgr_queue_unthrottle";
- QMGR_TRANSPORT *transport = queue->transport;
-
- if (msg_verbose)
- msg_info("%s: queue %s", myname, queue->name);
-
- /*
- * Special case when this site was dead.
- */
- if (queue->window == 0) {
- event_cancel_timer(qmgr_queue_unthrottle_wrapper, (char *) queue);
- if (queue->reason == 0)
- msg_panic("%s: queue %s: window 0 reason 0", myname, queue->name);
- myfree(queue->reason);
- queue->reason = 0;
- queue->window = transport->init_dest_concurrency;
- return;
- }
-
- /*
- * Increase the destination's concurrency limit until we reach the
- * transport's concurrency limit. Allow for a margin the size of the
- * initial destination concurrency, so that we're not too gentle.
- */
- if (transport->dest_concurrency_limit == 0
- || transport->dest_concurrency_limit > queue->window)
- if (queue->window <= queue->busy_refcount + transport->init_dest_concurrency)
- queue->window++;
-}
-
-/* qmgr_queue_throttle - handle destination delivery failure */
-
-void qmgr_queue_throttle(QMGR_QUEUE *queue, const char *reason)
-{
- char *myname = "qmgr_queue_throttle";
-
- /*
- * Sanity checks.
- */
- if (queue->reason)
- msg_panic("%s: queue %s: spurious reason %s",
- myname, queue->name, queue->reason);
- if (msg_verbose)
- msg_info("%s: queue %s: %s", myname, queue->name, reason);
-
- /*
- * Decrease the destination's concurrency limit until we reach zero, at
- * which point the destination is declared dead. Decrease the concurrency
- * limit by one, instead of using actual concurrency - 1, to avoid
- * declaring a host dead after just one single delivery failure.
- */
- if (queue->window > 0)
- queue->window--;
-
- /*
- * Special case for a site that just was declared dead.
- */
- if (queue->window == 0) {
- queue->reason = mystrdup(reason);
- event_request_timer(qmgr_queue_unthrottle_wrapper,
- (char *) queue, var_min_backoff_time);
- }
-}
-
-/* qmgr_queue_done - delete in-core queue for site */
-
-void qmgr_queue_done(QMGR_QUEUE *queue)
-{
- char *myname = "qmgr_queue_done";
- QMGR_TRANSPORT *transport = queue->transport;
-
- /*
- * Sanity checks. It is an error to delete an in-core queue with pending
- * messages or timers.
- */
- if (queue->busy_refcount != 0 || queue->todo_refcount != 0)
- msg_panic("%s: refcount: %d", myname,
- queue->busy_refcount + queue->todo_refcount);
- if (queue->todo.next || queue->busy.next)
- msg_panic("%s: queue not empty: %s", myname, queue->name);
- if (queue->window <= 0)
- msg_panic("%s: window %d", myname, queue->window);
- if (queue->reason)
- msg_panic("%s: queue %s: spurious reason %s",
- myname, queue->name, queue->reason);
-
- /*
- * Clean up this in-core queue.
- */
- QMGR_LIST_UNLINK(transport->queue_list, QMGR_QUEUE *, queue, peers);
- htable_delete(transport->queue_byname, queue->name, (void (*) (char *)) 0);
- myfree(queue->name);
- qmgr_queue_count--;
- myfree((char *) queue);
-}
-
-/* qmgr_queue_create - create in-core queue for site */
-
-QMGR_QUEUE *qmgr_queue_create(QMGR_TRANSPORT *transport, const char *site)
-{
- QMGR_QUEUE *queue;
-
- /*
- * If possible, choose an initial concurrency of > 1 so that one bad
- * message or one bad network won't slow us down unnecessarily.
- */
-
- queue = (QMGR_QUEUE *) mymalloc(sizeof(QMGR_QUEUE));
- qmgr_queue_count++;
- queue->name = mystrdup(site);
- queue->todo_refcount = 0;
- queue->busy_refcount = 0;
- queue->transport = transport;
- queue->window = transport->init_dest_concurrency;
- QMGR_LIST_INIT(queue->todo);
- QMGR_LIST_INIT(queue->busy);
- queue->reason = 0;
- QMGR_LIST_APPEND(transport->queue_list, queue, peers);
- htable_enter(transport->queue_byname, site, (char *) queue);
- return (queue);
-}
-
-/* qmgr_queue_find - find in-core queue for site */
-
-QMGR_QUEUE *qmgr_queue_find(QMGR_TRANSPORT *transport, const char *site)
-{
- return ((QMGR_QUEUE *) htable_find(transport->queue_byname, site));
-}
+++ /dev/null
-/*++
-/* NAME
-/* qmgr_rcpt_list 3
-/* SUMMARY
-/* in-core recipient structures
-/* SYNOPSIS
-/* #include "qmgr.h"
-/*
-/* void qmgr_rcpt_list_init(list)
-/* QMGR_RCPT_LIST *list;
-/*
-/* void qmgr_rcpt_list_add(list, offset, recipient)
-/* QMGR_RCPT_LIST *list;
-/* long offset;
-/* const char *recipient;
-/*
-/* void qmgr_rcpt_list_free(list)
-/* QMGR_RCPT_LIST *list;
-/* DESCRIPTION
-/* This module maintains lists of queue manager recipient structures.
-/* These structures are extended versions of the structures maintained
-/* by the recipient_list(3) module. The extension is that the queue
-/* manager version of a recipient can have a reference to a queue
-/* structure.
-/*
-/* qmgr_rcpt_list_init() creates an empty recipient structure list.
-/* The list argument is initialized such that it can be given to
-/* qmgr_rcpt_list_add() and qmgr_rcpt_list_free().
-/*
-/* qmgr_rcpt_list_add() adds a recipient to the specified list.
-/* The recipient name is copied.
-/*
-/* qmgr_rcpt_list_free() releases memory for the specified list
-/* of recipient structures.
-/* SEE ALSO
-/* qmgr_rcpt_list(3h) data structure
-/* recipient_list(3) same code, different data structure.
-/* DIAGNOSTICS
-/* Fatal errors: memory allocation.
-/* 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>
-
-/* Utility library. */
-
-#include <mymalloc.h>
-
-/* Application-specific. */
-
-#include "qmgr.h"
-
-/* qmgr_rcpt_list_init - initialize */
-
-void qmgr_rcpt_list_init(QMGR_RCPT_LIST *list)
-{
- list->avail = 1;
- list->len = 0;
- list->info = (QMGR_RCPT *) mymalloc(sizeof(QMGR_RCPT));
-}
-
-/* qmgr_rcpt_list_add - add rcpt to list */
-
-void qmgr_rcpt_list_add(QMGR_RCPT_LIST *list, long offset, const char *rcpt)
-{
- if (list->len >= list->avail) {
- list->avail *= 2;
- list->info = (QMGR_RCPT *)
- myrealloc((char *) list->info, list->avail * sizeof(QMGR_RCPT));
- }
- list->info[list->len].address = mystrdup(rcpt);
- list->info[list->len].offset = offset;
- list->info[list->len].queue = 0;
- list->len++;
-}
-
-/* qmgr_rcpt_list_free - release memory for in-core recipient structure */
-
-void qmgr_rcpt_list_free(QMGR_RCPT_LIST *list)
-{
- QMGR_RCPT *rcpt;
-
- for (rcpt = list->info; rcpt < list->info + list->len; rcpt++)
- myfree(rcpt->address);
- myfree((char *) list->info);
-}
+++ /dev/null
-/*++
-/* NAME
-/* qmgr_scan 3
-/* SUMMARY
-/* queue scanning
-/* SYNOPSIS
-/* #include "qmgr.h"
-/*
-/* QMGR_SCAN *qmgr_scan_create(queue_name)
-/* const char *queue_name;
-/*
-/* char *qmgr_scan_next(scan_info)
-/* QMGR_SCAN *scan_info;
-/*
-/* void qmgr_scan_request(scan_info, flags)
-/* QMGR_SCAN *scan_info;
-/* int flags;
-/* DESCRIPTION
-/* This module implements queue scans. A queue scan always runs
-/* to completion, so that all files get a fair chance. The caller
-/* can request that a queue scan be restarted once it completes.
-/*
-/* qmgr_scan_create() creates a context for scanning the named queue,
-/* but does not start a queue scan.
-/*
-/* qmgr_scan_next() returns the base name of the next queue file.
-/* A null pointer means that no file was found. qmgr_scan_next()
-/* automagically restarts a queue scan when a scan request had
-/* arrived while the scan was in progress.
-/*
-/* qmgr_scan_request() records a request for the next queue scan. The
-/* flags argument is the bit-wise OR of zero or more of the following,
-/* unrecognized flags being ignored:
-/* .IP QMGR_FLUSH_DEAD
-/* Forget state information about dead hosts or transports. This
-/* request takes effect upon the next queue scan.
-/* .IP QMGR_SCAN_ALL
-/* Ignore queue file time stamps.
-/* This flag is passed on to the qmgr_active_feed() routine.
-/* .IP QMGR_SCAN_START
-/* Start a queue scan when none is in progress, or restart the
-/* current scan upon completion.
-/* DIAGNOSTICS
-/* Fatal: out of memory.
-/* Panic: interface violations, internal consistency errors.
-/* 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>
-
-/* Utility library. */
-
-#include <msg.h>
-#include <mymalloc.h>
-#include <scan_dir.h>
-
-/* Global library. */
-
-#include <mail_scan_dir.h>
-
-/* Application-specific. */
-
-#include "qmgr.h"
-
-/* qmgr_scan_start - start queue scan */
-
-static void qmgr_scan_start(QMGR_SCAN *scan_info)
-{
- char *myname = "qmgr_scan_start";
-
- /*
- * Sanity check.
- */
- if (scan_info->handle)
- msg_panic("%s: %s queue scan in progress",
- myname, scan_info->queue);
-
- /*
- * Give the poor tester a clue.
- */
- if (msg_verbose)
- msg_info("%s: %sstart %s queue scan",
- myname,
- scan_info->nflags & QMGR_SCAN_START ? "re" : "",
- scan_info->queue);
-
- /*
- * Optionally forget all dead host information.
- */
- if (scan_info->nflags & QMGR_FLUSH_DEAD)
- qmgr_enable_all();
-
- /*
- * Start or restart the scan.
- */
- scan_info->flags = scan_info->nflags;
- scan_info->nflags = 0;
- scan_info->handle = scan_dir_open(scan_info->queue);
-}
-
-/* qmgr_scan_request - request for future scan */
-
-void qmgr_scan_request(QMGR_SCAN *scan_info, int flags)
-{
-
- /*
- * If a scan is in progress, just record the request.
- */
- scan_info->nflags |= flags;
- if (scan_info->handle == 0 && (flags & QMGR_SCAN_START) != 0) {
- scan_info->nflags &= ~QMGR_SCAN_START;
- qmgr_scan_start(scan_info);
- }
-}
-
-/* qmgr_scan_next - look for next queue file */
-
-char *qmgr_scan_next(QMGR_SCAN *scan_info)
-{
- char *path = 0;
-
- /*
- * Restart the scan if we reach the end and a queue scan request has
- * arrived in the mean time.
- */
- if (scan_info->handle && (path = mail_scan_dir_next(scan_info->handle)) == 0) {
- scan_info->handle = scan_dir_close(scan_info->handle);
- if (msg_verbose && (scan_info->nflags & QMGR_SCAN_START) == 0)
- msg_info("done %s queue scan", scan_info->queue);
- }
- if (!scan_info->handle && (scan_info->nflags & QMGR_SCAN_START)) {
- qmgr_scan_start(scan_info);
- path = mail_scan_dir_next(scan_info->handle);
- }
- return (path);
-}
-
-/* qmgr_scan_create - create queue scan context */
-
-QMGR_SCAN *qmgr_scan_create(const char *queue)
-{
- QMGR_SCAN *scan_info;
-
- scan_info = (QMGR_SCAN *) mymalloc(sizeof(*scan_info));
- scan_info->queue = mystrdup(queue);
- scan_info->flags = scan_info->nflags = 0;
- scan_info->handle = 0;
- return (scan_info);
-}
+++ /dev/null
-/*++
-/* NAME
-/* qmgr_transport 3
-/* SUMMARY
-/* per-transport data structures
-/* SYNOPSIS
-/* #include "qmgr.h"
-/*
-/* QMGR_TRANSPORT *qmgr_transport_create(name)
-/* const char *name;
-/*
-/* QMGR_TRANSPORT *qmgr_transport_find(name)
-/* const char *name;
-/*
-/* QMGR_TRANSPORT *qmgr_transport_select()
-/*
-/* void qmgr_transport_alloc(transport, notify)
-/* QMGR_TRANSPORT *transport;
-/* void (*notify)(QMGR_TRANSPORT *transport, VSTREAM *fp);
-/*
-/* void qmgr_transport_throttle(transport, reason)
-/* QMGR_TRANSPORT *transport;
-/* const char *reason;
-/*
-/* void qmgr_transport_unthrottle(transport)
-/* QMGR_TRANSPORT *transport;
-/* DESCRIPTION
-/* This module organizes the world by message transport type.
-/* Each transport can have zero or more destination queues
-/* associated with it.
-/*
-/* qmgr_transport_create() instantiates a data structure for the
-/* named transport type.
-/*
-/* qmgr_transport_find() looks up an existing message transport
-/* data structure.
-/*
-/* qmgr_transport_select() attempts to find a transport that
-/* has messages pending delivery. This routine implements
-/* round-robin search among transports.
-/*
-/* qmgr_transport_alloc() allocates a delivery process for the
-/* specified transport type. Allocation is performed asynchronously.
-/* When a process becomes available, the application callback routine
-/* is invoked with as arguments the transport and a stream that
-/* is connected to a delivery process. It is an error to call
-/* qmgr_transport_alloc() while delivery process allocation for
-/* the same transport is in progress.
-/*
-/* qmgr_transport_throttle blocks further allocation of delivery
-/* processes for the named transport. Attempts to throttle a
-/* throttled transport are ignored.
-/*
-/* qmgr_transport_unthrottle() undoes qmgr_transport_throttle().
-/* Attempts to unthrottle a non-throttled transport are ignored.
-/* DIAGNOSTICS
-/* Panic: consistency check failure. Fatal: out of memory.
-/* 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
-/*
-/* Scheduler enhancements:
-/* Patrik Rak
-/* Modra 6
-/* 155 00, Prague, Czech Republic
-/*--*/
-
-/* System library. */
-
-#include <sys_defs.h>
-#include <unistd.h>
-
-/* Utility library. */
-
-#include <msg.h>
-#include <htable.h>
-#include <events.h>
-#include <mymalloc.h>
-#include <vstream.h>
-#include <iostuff.h>
-
-/* Global library. */
-
-#include <mail_proto.h>
-#include <recipient_list.h>
-#include <mail_conf.h>
-#include <mail_params.h>
-
-/* Application-specific. */
-
-#include "qmgr.h"
-
-HTABLE *qmgr_transport_byname; /* transport by name */
-QMGR_TRANSPORT_LIST qmgr_transport_list;/* transports, round robin */
-
- /*
- * A local structure to remember a delivery process allocation request.
- */
-typedef struct QMGR_TRANSPORT_ALLOC QMGR_TRANSPORT_ALLOC;
-
-struct QMGR_TRANSPORT_ALLOC {
- QMGR_TRANSPORT *transport; /* transport context */
- VSTREAM *stream; /* delivery service stream */
- QMGR_TRANSPORT_ALLOC_NOTIFY notify; /* application call-back routine */
-};
-
-/* qmgr_transport_unthrottle_wrapper - in case (char *) != (struct *) */
-
-static void qmgr_transport_unthrottle_wrapper(int unused_event, char *context)
-{
- qmgr_transport_unthrottle((QMGR_TRANSPORT *) context);
-}
-
-/* qmgr_transport_unthrottle - open the throttle */
-
-void qmgr_transport_unthrottle(QMGR_TRANSPORT *transport)
-{
- char *myname = "qmgr_transport_unthrottle";
-
- /*
- * This routine runs after expiration of the timer set by
- * qmgr_transport_throttle(), or whenever a delivery transport has been
- * used without malfunction. In either case, we enable delivery again if
- * the transport was blocked, otherwise the request is ignored.
- */
- if ((transport->flags & QMGR_TRANSPORT_STAT_DEAD) != 0) {
- if (msg_verbose)
- msg_info("%s: transport %s", myname, transport->name);
- transport->flags &= ~QMGR_TRANSPORT_STAT_DEAD;
- if (transport->reason == 0)
- msg_panic("%s: transport %s: null reason", myname, transport->name);
- myfree(transport->reason);
- transport->reason = 0;
- event_cancel_timer(qmgr_transport_unthrottle_wrapper,
- (char *) transport);
- }
-}
-
-/* qmgr_transport_throttle - disable delivery process allocation */
-
-void qmgr_transport_throttle(QMGR_TRANSPORT *transport, const char *reason)
-{
- char *myname = "qmgr_transport_throttle";
-
- /*
- * We are unable to connect to a deliver process for this type of message
- * transport. Instead of hosing the system by retrying in a tight loop,
- * back off and disable this transport type for a while.
- */
- if ((transport->flags & QMGR_TRANSPORT_STAT_DEAD) == 0) {
- if (msg_verbose)
- msg_info("%s: transport %s: reason: %s",
- myname, transport->name, reason);
- transport->flags |= QMGR_TRANSPORT_STAT_DEAD;
- if (transport->reason)
- msg_panic("%s: transport %s: spurious reason: %s",
- myname, transport->name, transport->reason);
- transport->reason = mystrdup(reason);
- event_request_timer(qmgr_transport_unthrottle_wrapper,
- (char *) transport, var_transport_retry_time);
- }
-}
-
-/* qmgr_transport_abort - transport connect watchdog */
-
-static void qmgr_transport_abort(int unused_event, char *context)
-{
- QMGR_TRANSPORT_ALLOC *alloc = (QMGR_TRANSPORT_ALLOC *) context;
-
- msg_fatal("timeout connecting to transport: %s", alloc->transport->name);
-}
-
-/* qmgr_transport_event - delivery process availability notice */
-
-static void qmgr_transport_event(int unused_event, char *context)
-{
- QMGR_TRANSPORT_ALLOC *alloc = (QMGR_TRANSPORT_ALLOC *) context;
-
- /*
- * This routine notifies the application when the request given to
- * qmgr_transport_alloc() completes.
- */
- if (msg_verbose)
- msg_info("transport_event: %s", alloc->transport->name);
-
- /*
- * Connection request completed. Stop the watchdog timer.
- */
- event_cancel_timer(qmgr_transport_abort, context);
-
- /*
- * Disable further read events that end up calling this function.
- */
- event_disable_readwrite(vstream_fileno(alloc->stream));
- alloc->transport->flags &= ~QMGR_TRANSPORT_STAT_BUSY;
-
- /*
- * Notify the requestor.
- */
- alloc->notify(alloc->transport, alloc->stream);
- myfree((char *) alloc);
-}
-
-#ifdef UNIX_DOMAIN_CONNECT_BLOCKS_FOR_ACCEPT
-
-/* qmgr_transport_connect - handle connection request completion */
-
-static void qmgr_transport_connect(int unused_event, char *context)
-{
- QMGR_TRANSPORT_ALLOC *alloc = (QMGR_TRANSPORT_ALLOC *) context;
-
- /*
- * This code is necessary for some versions of LINUX, where connect(2)
- * blocks until the application performs an accept(2). Reportedly, the
- * same can happen on Solaris 2.5.1.
- */
- event_disable_readwrite(vstream_fileno(alloc->stream));
- non_blocking(vstream_fileno(alloc->stream), BLOCKING);
- event_enable_read(vstream_fileno(alloc->stream),
- qmgr_transport_event, (char *) alloc);
-}
-
-#endif
-
-/* qmgr_transport_select - select transport for allocation */
-
-QMGR_TRANSPORT *qmgr_transport_select(void)
-{
- QMGR_TRANSPORT *xport;
- QMGR_QUEUE *queue;
-
- /*
- * If we find a suitable transport, rotate the list of transports to
- * effectuate round-robin selection. See similar selection code in
- * qmgr_peer_select().
- */
-#define STAY_AWAY (QMGR_TRANSPORT_STAT_BUSY | QMGR_TRANSPORT_STAT_DEAD)
-
- for (xport = qmgr_transport_list.next; xport; xport = xport->peers.next) {
- if (xport->flags & STAY_AWAY)
- continue;
- for (queue = xport->queue_list.next; queue; queue = queue->peers.next) {
- if (queue->window > queue->busy_refcount && queue->todo.next != 0) {
- QMGR_LIST_ROTATE(qmgr_transport_list, xport, peers);
- if (msg_verbose)
- msg_info("qmgr_transport_select: %s", xport->name);
- return (xport);
- }
- }
- }
- return (0);
-}
-
-/* qmgr_transport_alloc - allocate delivery process */
-
-void qmgr_transport_alloc(QMGR_TRANSPORT *transport, QMGR_TRANSPORT_ALLOC_NOTIFY notify)
-{
- QMGR_TRANSPORT_ALLOC *alloc;
- VSTREAM *stream;
-
- /*
- * Sanity checks.
- */
- if (transport->flags & QMGR_TRANSPORT_STAT_DEAD)
- msg_panic("qmgr_transport: dead transport: %s", transport->name);
- if (transport->flags & QMGR_TRANSPORT_STAT_BUSY)
- msg_panic("qmgr_transport: nested allocation: %s", transport->name);
-
- /*
- * Connect to the well-known port for this delivery service, and wake up
- * when a process announces its availability. In the mean time, block out
- * other delivery process allocation attempts for this transport. In case
- * of problems, back off. Do not hose the system when it is in trouble
- * already.
- */
-#ifdef UNIX_DOMAIN_CONNECT_BLOCKS_FOR_ACCEPT
-#define BLOCK_MODE NON_BLOCKING
-#define ENABLE_EVENTS event_enable_write
-#define EVENT_HANDLER qmgr_transport_connect
-#else
-#define BLOCK_MODE BLOCKING
-#define ENABLE_EVENTS event_enable_read
-#define EVENT_HANDLER qmgr_transport_event
-#endif
-
- 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, "transport is unavailable");
- return;
- }
- alloc = (QMGR_TRANSPORT_ALLOC *) mymalloc(sizeof(*alloc));
- alloc->stream = stream;
- alloc->transport = transport;
- alloc->notify = notify;
- transport->flags |= QMGR_TRANSPORT_STAT_BUSY;
- ENABLE_EVENTS(vstream_fileno(alloc->stream), EVENT_HANDLER, (char *) alloc);
-
- /*
- * Guard against broken systems.
- */
- event_request_timer(qmgr_transport_abort, (char *) alloc,
- var_daemon_timeout);
-}
-
-/* qmgr_transport_create - create transport instance */
-
-QMGR_TRANSPORT *qmgr_transport_create(const char *name)
-{
- QMGR_TRANSPORT *transport;
-
- if (htable_find(qmgr_transport_byname, name) != 0)
- msg_panic("qmgr_transport_create: transport exists: %s", name);
- transport = (QMGR_TRANSPORT *) mymalloc(sizeof(QMGR_TRANSPORT));
- transport->flags = 0;
- transport->name = mystrdup(name);
-
- /*
- * Use global configuration settings or transport-specific settings.
- */
- transport->dest_concurrency_limit =
- get_mail_conf_int2(name, _DEST_CON_LIMIT,
- var_dest_con_limit, 0, 0);
- transport->recipient_limit =
- get_mail_conf_int2(name, _DEST_RCPT_LIMIT,
- var_dest_rcpt_limit, 0, 0);
-
- if (transport->dest_concurrency_limit == 0
- || transport->dest_concurrency_limit >= var_init_dest_concurrency)
- transport->init_dest_concurrency = var_init_dest_concurrency;
- else
- transport->init_dest_concurrency = transport->dest_concurrency_limit;
-
- transport->slot_cost = get_mail_conf_int2(name, _DELIVERY_SLOT_COST,
- var_delivery_slot_cost, 0, 0);
- transport->slot_loan = get_mail_conf_int2(name, _DELIVERY_SLOT_LOAN,
- var_delivery_slot_loan, 0, 0);
- transport->slot_loan_factor =
- 100 - get_mail_conf_int2(name, _DELIVERY_SLOT_DISCOUNT,
- var_delivery_slot_discount, 0, 100);
- transport->min_slots = get_mail_conf_int2(name, _MIN_DELIVERY_SLOTS,
- var_min_delivery_slots, 0, 0);
- transport->rcpt_unused = get_mail_conf_int2(name, _XPORT_RCPT_LIMIT,
- var_xport_rcpt_limit, 0, 0);
- transport->rcpt_per_stack = get_mail_conf_int2(name, _STACK_RCPT_LIMIT,
- var_stack_rcpt_limit, 0, 0);
-
- transport->queue_byname = htable_create(0);
- QMGR_LIST_INIT(transport->queue_list);
- transport->job_byname = htable_create(0);
- QMGR_LIST_INIT(transport->job_list);
- QMGR_LIST_INIT(transport->job_stack);
- transport->job_next_unread = 0;
- transport->candidate_cache = 0;
- transport->candidate_cache_time = (time_t) 0;
- transport->reason = 0;
- if (qmgr_transport_byname == 0)
- qmgr_transport_byname = htable_create(10);
- htable_enter(qmgr_transport_byname, name, (char *) transport);
- QMGR_LIST_PREPEND(qmgr_transport_list, transport, peers);
- if (msg_verbose)
- msg_info("qmgr_transport_create: %s concurrency %d recipients %d",
- transport->name, transport->dest_concurrency_limit,
- transport->recipient_limit);
- return (transport);
-}
-
-/* qmgr_transport_find - find transport instance */
-
-QMGR_TRANSPORT *qmgr_transport_find(const char *name)
-{
- return ((QMGR_TRANSPORT *) htable_find(qmgr_transport_byname, name));
-}
#include <rec_type.h>
#include <off_cvt.h>
#include <mark_corrupt.h>
+#include <quote_821_local.h>
/* Application-specific. */
#include "smtp.h"
-#include "quote_821_local.h"
#include "smtp_sasl.h"
/*
* anti-UCE systems, by people who aren't aware of RFC details.
*/
if ((!SMTP_SOFT(code) && !SMTP_HARD(code))
- || code == 555 /* RFC 1869, section 6.1. */
+ || code == 555 /* RFC 1869, section 6.1. */
|| (code >= 500 && code < 510))
state->error_mask |= MAIL_ERROR_PROTOCOL;
}
/* .IP \fBstrict_rfc821_envelopes\fR
/* Disallow non-RFC 821 style addresses in envelopes. For example,
/* allow RFC822-style address forms with comments, like Sendmail does.
+/* .IP \fBallow_broken_auth_clients\fR
+/* Support older Microsoft clients that mis-implement the AUTH
+/* protocol, and that expect an EHLO response of "250 AUTH=list"
+/* instead of "250 AUTH list".
/* .SH "Content inspection controls"
/* .IP \fBcontent_filter\fR
/* The name of a mail delivery transport that filters mail and that
char *var_smtpd_sasl_opts;
char *var_smtpd_sasl_realm;
char *var_filter_xport;
+bool var_broken_auth_clients;
/*
* Global state, for stand-alone mode queue file cleanup. When this is
smtpd_chat_reply(state, "250-SIZE");
smtpd_chat_reply(state, "250-ETRN");
#ifdef USE_SASL_AUTH
- if (var_smtpd_sasl_enable)
+ if (var_smtpd_sasl_enable) {
smtpd_chat_reply(state, "250-AUTH %s", state->sasl_mechanism_list);
+ if (var_broken_auth_clients)
+ smtpd_chat_reply(state, "250-AUTH=%s", state->sasl_mechanism_list);
+ }
#endif
smtpd_chat_reply(state, "250 8BITMIME");
return (0);
state->error_mask |= MAIL_ERROR_BOUNCE;
smtpd_chat_reply(state, "554 Error: too many hops");
} else if ((state->err & CLEANUP_STAT_CONT) != 0) {
- state->error_mask |= MAIL_ERROR_BOUNCE;
+ state->error_mask |= MAIL_ERROR_POLICY;
smtpd_chat_reply(state, "552 Error: content rejected");
} else if ((state->err & CLEANUP_STAT_WRITE) != 0) {
state->error_mask |= MAIL_ERROR_RESOURCE;
VAR_DISABLE_VRFY_CMD, DEF_DISABLE_VRFY_CMD, &var_disable_vrfy_cmd,
VAR_ALLOW_UNTRUST_ROUTE, DEF_ALLOW_UNTRUST_ROUTE, &var_allow_untrust_route,
VAR_SMTPD_SASL_ENABLE, DEF_SMTPD_SASL_ENABLE, &var_smtpd_sasl_enable,
+ VAR_BROKEN_AUTH_CLNTS, DEF_BROKEN_AUTH_CLNTS, &var_broken_auth_clients,
0,
};
static CONFIG_STR_TABLE str_table[] = {
static void connect_event(int unused_event, char *context)
{
int sock = CAST_CHAR_PTR_TO_INT(context);
+ struct sockaddr sa;
+ SOCKADDR_SIZE len = sizeof(sa);
SINK_STATE *state;
int fd;
- if ((fd = accept(sock, (struct sockaddr *) 0, (SOCKADDR_SIZE *) 0)) >= 0) {
+ if ((fd = accept(sock, &sa, &len)) >= 0) {
if (msg_verbose)
- msg_info("connect");
+ msg_info("connect (%s)", sa.sa_family == AF_LOCAL ? "AF_LOCAL" :
+#ifdef AF_INET6
+ sa.sa_family == AF_INET6 ? "AF_INET6" :
+#endif
+ sa.sa_family == AF_INET ? "AF_INET" :
+ "unknown protocol family");
non_blocking(fd, NON_BLOCKING);
state = (SINK_STATE *) mymalloc(sizeof(*state));
state->stream = vstream_fdopen(fd, O_RDWR);
static int session_count;
static int message_count = 1;
static struct sockaddr_in sin;
+
#undef sun
static struct sockaddr_un sun;
static struct sockaddr *sa;
-static int sa_len;
+static int sa_length;
static int recipients = 1;
static char *defaddr;
static char *recipient;
session->stream = vstream_fdopen(fd, O_RDWR);
event_enable_write(fd, connect_done, (char *) session);
smtp_timeout_setup(session->stream, var_timeout);
- if (connect(fd, sa, sa_len) < 0 && errno != EINPROGRESS)
+ if (connect(fd, sa, sa_length) < 0 && errno != EINPROGRESS)
fail_connect(session);
}
#endif
memcpy(sun.sun_path, path, path_len);
sa = (struct sockaddr *) & sun;
- sa_len = sizeof(sun);
+ sa_length = sizeof(sun);
} else {
if (strncmp(argv[optind], "inet:", 5) == 0)
argv[optind] += 5;
sin.sin_addr.s_addr = find_inet_addr(host);
sin.sin_port = find_inet_port(port, "tcp");
sa = (struct sockaddr *) & sin;
- sa_len = sizeof(sin);
+ sa_length = sizeof(sin);
}
/*
} else {
if (errno != ENOENT)
break;
- if ((ret = mkdir(saved_path, perms)) < 0 && errno != EEXIST)
- break;
+ if ((ret = mkdir(saved_path, perms)) < 0) {
+ if (errno != EEXIST)
+ break;
+ /* Race condition? */
+ if ((ret = stat(saved_path, &st)) < 0)
+ break;
+ if (!S_ISDIR(st.st_mode)) {
+ errno = ENOTDIR;
+ ret = -1;
+ break;
+ }
+ }
}
if (saved_ch != 0)
*cp = saved_ch;