From: Wietse Venema Date: Fri, 19 Jan 2001 05:00:00 +0000 (-0500) Subject: postfix-20010119 X-Git-Tag: v20010228~3 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=fc3f6d0249980cd81d0cbee0811905a0c7fc1d31;p=thirdparty%2Fpostfix.git postfix-20010119 --- diff --git a/postfix/FILTER_README b/postfix/FILTER_README index 5c8c495c0..243c2f4c9 100644 --- a/postfix/FILTER_README +++ b/postfix/FILTER_README @@ -60,23 +60,20 @@ The filter program can start out as a simple shell script like this: # Exit codes from 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 , 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 diff --git a/postfix/conf/sample-smtp.cf b/postfix/conf/sample-smtp.cf index 0750e0f21..ccaf5ec64 100644 --- a/postfix/conf/sample-smtp.cf +++ b/postfix/conf/sample-smtp.cf @@ -66,11 +66,24 @@ smtp_never_send_ehlo = 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 diff --git a/postfix/conf/sample-smtpd.cf b/postfix/conf/sample-smtpd.cf index 02df49246..56f301800 100644 --- a/postfix/conf/sample-smtpd.cf +++ b/postfix/conf/sample-smtpd.cf @@ -63,6 +63,16 @@ smtpd_recipient_limit = 1000 # 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 # diff --git a/postfix/conf/sample-virtual.cf b/postfix/conf/sample-virtual.cf deleted file mode 100644 index d660d6b79..000000000 --- a/postfix/conf/sample-virtual.cf +++ /dev/null @@ -1,23 +0,0 @@ -# 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 = diff --git a/postfix/examples/chroot-setup/LINUX2 b/postfix/examples/chroot-setup/LINUX2 index 78aaab251..dc9419db1 100644 --- a/postfix/examples/chroot-setup/LINUX2 +++ b/postfix/examples/chroot-setup/LINUX2 @@ -1,9 +1,14 @@ #! /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 + +# 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". @@ -26,14 +31,29 @@ # 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 @@ -47,10 +67,18 @@ mkdir -p etc lib usr/lib/zoneinfo # 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 diff --git a/postfix/html/Makefile.in b/postfix/html/Makefile.in index c15b21a1f..9374acc1a 100644 --- a/postfix/html/Makefile.in +++ b/postfix/html/Makefile.in @@ -5,7 +5,7 @@ SHELL = /bin/sh 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 \ @@ -51,12 +51,6 @@ local.8.html: ../src/local/local.c 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 >$@ @@ -81,9 +75,6 @@ smtpd.8.html: ../src/smtpd/smtpd.c 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 >$@ diff --git a/postfix/html/nqmgr.8.html b/postfix/html/nqmgr.8.html deleted file mode 100644 index 624352aa2..000000000 --- a/postfix/html/nqmgr.8.html +++ /dev/null @@ -1,464 +0,0 @@ -
-
-
-
-NQMGR(8)                                                 NQMGR(8)
-
-
-NAME
-       nqmgr - Postfix queue manager
-
-SYNOPSIS
-       nqmgr [generic Postfix daemon options]
-
-DESCRIPTION
-       The  nqmgr  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 triv-
-       ial-rewrite(8) daemon.  This program  expects  to  be  run
-       from the master(8) process manager.
-
-       Mail  addressed  to  the  local  double-bounce  address is
-       silently discarded.  This stops potential loops caused  by
-       undeliverable bounce notifications.
-
-       Mail  addressed to a user listed in the optional relocated
-       database is bounced with a "user has  moved  to  new_loca-
-       tion" message. See relocated(5) for a precise description.
-
-MAIL QUEUES
-       The nqmgr daemon maintains the following queues:
-
-       incoming
-              Inbound mail from the network, or mail picked up by
-              the local pickup agent from the maildrop directory.
-
-       active Messages that the  queue  manager  has  opened  for
-              delivery.  Only  a  limited  number  of messages is
-              allowed to enter the  active  queue  (leaky  bucket
-              strategy, for a fixed delivery rate).
-
-       deferred
-              Mail  that  could  not  be delivered upon the first
-              attempt. The queue manager  implements  exponential
-              backoff  by  doubling  the  time  between  delivery
-              attempts.
-
-       corrupt
-              Unreadable or damaged queue files  are  moved  here
-              for inspection.
-
-DELIVERY STATUS REPORTS
-       The nqmgr 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:
-
-       bounce Per-recipient status information about why mail  is
-              bounced.    These   files  are  maintained  by  the
-              bounce(8) daemon.
-
-       defer  Per-recipient status information about why mail  is
-
-
-
-                                                                1
-
-
-
-
-
-NQMGR(8)                                                 NQMGR(8)
-
-
-              delayed.    These   files  are  maintained  by  the
-              defer(8) daemon.
-
-       The nqmgr daemon is responsible for asking  the  bounce(8)
-       or defer(8) daemons to send non-delivery reports.
-
-STRATEGIES
-       The  queue  manager implements a variety of strategies for
-       either opening queue files (input) or for message delivery
-       (output).
-
-       leaky bucket
-              This  strategy limits the number of messages in the
-              active queue and prevents the  queue  manager  from
-              running out of memory under heavy load.
-
-       fairness
-              When  the  active queue has room, the queue manager
-              takes one message from the incoming queue  and  one
-              from the deferred queue. This prevents a large mail
-              backlog from blocking the delivery of new mail.
-
-       slow start
-              This strategy eliminates "thundering herd" problems
-              by slowly adjusting the number of parallel deliver-
-              ies to the same destination.
-
-       round robin
-              The queue manager sorts delivery requests by desti-
-              nation.   Round-robin selection prevents one desti-
-              nation from dominating deliveries to other destina-
-              tions.
-
-       exponential backoff
-              Mail  that  cannot  be  delivered  upon  the  first
-              attempt is deferred.   The  time  interval  between
-              delivery attempts is doubled after each attempt.
-
-       destination status cache
-              The   queue  manager  avoids  unnecessary  delivery
-              attempts by  maintaining  a  short-term,  in-memory
-              list of unreachable destinations.
-
-       preemptive message scheduling
-              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.
-
-TRIGGERS
-       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):
-
-       D (QMGR_REQ_SCAN_DEFERRED)
-              Start  a  deferred queue scan.  If a deferred queue
-              scan is already in  progress,  that  scan  will  be
-              restarted as soon as it finishes.
-
-       I (QMGR_REQ_SCAN_INCOMING)
-              Start  an incoming queue scan. If an incoming queue
-              scan is already in  progress,  that  scan  will  be
-              restarted as soon as it finishes.
-
-       A (QMGR_REQ_SCAN_ALL)
-              Ignore deferred queue file time stamps. The request
-              affects the next deferred queue scan.
-
-       F (QMGR_REQ_FLUSH_DEAD)
-              Purge all information  about  dead  transports  and
-              destinations.
-
-       W (TRIGGER_REQ_WAKEUP)
-              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 nqmgr daemon reads an entire buffer worth of triggers.
-       Multiple  identical  trigger  requests  are collapsed into
-       one, and trigger requests are sorted so that A and F  pre-
-       cede  D  and  I.  Thus, in order to force a deferred queue
-       run, one would request A F D; in order to notify the queue
-       manager of the arrival of new mail one would request I.
-
-STANDARDS
-       None.  The nqmgr daemon does not interact with the outside
-       world.
-
-SECURITY
-       The nqmgr 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
-       nqmgr  daemon  does  not talk to the outside world, and it
-       can be run at fixed low privilege in a  chrooted  environ-
-       ment.
-
-DIAGNOSTICS
-       Problems and transactions are logged to the syslog daemon.
-       Corrupted message files are saved to the corrupt queue for
-       further inspection.
-
-       Depending  on the setting of the notify_classes parameter,
-       the postmaster is notified of bounces and of  other  trou-
-       ble.
-
-
-
-                                                                3
-
-
-
-
-
-NQMGR(8)                                                 NQMGR(8)
-
-
-BUGS
-       A  single  queue  manager  process has to compete for disk
-       access with multiple front-end processes such as smtpd.  A
-       sudden  burst  of  inbound mail can negatively impact out-
-       bound delivery rates.
-
-CONFIGURATION PARAMETERS
-       The following main.cf parameters are  especially  relevant
-       to  this  program. See the Postfix main.cf file for syntax
-       details and for default values.  Use  the  postfix  reload
-       command after a configuration change.
-
-Miscellaneous
-       allow_min_user
-              Do  not  bounce recipient addresses that begin with
-              '-'.
-
-       relocated_maps
-              Tables with contact information for users, hosts or
-              domains that no longer exist. See relocated(5).
-
-       queue_directory
-              Top-level directory of the Postfix queue.
-
-Active queue controls
-       In  the text below, transport is the first field in a mas-
-       ter.cf entry.
-
-       qmgr_message_active_limit
-              Limit the number of messages in the active queue.
-
-       qmgr_message_recipient_limit
-              Limit the number of in-memory recipients.
-
-              This parameter also limits the size of  the  short-
-              term, in-memory destination cache.
-
-       qmgr_message_recipient_minimum
-              Per message minimum of in-memory recipients.
-
-       default_recipient_limit
-              Default limit on the number of in-memory recipients
-              per transport.
-
-       transport_recipient_limit
-              Limit on the number of  in-memory  recipients,  for
-              the named message transport.
-
-       default_extra_recipient_limit
-              Default  limit on the total number of per transport
-              in-memory recipients that the  preempting  messages
-              can have.
-
-
-
-
-
-                                                                4
-
-
-
-
-
-NQMGR(8)                                                 NQMGR(8)
-
-
-       transport_extra_recipient_limit
-              Limit  on  the number of in-memory recipients which
-              all preempting messages delivered by the  transport
-              transport can have.
-
-Timing controls
-       min_backoff
-              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.
-
-       max_backoff
-              Maximal time in seconds between  delivery  attempts
-              of a deferred message.
-
-       maximal_queue_lifetime
-              Maximal  time in days a message is queued before it
-              is sent back as undeliverable.
-
-       queue_run_delay
-              Time in seconds between deferred queue scans. Queue
-              scans do not overlap.
-
-       transport_retry_time
-              Time  in seconds between attempts to contact a bro-
-              ken delivery transport.
-
-Concurrency controls
-       initial_destination_concurrency
-              Initial per-destination concurrency level for  par-
-              allel delivery to the same destination.
-
-       default_destination_concurrency_limit
-              Default  limit on the number of parallel deliveries
-              to the same destination.
-
-       transport_destination_concurrency_limit
-              Limit on the number of parallel deliveries  to  the
-              same  destination,  for delivery via the named mes-
-              sage transport.
-
-Recipient controls
-       default_destination_recipient_limit
-              Default limit on the number of recipients per  mes-
-              sage transfer.
-
-       transport_destination_recipient_limit
-              Limit  on  the  number  of  recipients  per message
-              transfer, for the named message transport.
-
-
-
-
-
-                                                                5
-
-
-
-
-
-NQMGR(8)                                                 NQMGR(8)
-
-
-Message scheduling
-       transport_delivery_slot_cost (valid range: 0,2,3...)
-              This parameter basically controls how often a  mes-
-              sage  delivered  by  transport  can be preempted by
-              another message.  An internal per-message/transport
-              counter  is  incremented  by  one  for  each trans-
-              port_delivery_slot_cost   deliveries   handled   by
-              transport.  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  transport agents than the value of the
-              "available delivery slots" counter.
-
-              Value equal to 0 disables  the  message  preemption
-              for transport.
-
-       transport_minimum_delivery_slots
-              Message preemption is not attempted at all whenever
-              a message  that  can't  ever  accumulate  at  least
-              transport_minimum_delivery_slots available delivery
-              slots is being delivered by transport.
-
-       transport_delivery_slot_discount (valid range: 0..100)
-
-       transport_delivery_slot_loan
-              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 trans-
-              port_delivery_slot_discount percent of the required
-              amount   plus   transport_delivery_slot_loan  still
-              remains to  be  accumulated.  Note  that  the  full
-              amount  will  still  have  to be accumulated before
-              another preemption can take place later.
-
-       default_delivery_slot_cost
-
-       default_minimum_delivery_slots
-
-       default_delivery_slot_discount
-
-       default_delivery_slot_loan
-              Default values for the transport  specific  parame-
-              ters 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
-       The  Secure  Mailer  license must be distributed with this
-
-
-
-                                                                6
-
-
-
-
-
-NQMGR(8)                                                 NQMGR(8)
-
-
-       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
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-                                                                7
-
-
-
diff --git a/postfix/html/smtpd.8.html b/postfix/html/smtpd.8.html index c81578a9b..a6fe58de5 100644 --- a/postfix/html/smtpd.8.html +++ b/postfix/html/smtpd.8.html @@ -77,6 +77,11 @@ SMTPD(8) SMTPD(8) For example, allow RFC822-style address forms with comments, like Sendmail does. + allow_broken_auth_clients + 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". + Content inspection controls content_filter The name of a mail delivery transport that filters @@ -119,11 +124,6 @@ SMTPD(8) SMTPD(8) Location of Postfix support commands (default: $program_directory). - debug_peer_level - Increment in verbose logging level when a remote - host matches a pattern in the debug_peer_list - parameter. - @@ -137,6 +137,11 @@ SMTPD(8) SMTPD(8) SMTPD(8) SMTPD(8) + debug_peer_level + Increment in verbose logging level when a remote + host matches a pattern in the debug_peer_list + parameter. + debug_peer_list List of domain or network patterns. When a remote host matches a pattern, increase the verbose log- @@ -186,11 +191,6 @@ SMTPD(8) SMTPD(8) reject responses. This can be useful for testing purposes. -Resource controls - line_length_limit - Limit the amount of memory in bytes used for the - handling of partial input lines. - @@ -203,6 +203,11 @@ SMTPD(8) SMTPD(8) SMTPD(8) SMTPD(8) +Resource controls + line_length_limit + Limit the amount of memory in bytes used for the + handling of partial input lines. + message_size_limit Limit the total size in bytes of a message, includ- ing on-disk storage for envelope information. @@ -253,11 +258,6 @@ SMTPD(8) SMTPD(8) Restrict what recipient addresses are allowed in RCPT TO commands. - smtpd_etrn_restrictions - Restrict what domain names can be used in ETRN com- - mands, and what clients may issue ETRN commands. - - 4 @@ -269,6 +269,10 @@ SMTPD(8) SMTPD(8) SMTPD(8) SMTPD(8) + smtpd_etrn_restrictions + Restrict what domain names can be used in ETRN com- + mands, and what clients may issue ETRN commands. + allow_untrusted_routing Allow untrusted clients to specify addresses with sender-specified routing. Enabling this opens up @@ -319,10 +323,6 @@ SMTPD(8) SMTPD(8) name mapping violates the reject_unknown_clients restriction. - unknown_hostname_reject_code - Server response when a client violates the - reject_unknown_hostname restriction. - @@ -335,6 +335,10 @@ SMTPD(8) SMTPD(8) SMTPD(8) SMTPD(8) + unknown_hostname_reject_code + Server response when a client violates the + reject_unknown_hostname restriction. + SEE ALSO cleanup(8) message canonicalization master(8) process manager @@ -383,10 +387,6 @@ SMTPD(8) SMTPD(8) - - - - diff --git a/postfix/html/virtual.8.html b/postfix/html/virtual.8.html deleted file mode 100644 index 5f5aafb9d..000000000 --- a/postfix/html/virtual.8.html +++ /dev/null @@ -1,266 +0,0 @@ -
-
-
-
-VIRTUAL(8)                                             VIRTUAL(8)
-
-
-NAME
-       virtual - Postfix virtual domain mail delivery agent
-
-SYNOPSIS
-       virtual [generic Postfix daemon options]
-
-DESCRIPTION
-       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  virtual  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 master(8) process manager.
-
-       The virtual 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 bounce(8) or defer(8) dae-
-       mon as appropriate.
-
-MAILBOX DELIVERY
-       The virtual 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 virtual_mail-
-       box_base and virtual_mailbox_maps configuration parameters
-       (see below).
-
-       In the case of UNIX-style mailbox delivery, the local dae-
-       mon prepends a "From sender time_stamp" envelope header to
-       each message, prepends a  Delivered-To:  header  with  the
-       envelope recipient address, prepends a Return-Path: header
-       with the envelope sender address, prepends a  >  character
-       to  lines  beginning  with  "From  ", 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 maildir delivery, the local daemon prepends
-       a Delivered-To: header with the envelope recipient address
-       and prepends  a  Return-Path:  header  with  the  envelope
-       sender address.
-
-DELIVERY RIGHTS
-       Deliveries  are  made  with  the user and group privileges
-       that are listed in the  tables  specified  with  the  vir-
-       tual_uid_maps and virtual_gid_maps, respectively.
-
-       The  virtual_minimum_uid parameter specifies a lower bound
-
-
-
-                                                                1
-
-
-
-
-
-VIRTUAL(8)                                             VIRTUAL(8)
-
-
-       on  user  ID  values  that  may  be  specified   in   vir-
-       tual_uid_maps.  Mail  will not be delivered when a too low
-       UID value is found.
-
-STANDARDS
-       RFC 822 (ARPA Internet Text Messages)
-
-DIAGNOSTICS
-       Problems and transactions are logged to syslogd(8).   Cor-
-       rupted  message files are marked so that the queue manager
-       can move them to the corrupt queue afterwards.
-
-       Depending on the setting of the notify_classes  parameter,
-       the  postmaster  is notified of bounces and of other trou-
-       ble.
-
-CONFIGURATION PARAMETERS
-       The following main.cf parameters are  especially  relevant
-       to  this  program. See the Postfix main.cf file for syntax
-       details and for default values.  Use  the  postfix  reload
-       command after a configuration change.
-
-Mailbox delivery
-       virtual_mailbox_base
-              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 virtual_mail-
-              box_maps doesn't litter the filesystem  with  mail-
-              boxes.   While it could be set to "/", this setting
-              isn't recommended.
-
-       virtual_mailbox_maps
-              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 virtual_mailbox_base  is  unconditionally
-              prepended to this path.
-
-       virtual_minimum_uid
-              Specifies  a minimum uid that will be accepted as a
-              return from  a  virtual_uid_maps  lookup.  Returned
-              values  less  than  this  will be rejected, and the
-              message will be deferred.
-
-       virtual_uid_maps
-              Recipients are looked up in this map  to  determine
-              the UID to be used when writing to the target mail-
-              box.
-
-       virtual_gid_maps
-              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.
-
-Locking controls
-       mailbox_delivery_lock
-              How to lock UNIX-style mailboxes: one  or  more  of
-              flock, fcntl or dotlock.
-
-              Use  the command postconf -m to find out what lock-
-              ing methods are available on your system.
-
-       deliver_lock_attempts
-              Limit the number of attempts to acquire  an  exclu-
-              sive lock on a mailbox file.
-
-       deliver_lock_delay
-              Time (default: seconds) between successive attempts
-              to acquire an exclusive lock on a mailbox file.
-
-       stale_lock_time
-              Limit the time after  which  a  stale  lockfile  is
-              removed.
-
-Resource controls
-       virtual_destination_concurrency_limit
-              Limit the number of parallel deliveries to the same
-              domain.   The  default  limit  is  taken  from  the
-              default_destination_concurrency_limit parameter.
-
-       virtual_destination_recipient_limit
-              Limit  the  number of recipients per message deliv-
-              ery.   The  default  limit  is   taken   from   the
-              default_destination_recipient_limit parameter.
-
-HISTORY
-       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 Delivered-To: header appears in the  qmail  system  by
-       Daniel Bernstein.
-
-       The  maildir  structure  appears  in  the  qmail system by
-       Daniel Bernstein.
-
-SEE ALSO
-       bounce(8) non-delivery status reports
-       syslogd(8) system logging
-       qmgr(8) queue manager
-
-LICENSE
-       The Secure Mailer license must be  distributed  with  this
-       software.
-
-
-
-                                                                3
-
-
-
-
-
-VIRTUAL(8)                                             VIRTUAL(8)
-
-
-AUTHOR(S)
-       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
-
-
-
diff --git a/postfix/man/Makefile.in b/postfix/man/Makefile.in index 8ea0fe0fd..70b4d9817 100644 --- a/postfix/man/Makefile.in +++ b/postfix/man/Makefile.in @@ -5,7 +5,7 @@ SHELL = /bin/sh 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 \ @@ -50,12 +50,6 @@ man8/lmtp.8: ../src/lmtp/lmtp.c 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 $? >$@ @@ -80,9 +74,6 @@ man8/smtpd.8: ../src/smtpd/smtpd.c 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 $? >$@ diff --git a/postfix/man/man8/nqmgr.8 b/postfix/man/man8/nqmgr.8 deleted file mode 100644 index 4a36d1df5..000000000 --- a/postfix/man/man8/nqmgr.8 +++ /dev/null @@ -1,307 +0,0 @@ -.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 diff --git a/postfix/man/man8/smtpd.8 b/postfix/man/man8/smtpd.8 index 832e3e19d..f8eccc0cb 100644 --- a/postfix/man/man8/smtpd.8 +++ b/postfix/man/man8/smtpd.8 @@ -71,6 +71,10 @@ a configuration change. .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 diff --git a/postfix/man/man8/virtual.8 b/postfix/man/man8/virtual.8 deleted file mode 100644 index 79849c37d..000000000 --- a/postfix/man/man8/virtual.8 +++ /dev/null @@ -1,183 +0,0 @@ -.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 diff --git a/postfix/src/base64/.indent.pro b/postfix/src/base64/.indent.pro deleted file mode 120000 index 5c837eca6..000000000 --- a/postfix/src/base64/.indent.pro +++ /dev/null @@ -1 +0,0 @@ -../../.indent.pro \ No newline at end of file diff --git a/postfix/src/base64/Makefile.in b/postfix/src/base64/Makefile.in deleted file mode 100644 index 08a2e4915..000000000 --- a/postfix/src/base64/Makefile.in +++ /dev/null @@ -1,79 +0,0 @@ -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 diff --git a/postfix/src/base64/base64decode.c b/postfix/src/base64/base64decode.c deleted file mode 100644 index 14602ad20..000000000 --- a/postfix/src/base64/base64decode.c +++ /dev/null @@ -1,63 +0,0 @@ -/* base64decode - transform base 64 data to printable form */ - -/* System library. */ - -#include -#include - -/* SASL library. */ - -#include -#include - -/* Utility library. */ - -#include -#include -#include -#include -#include - -/* 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); -} diff --git a/postfix/src/base64/base64encode.c b/postfix/src/base64/base64encode.c deleted file mode 100644 index 73ee0dc11..000000000 --- a/postfix/src/base64/base64encode.c +++ /dev/null @@ -1,77 +0,0 @@ -/* base64encode - transform printable form to base 64 data */ - -/* System library. */ - -#include -#include - -/* SASL library. */ - -#include -#include - -/* Utility library. */ - -#include -#include -#include -#include -#include - -/* 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); -} diff --git a/postfix/src/bounce/bounce_notify_util.c b/postfix/src/bounce/bounce_notify_util.c index b31a1e8f5..cc5e15f6b 100644 --- a/postfix/src/bounce/bounce_notify_util.c +++ b/postfix/src/bounce/bounce_notify_util.c @@ -339,10 +339,10 @@ int bounce_boilerplate(VSTREAM *bounce, BOUNCE_INFO *bounce_info) "####################################################################"); 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); } diff --git a/postfix/src/global/mail_params.h b/postfix/src/global/mail_params.h index 8ff8a2a19..559e89ac4 100644 --- a/postfix/src/global/mail_params.h +++ b/postfix/src/global/mail_params.h @@ -237,6 +237,14 @@ extern char *var_rcpt_witheld; #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. */ @@ -712,7 +720,7 @@ extern int var_smtpd_err_sleep; 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 @@ -727,7 +735,7 @@ extern char *var_smtpd_sasl_opts; 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 @@ -741,6 +749,24 @@ extern char *var_smtp_sasl_passwd; #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" /* @@ -1110,18 +1136,23 @@ extern char *var_export_environ; #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; diff --git a/postfix/src/global/mail_queue.c b/postfix/src/global/mail_queue.c index 23e18cccc..cca871749 100644 --- a/postfix/src/global/mail_queue.c +++ b/postfix/src/global/mail_queue.c @@ -415,6 +415,7 @@ VSTREAM *mail_queue_open(const char *queue_name, const char *queue_id, * 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); diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h index 0b70e3714..d70bbe20c 100644 --- a/postfix/src/global/mail_version.h +++ b/postfix/src/global/mail_version.h @@ -15,7 +15,7 @@ * 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 diff --git a/postfix/src/lmtp/Makefile.in b/postfix/src/lmtp/Makefile.in index 7c8f4db72..6e4833185 100644 --- a/postfix/src/lmtp/Makefile.in +++ b/postfix/src/lmtp/Makefile.in @@ -1,8 +1,10 @@ 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 \ @@ -79,6 +81,7 @@ lmtp.o: ../../include/debug_peer.h 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 @@ -157,7 +160,30 @@ lmtp_proto.o: ../../include/off_cvt.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 @@ -182,6 +208,7 @@ lmtp_state.o: lmtp.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 diff --git a/postfix/src/lmtp/lmtp.c b/postfix/src/lmtp/lmtp.c index dfd3ec5c3..ec0e1c73c 100644 --- a/postfix/src/lmtp/lmtp.c +++ b/postfix/src/lmtp/lmtp.c @@ -49,6 +49,7 @@ /* 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 @@ -85,6 +86,27 @@ /* .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 @@ -231,6 +253,7 @@ /* Application-specific. */ #include "lmtp.h" +#include "lmtp_sasl.h" /* * Tunable parameters. These have compiled-in defaults that can be overruled @@ -252,6 +275,9 @@ int var_lmtp_cache_conn; 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. @@ -432,6 +458,10 @@ static void post_init(char *unused_name, char **unused_argv) 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. */ @@ -446,6 +476,10 @@ static void cleanup(void) 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 */ @@ -467,6 +501,8 @@ int main(int argc, char **argv) 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[] = { diff --git a/postfix/src/lmtp/lmtp.h b/postfix/src/lmtp/lmtp.h index 0f53c46a7..9574b648a 100644 --- a/postfix/src/lmtp/lmtp.h +++ b/postfix/src/lmtp/lmtp.h @@ -8,6 +8,14 @@ /* DESCRIPTION /* .nf + /* + * SASL library. + */ +#ifdef USE_SASL_AUTH +#include +#include +#endif + /* * Utility library. */ @@ -35,6 +43,15 @@ typedef struct LMTP_STATE { 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 */ @@ -44,6 +61,7 @@ typedef struct LMTP_STATE { #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 diff --git a/postfix/src/lmtp/lmtp_chat.c b/postfix/src/lmtp/lmtp_chat.c index 9fd23f2bf..19b37cc03 100644 --- a/postfix/src/lmtp/lmtp_chat.c +++ b/postfix/src/lmtp/lmtp_chat.c @@ -104,6 +104,7 @@ #include #include #include +#include /* Application-specific. */ @@ -172,7 +173,6 @@ LMTP_RESP *lmtp_chat_resp(LMTP_STATE *state) { LMTP_SESSION *session = state->session; static LMTP_RESP rdata; - int more; char *cp; int last_char; @@ -190,17 +190,12 @@ LMTP_RESP *lmtp_chat_resp(LMTP_STATE *state) 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 @@ -212,13 +207,23 @@ LMTP_RESP *lmtp_chat_resp(LMTP_STATE *state) 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); diff --git a/postfix/src/lmtp/lmtp_proto.c b/postfix/src/lmtp/lmtp_proto.c index 93ed07dd0..9dd965f2b 100644 --- a/postfix/src/lmtp/lmtp_proto.c +++ b/postfix/src/lmtp/lmtp_proto.c @@ -116,11 +116,12 @@ #include #include #include +#include /* 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 @@ -186,14 +187,14 @@ int lmtp_lhlo(LMTP_STATE *state) */ 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", " "))); /* @@ -202,7 +203,7 @@ int lmtp_lhlo(LMTP_STATE *state) 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", " "))); @@ -223,11 +224,20 @@ int lmtp_lhlo(LMTP_STATE *state) 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 @@ -446,7 +456,7 @@ static int lmtp_loop(LMTP_STATE *state, int send_state, int recv_state) 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); @@ -463,7 +473,7 @@ static int lmtp_loop(LMTP_STATE *state, int send_state, int recv_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; } @@ -487,7 +497,7 @@ static int lmtp_loop(LMTP_STATE *state, int send_state, int recv_state) 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 */ } @@ -505,7 +515,7 @@ static int lmtp_loop(LMTP_STATE *state, int send_state, int recv_state) 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; } @@ -535,7 +545,7 @@ static int lmtp_loop(LMTP_STATE *state, int send_state, int recv_state) } } 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 */ } @@ -618,7 +628,7 @@ static int lmtp_loop(LMTP_STATE *state, int send_state, int recv_state) 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) diff --git a/postfix/src/lmtp/lmtp_sasl.h b/postfix/src/lmtp/lmtp_sasl.h new file mode 100644 index 000000000..1054d688c --- /dev/null +++ b/postfix/src/lmtp/lmtp_sasl.h @@ -0,0 +1,39 @@ +/*++ +/* 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 +/*--*/ diff --git a/postfix/src/lmtp/lmtp_sasl_glue.c b/postfix/src/lmtp/lmtp_sasl_glue.c new file mode 100644 index 000000000..ba57156a7 --- /dev/null +++ b/postfix/src/lmtp/lmtp_sasl_glue.c @@ -0,0 +1,512 @@ +/*++ +/* 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 +#include +#include +#ifdef STRCASECMP_IN_STRINGS_H +#include +#endif + + /* + * Utility library + */ +#include +#include +#include +#include +#include + + /* + * Global library + */ +#include +#include +#include + + /* + * 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 diff --git a/postfix/src/lmtp/lmtp_sasl_proto.c b/postfix/src/lmtp/lmtp_sasl_proto.c new file mode 100644 index 000000000..8eaeafd14 --- /dev/null +++ b/postfix/src/lmtp/lmtp_sasl_proto.c @@ -0,0 +1,120 @@ +/*++ +/* 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 + +/* Utility library. */ + +#include +#include + +/* Global library. */ + +#include + +/* 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 diff --git a/postfix/src/lmtp/lmtp_state.c b/postfix/src/lmtp/lmtp_state.c index 7a9626609..9f25f8149 100644 --- a/postfix/src/lmtp/lmtp_state.c +++ b/postfix/src/lmtp/lmtp_state.c @@ -53,11 +53,12 @@ /* Global library. */ -#include +#include /* Application-specific. */ #include "lmtp.h" +#include "lmtp_sasl.h" /* lmtp_state_alloc - initialize */ @@ -75,6 +76,9 @@ LMTP_STATE *lmtp_state_alloc(void) 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; @@ -88,5 +92,8 @@ void lmtp_state_free(LMTP_STATE *state) vstring_free(state->buffer); vstring_free(state->scratch); vstring_free(state->scratch2); +#ifdef USE_SASL_AUTH + lmtp_sasl_cleanup(state); +#endif myfree((char *) state); } diff --git a/postfix/src/lmtp/lmtp_trouble.c b/postfix/src/lmtp/lmtp_trouble.c index 713e4d89b..38b6185d3 100644 --- a/postfix/src/lmtp/lmtp_trouble.c +++ b/postfix/src/lmtp/lmtp_trouble.c @@ -22,7 +22,7 @@ /* 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; @@ -63,7 +63,7 @@ /* 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 @@ -270,9 +270,9 @@ void lmtp_rcpt_fail(LMTP_STATE *state, int code, RECIPIENT *rcpt, 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; @@ -285,7 +285,7 @@ int lmtp_stream_except(LMTP_STATE *state, int code, char *description) */ 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); diff --git a/postfix/src/nqmgr/.indent.pro b/postfix/src/nqmgr/.indent.pro deleted file mode 120000 index 5c837eca6..000000000 --- a/postfix/src/nqmgr/.indent.pro +++ /dev/null @@ -1 +0,0 @@ -../../.indent.pro \ No newline at end of file diff --git a/postfix/src/nqmgr/.printfck b/postfix/src/nqmgr/.printfck deleted file mode 100644 index 66016ed45..000000000 --- a/postfix/src/nqmgr/.printfck +++ /dev/null @@ -1,25 +0,0 @@ -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 diff --git a/postfix/src/nqmgr/Makefile.in b/postfix/src/nqmgr/Makefile.in deleted file mode 100644 index eea19d355..000000000 --- a/postfix/src/nqmgr/Makefile.in +++ /dev/null @@ -1,290 +0,0 @@ -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 diff --git a/postfix/src/nqmgr/qmgr.c b/postfix/src/nqmgr/qmgr.c deleted file mode 100644 index c7cbd62c5..000000000 --- a/postfix/src/nqmgr/qmgr.c +++ /dev/null @@ -1,555 +0,0 @@ -/*++ -/* 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 -#include -#include -#include - -/* Utility library. */ - -#include -#include -#include -#include - -/* Global library. */ - -#include -#include -#include -#include -#include /* QMGR_SCAN constants */ - -/* Master process interface */ - -#include -#include - -/* 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); -} diff --git a/postfix/src/nqmgr/qmgr.h b/postfix/src/nqmgr/qmgr.h deleted file mode 100644 index 2da9dea0b..000000000 --- a/postfix/src/nqmgr/qmgr.h +++ /dev/null @@ -1,413 +0,0 @@ -/*++ -/* NAME -/* qmgr 3h -/* SUMMARY -/* queue manager data structures -/* SYNOPSIS -/* #include "qmgr.h" -/* DESCRIPTION -/* .nf - - /* - * Utility library. - */ -#include -#include - - /* - * Global library. - */ -#include - - /* - * 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 -/*--*/ diff --git a/postfix/src/nqmgr/qmgr_active.c b/postfix/src/nqmgr/qmgr_active.c deleted file mode 100644 index ec00584bd..000000000 --- a/postfix/src/nqmgr/qmgr_active.c +++ /dev/null @@ -1,478 +0,0 @@ -/*++ -/* 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 -#include -#include -#include -#include -#include -#include -#include - -#ifndef S_IRWXU /* What? no POSIX system? */ -#define S_IRWXU 0700 -#endif - -/* Utility library. */ - -#include -#include -#include -#include - -/* Global library. */ - -#include -#include -#include -#include -#include -#include -#include -#include - -/* 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); - } -} diff --git a/postfix/src/nqmgr/qmgr_bounce.c b/postfix/src/nqmgr/qmgr_bounce.c deleted file mode 100644 index e8f01405f..000000000 --- a/postfix/src/nqmgr/qmgr_bounce.c +++ /dev/null @@ -1,79 +0,0 @@ -/*++ -/* 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 -#include - -/* Utility library. */ - -/* Global library. */ - -#include -#include - -/* 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; -} diff --git a/postfix/src/nqmgr/qmgr_defer.c b/postfix/src/nqmgr/qmgr_defer.c deleted file mode 100644 index 72d30aac9..000000000 --- a/postfix/src/nqmgr/qmgr_defer.c +++ /dev/null @@ -1,164 +0,0 @@ -/*++ -/* 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 - -/* Utility library. */ - -#include -#include - -/* Global library. */ - -#include - -/* 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); -} diff --git a/postfix/src/nqmgr/qmgr_deliver.c b/postfix/src/nqmgr/qmgr_deliver.c deleted file mode 100644 index 3c479746f..000000000 --- a/postfix/src/nqmgr/qmgr_deliver.c +++ /dev/null @@ -1,307 +0,0 @@ -/*++ -/* 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 -#include -#include - -/* Utility library. */ - -#include -#include -#include -#include -#include -#include - -/* Global library. */ - -#include -#include -#include -#include -#include - -/* 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); -} diff --git a/postfix/src/nqmgr/qmgr_enable.c b/postfix/src/nqmgr/qmgr_enable.c deleted file mode 100644 index 5f0c3841b..000000000 --- a/postfix/src/nqmgr/qmgr_enable.c +++ /dev/null @@ -1,107 +0,0 @@ -/*++ -/* 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 - -/* Utility library. */ - -#include -#include - -/* 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); -} diff --git a/postfix/src/nqmgr/qmgr_entry.c b/postfix/src/nqmgr/qmgr_entry.c deleted file mode 100644 index 079ae7a60..000000000 --- a/postfix/src/nqmgr/qmgr_entry.c +++ /dev/null @@ -1,242 +0,0 @@ -/*++ -/* 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 -#include -#include - -/* Utility library. */ - -#include -#include -#include -#include - -/* Global library. */ - -#include - -/* 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); -} diff --git a/postfix/src/nqmgr/qmgr_job.c b/postfix/src/nqmgr/qmgr_job.c deleted file mode 100644 index 3be930997..000000000 --- a/postfix/src/nqmgr/qmgr_job.c +++ /dev/null @@ -1,748 +0,0 @@ -/*++ -/* 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 - -/* Utility library. */ - -#include -#include -#include -#include - -/* 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); -} diff --git a/postfix/src/nqmgr/qmgr_message.c b/postfix/src/nqmgr/qmgr_message.c deleted file mode 100644 index 722f44a14..000000000 --- a/postfix/src/nqmgr/qmgr_message.c +++ /dev/null @@ -1,961 +0,0 @@ -/*++ -/* 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 -#include -#include -#include /* sscanf() */ -#include -#include -#include -#include - -#ifdef STRCASECMP_IN_STRINGS_H -#include -#endif - -/* Utility library. */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* Global library. */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* Client stubs. */ - -#include - -/* 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); - } -} diff --git a/postfix/src/nqmgr/qmgr_move.c b/postfix/src/nqmgr/qmgr_move.c deleted file mode 100644 index 7f86197ec..000000000 --- a/postfix/src/nqmgr/qmgr_move.c +++ /dev/null @@ -1,94 +0,0 @@ -/*++ -/* 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 -#include -#include -#include - -/* Utility library. */ - -#include -#include -#include - -/* Global library. */ - -#include -#include - -/* 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); -} diff --git a/postfix/src/nqmgr/qmgr_peer.c b/postfix/src/nqmgr/qmgr_peer.c deleted file mode 100644 index 9b61e64e5..000000000 --- a/postfix/src/nqmgr/qmgr_peer.c +++ /dev/null @@ -1,136 +0,0 @@ -/*++ -/* 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 - -/* Utility library. */ - -#include -#include -#include - -/* 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); -} diff --git a/postfix/src/nqmgr/qmgr_queue.c b/postfix/src/nqmgr/qmgr_queue.c deleted file mode 100644 index ef7d69672..000000000 --- a/postfix/src/nqmgr/qmgr_queue.c +++ /dev/null @@ -1,250 +0,0 @@ -/*++ -/* 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 -#include - -/* Utility library. */ - -#include -#include -#include -#include - -/* Global library. */ - -#include -#include - -/* 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)); -} diff --git a/postfix/src/nqmgr/qmgr_rcpt_list.c b/postfix/src/nqmgr/qmgr_rcpt_list.c deleted file mode 100644 index 2bb292e01..000000000 --- a/postfix/src/nqmgr/qmgr_rcpt_list.c +++ /dev/null @@ -1,96 +0,0 @@ -/*++ -/* 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 - -/* Utility library. */ - -#include - -/* 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); -} diff --git a/postfix/src/nqmgr/qmgr_scan.c b/postfix/src/nqmgr/qmgr_scan.c deleted file mode 100644 index e0d9fc079..000000000 --- a/postfix/src/nqmgr/qmgr_scan.c +++ /dev/null @@ -1,159 +0,0 @@ -/*++ -/* 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 - -/* Utility library. */ - -#include -#include -#include - -/* Global library. */ - -#include - -/* 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); -} diff --git a/postfix/src/nqmgr/qmgr_transport.c b/postfix/src/nqmgr/qmgr_transport.c deleted file mode 100644 index 7fef4c759..000000000 --- a/postfix/src/nqmgr/qmgr_transport.c +++ /dev/null @@ -1,378 +0,0 @@ -/*++ -/* 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 -#include - -/* Utility library. */ - -#include -#include -#include -#include -#include -#include - -/* Global library. */ - -#include -#include -#include -#include - -/* 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)); -} diff --git a/postfix/src/smtp/smtp_proto.c b/postfix/src/smtp/smtp_proto.c index 0f4394e9d..4a65146b0 100644 --- a/postfix/src/smtp/smtp_proto.c +++ b/postfix/src/smtp/smtp_proto.c @@ -98,11 +98,11 @@ #include #include #include +#include /* Application-specific. */ #include "smtp.h" -#include "quote_821_local.h" #include "smtp_sasl.h" /* diff --git a/postfix/src/smtp/smtp_trouble.c b/postfix/src/smtp/smtp_trouble.c index 0085bda69..d9128f1f5 100644 --- a/postfix/src/smtp/smtp_trouble.c +++ b/postfix/src/smtp/smtp_trouble.c @@ -133,7 +133,7 @@ static void smtp_check_code(SMTP_STATE *state, int code) * 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; } diff --git a/postfix/src/smtpd/smtpd.c b/postfix/src/smtpd/smtpd.c index 6f4703ed4..9df713181 100644 --- a/postfix/src/smtpd/smtpd.c +++ b/postfix/src/smtpd/smtpd.c @@ -57,6 +57,10 @@ /* .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 @@ -343,6 +347,7 @@ bool var_smtpd_sasl_enable; 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 @@ -439,8 +444,11 @@ static int ehlo_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv) 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); @@ -949,7 +957,7 @@ static int data_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *unused_argv) 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; @@ -1447,6 +1455,7 @@ int main(int argc, char **argv) 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[] = { diff --git a/postfix/src/smtpstone/smtp-sink.c b/postfix/src/smtpstone/smtp-sink.c index a10b48e8e..a41801354 100644 --- a/postfix/src/smtpstone/smtp-sink.c +++ b/postfix/src/smtpstone/smtp-sink.c @@ -329,12 +329,19 @@ static void disconnect(SINK_STATE *state) 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); diff --git a/postfix/src/smtpstone/smtp-source.c b/postfix/src/smtpstone/smtp-source.c index c5d9dbcc7..6ab916429 100644 --- a/postfix/src/smtpstone/smtp-source.c +++ b/postfix/src/smtpstone/smtp-source.c @@ -136,10 +136,11 @@ static const char *var_myhostname; 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; @@ -398,7 +399,7 @@ static void start_connect(SESSION *session) 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); } @@ -842,7 +843,7 @@ int main(int argc, char **argv) #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; @@ -853,7 +854,7 @@ int main(int argc, char **argv) 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); } /* diff --git a/postfix/src/util/make_dirs.c b/postfix/src/util/make_dirs.c index 1ca68af13..b3efcddbf 100644 --- a/postfix/src/util/make_dirs.c +++ b/postfix/src/util/make_dirs.c @@ -81,8 +81,18 @@ int make_dirs(const char *path, int perms) } 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; diff --git a/postfix/src/virtual/virtual b/postfix/src/virtual/virtual new file mode 100755 index 000000000..bde19ec8b Binary files /dev/null and b/postfix/src/virtual/virtual differ