]> git.ipfire.org Git - thirdparty/postfix.git/commitdiff
postfix-2.2-20040720
authorWietse Venema <wietse@porcupine.org>
Tue, 20 Jul 2004 05:00:00 +0000 (00:00 -0500)
committerViktor Dukhovni <viktor@dukhovni.org>
Tue, 5 Feb 2013 06:29:43 +0000 (06:29 +0000)
107 files changed:
postfix/.indent.pro
postfix/HISTORY
postfix/Makefile.in
postfix/README_FILES/OVERVIEW
postfix/RELEASE_NOTES
postfix/conf/master.cf
postfix/conf/post-install
postfix/conf/postfix-files
postfix/examples/smtpd-policy/spf.pl
postfix/html/Makefile.in
postfix/html/OVERVIEW.html
postfix/html/cleanup.8.html
postfix/html/ldap_table.5.html
postfix/html/pipe.8.html
postfix/html/postconf.5.html
postfix/html/scache.8.html [new file with mode: 0644]
postfix/html/smtp.8.html
postfix/man/Makefile.in
postfix/man/man5/ldap_table.5
postfix/man/man5/postconf.5
postfix/man/man8/cleanup.8
postfix/man/man8/pipe.8
postfix/man/man8/scache.8 [new file with mode: 0644]
postfix/man/man8/smtp.8
postfix/mantools/make-relnotes
postfix/mantools/postlink
postfix/proto/OVERVIEW.html
postfix/proto/ldap_table
postfix/proto/postconf.proto
postfix/src/anvil/Makefile.in
postfix/src/bounce/Makefile.in
postfix/src/cleanup/Makefile.in
postfix/src/cleanup/cleanup.c
postfix/src/dns/Makefile.in
postfix/src/dns/dns.h
postfix/src/dns/dns_rr.c
postfix/src/error/Makefile.in
postfix/src/flush/Makefile.in
postfix/src/fsstone/Makefile.in
postfix/src/global/Makefile.in
postfix/src/global/mail_params.h
postfix/src/global/mail_proto.h
postfix/src/global/mail_version.h
postfix/src/global/mime_state.c
postfix/src/global/scache.c [new file with mode: 0644]
postfix/src/global/scache.h [new file with mode: 0644]
postfix/src/global/scache_clnt.c [new file with mode: 0644]
postfix/src/global/scache_multi.c [new file with mode: 0644]
postfix/src/global/scache_multi.in [new file with mode: 0644]
postfix/src/global/scache_multi.ref [new file with mode: 0644]
postfix/src/global/scache_single.c [new file with mode: 0644]
postfix/src/lmtp/Makefile.in
postfix/src/local/Makefile.in
postfix/src/master/Makefile.in
postfix/src/oqmgr/Makefile.in
postfix/src/pickup/Makefile.in
postfix/src/pipe/Makefile.in
postfix/src/pipe/pipe.c
postfix/src/postalias/Makefile.in
postfix/src/postcat/Makefile.in
postfix/src/postconf/Makefile.in
postfix/src/postdrop/Makefile.in
postfix/src/postfix/Makefile.in
postfix/src/postkick/Makefile.in
postfix/src/postlock/Makefile.in
postfix/src/postlog/Makefile.in
postfix/src/postmap/Makefile.in
postfix/src/postqueue/Makefile.in
postfix/src/postsuper/Makefile.in
postfix/src/proxymap/Makefile.in
postfix/src/qmgr/Makefile.in
postfix/src/qmqpd/Makefile.in
postfix/src/scache/.indent.pro [new symlink]
postfix/src/scache/Makefile.in [new file with mode: 0644]
postfix/src/scache/scache.c [new file with mode: 0644]
postfix/src/sendmail/Makefile.in
postfix/src/showq/Makefile.in
postfix/src/smtp/Makefile.in
postfix/src/smtp/smtp.c
postfix/src/smtp/smtp.h
postfix/src/smtp/smtp_addr.c
postfix/src/smtp/smtp_connect.c
postfix/src/smtp/smtp_proto.c
postfix/src/smtp/smtp_reuse.c [new file with mode: 0644]
postfix/src/smtp/smtp_reuse.h [new file with mode: 0644]
postfix/src/smtp/smtp_sasl.h
postfix/src/smtp/smtp_sasl_glue.c
postfix/src/smtp/smtp_session.c
postfix/src/smtp/smtp_state.c
postfix/src/smtp/smtp_trouble.c
postfix/src/smtpd/Makefile.in
postfix/src/smtpstone/Makefile.in
postfix/src/spawn/Makefile.in
postfix/src/trivial-rewrite/Makefile.in
postfix/src/util/Makefile.in
postfix/src/util/events.c
postfix/src/util/iostuff.h
postfix/src/util/ring.h
postfix/src/util/stream_recv_fd.c [new file with mode: 0644]
postfix/src/util/stream_send_fd.c [new file with mode: 0644]
postfix/src/util/sys_defs.h
postfix/src/util/unix_recv_fd.c [new file with mode: 0644]
postfix/src/util/unix_send_fd.c [new file with mode: 0644]
postfix/src/util/vstream.c
postfix/src/util/vstream.h
postfix/src/verify/Makefile.in
postfix/src/virtual/Makefile.in

index fbd8874f694735f73126b48755aba2406864f6ed..6c604ed34244bd1b62d9fe7f3de5002ac8d91955 100644 (file)
@@ -1,3 +1,4 @@
+-TSCACHE_HEAD_NODE
 -TABOUNCE
 -TALIAS_TOKEN
 -TANVIL_LOCAL
 -TRESPONSE
 -TREST_TABLE
 -TRES_CONTEXT
+-TSCACHE
+-TSCACHE_CLNT
+-TSCACHE_MULTI
+-TSCACHE_MULTI_DEST
+-TSCACHE_MULTI_ENDP
+-TSCACHE_MULTI_HEAD
+-TSCACHE_SINGLE
+-TSCACHE_SINGLE_DEST
+-TSCACHE_SINGLE_ENDP
 -TSCAN_DIR
 -TSCAN_INFO
 -TSCAN_OBJ
index 5d1e4644d0d1d4588cc97133af63c060fde31b46..6517f04d535e9d49e66350e698eed92c06f80c96 100644 (file)
@@ -8189,8 +8189,8 @@ Apologies for any names omitted.
        of mail probes, so it will no longer block for in_flow_delay
        seconds when mail arrives faster than it is delivered.
        Still need to make mail_stream_finish() asynchronous in
-       order to avoid blocking for trigger_timeout seconds when the
-       queue manager is overwhelmed.  Files:  global/post_mail.c,
+       order to avoid blocking for trigger_timeout seconds when
+       the queue manager is overwhelmed.  Files:  global/post_mail.c,
        verify/verify.c.
 
        Bugfix: removed extraneous sleep() after the last attempt
@@ -8365,13 +8365,13 @@ Apologies for any names omitted.
        Support for multiple A and TXT results in RBL lookups.
        Victor Duchovni, Morgan Stanley.  File: smtpd/smtpd_check.c.
 
-       Support for attribute-based query-reply protocols.
-       Files: util/attr_clnt.[hc], util/auto_clnt.[hc].
+       Support for attribute-based query-reply protocols.  Files:
+       util/attr_clnt.[hc], util/auto_clnt.[hc].
 
 20030711
 
-       Support for plain "name=value\n" attribute protocol.
-       Files: util/attr_{scan,print}_plain.c.
+       Support for plain "name=value\n" attribute protocol.  Files:
+       util/attr_{scan,print}_plain.c.
 
        Bugfix: the LMTP session caching code did not reset the
        EHLO server feature list when it needed to reconnect.
@@ -8399,7 +8399,7 @@ Apologies for any names omitted.
 
 20030717
 
-       Documentation: added description of policy_time_limit to 
+       Documentation: added description of policy_time_limit to
        the SMTPD_POLICY_README document.
 
        Documentation: corrected the command time limit parameter
@@ -8703,8 +8703,8 @@ Apologies for any names omitted.
 
 20031024
 
-       Portability: added localhost to mydestination for sites that
-       turn off append_dot_mydomain. File: global/mail_params.h.
+       Portability: added localhost to mydestination for sites
+       that turn off append_dot_mydomain. File: global/mail_params.h.
 
 20031027
 
@@ -8958,7 +8958,7 @@ Apologies for any names omitted.
        Feature: the reject_unlisted_sender(recipient) SMTPD access
        restriction rejects an address that matches a local, virtual
        or relay domain, while the address is not listed in the
-       corresponding local, virtual or relay recipient table. 
+       corresponding local, virtual or relay recipient table.
 
        Compatibility: the check_recipient_maps restriction works
        like reject_unlisted_recipient, but will eventually be
@@ -9037,8 +9037,8 @@ Apologies for any names omitted.
        Files:  *qmgr/qmgr_deliver.c.
 
        Cleanup: in postfix-files, symbolic links and hard links
-       are now first-class citizens with explicit mention of
-       source and destination pathnames. Files: postfix-install,
+       are now first-class citizens with explicit mention of source
+       and destination pathnames. Files: postfix-install,
        conf/postfix-files, conf/post-install.
 
 20040116
@@ -9071,8 +9071,8 @@ Apologies for any names omitted.
        longer needed and have been removed, as are the default
        *_table configuration files.
 
-       Cleanup: support for the non-standard Errors-To: header
-       is removed. File: cleanup/cleanup_message.c.
+       Cleanup: support for the non-standard Errors-To: header is
+       removed. File: cleanup/cleanup_message.c.
 
 20040121
 
@@ -9101,11 +9101,11 @@ Apologies for any names omitted.
 
 20040123
 
-       Feature: set smtpd_reject_unlisted_{sender,recipient}=no to
-       turn off automatic rejection of non-existent local, virtual
-       or relay addresses. This way it can be made conditional
-       for local clients, always on for remote clients. Files:
-       global/mail_params.h, smtpd/smtpd.c, smtpd/smtpd_check.c.
+       Feature: set smtpd_reject_unlisted_{sender,recipient}=no
+       to turn off automatic rejection of non-existent local,
+       virtual or relay addresses. This way it can be made
+       conditional for local clients, always on for remote clients.
+       Files:  global/mail_params.h, smtpd/smtpd.c, smtpd/smtpd_check.c.
 
 20040124
 
@@ -9114,8 +9114,8 @@ Apologies for any names omitted.
 
 20040126
 
-       Safety: handle the case that main.cf is updated while it is
-       being read. File: util/dict.c.
+       Safety: handle the case that main.cf is updated while it
+       is being read. File: util/dict.c.
 
        Feature: "instance" attribute that links policy etc. queries
        to the same message instance.
@@ -9186,8 +9186,8 @@ Apologies for any names omitted.
 
 20040324
 
-       Portability: ekkoBSD support by Philip Reynolds.
-       Files: makedefs, util/sys_defs.h.
+       Portability: ekkoBSD support by Philip Reynolds.  Files:
+       makedefs, util/sys_defs.h.
 
 20040325
 
@@ -9461,8 +9461,125 @@ Apologies for any names omitted.
        Workaround for fragile clients: add microsecond time to
        maildir filename.  Files: virtual/maildir.c, local/maildir.c.
 
+20040628-20040701
+
+       SMTP connection caching work with Victor Duchovni.
+
+       New module (later renamed to global/scache_single.c) for
+       protocol-independent session caching.  The initial
+       implementation supports in-process, single-session caching
+       only.  A later version will support a central session cache
+       daemon.  Some more work is needed for passivation/activation
+       of session attributes.
+
+       New function vstream_fdclose() to destroy a VSTREAM while
+       leaving the underlying file(s) open. Files: util/vstream.[hc].
+
+       New function dns_rr_remove() to remove one record from a
+       resource record list. Some more work is needed to turn the
+       list into a doubly-linked one. Files: dns/dns.h, dns/dns_rr.c.
+
+       Restructuring of the SMTP protocol engine for session
+       caching. File: smtp/smtp_proto.c.
+
+       Restructuring of the connection management module, and
+       first implementation of SMTP connection caching.  To enable,
+       specify an smtp_connection_cache_time value greater than
+       zero. The time unit is seconds. File: smtp/smtp_connect.c.
+
+       New code to passivate and re-activate SMTP_SESSION objects,
+       and isolation of session save/lookup in its own module.
+       Files: smtp/smtp_session.c, smtp/smtp_reuse.c.
+
+       Refinement: smtp_cache_reuse_limit parameter to bound the
+       number of times a session may be reused.  
+
+       Refinements: when a session comes from the cache, give it
+       back to the cache anyway (even when it will not be listed
+       under the next-hop destination name).
+
+       Future refinements should also include a bound on the number
+       of consecutive and total non-delivering uses and other
+       statistics.
+
+20040714
+
+       Bugfix: the code to eliminate the local MTA from the MX
+       address list did not handle the case that inet_interfaces
+       produced a less preferred match than proxy_interfaces.
+       Victor Duchovni, Morgan Stanley. File: smtp/smtp_addr.c.
+
+20040715
+
+       Resume work on SMTP session caching.  All good sessions
+       are now cached under their IP address. As before, only the
+       first good session per delivery request is cached under
+       the original next-hop destination.
+
+       At this point, SMTP session caching works, with a session
+       cache client module that uses in-process session caching.
+       This is sufficient to demonstrate that the SMTP client is
+       ready for session caching.
+
+20040716
+
+       New modules to send file descriptors from one process into
+       another one. This will be needed for implementing a central
+       connection cache manager daemon. Most systems use UNIX-domain
+       sockets as the transport for this. On Solaris we use streams
+       instead.  Applications are supposed to invoke LOCAL_SEND_FD()
+       and LOCAL_RECV_FD().  Files: {unix,streams}_{send,recv}_fd.c.
+
+20040717
+
+       First implementation of a session caching client API that
+       actually sends to/receives from a caching server process.
+       The old in-process, single-session caching functionality
+       is preserved as global/scache_single.c, so that we can use
+       it for bootstrapping the session cache server. File:
+       global/scache_clnt.c.
+
+       First implementation of the scache session cache server,
+       using the same in-process session caching code that was
+       used to bootstrap the SMTP client.  File: scache/scache.c.
+
+20040718
+
+       Performance: the default RSET timeouts are reduced from
+       120s to 20s.  Perhaps there should be different RSET timeout
+       for address probes and for session cache checks. File:
+       global/mail_params.h.
+
+20040719
+
+       Multi-session connection cache module. Implementing this
+       was actually the easiest part of the entire connection
+       caching project. File: global/scache_multi.c.
+
+20040720
+
+       Bugfix: event_drain() falsely reported a single-entry timer
+       queue as empty.  File: util/events.c.
+
+       Completed the multi-session cache support for SMTP. The
+       code can be stress tested with a driver program that reads
+       commands from a script. It is not practical to manually
+       test the effects of collisions in the time or in name space
+       domains. File:  global/scache.c.
+
 Open problems:
 
+       Low: update events.c so that 1-second timer requests do
+       not suffer from rounding errors. This is needed for 1-second
+       SMTP session caching time limits.
+
+       Low: trivial-rewrite should examine the map change status
+       every N seconds.
+
+       Low: per-sender resolver personalities?
+
+       Low: configurable internal/system locking method.
+
        Low: make sure CCARGS -I options come at the end.
 
        Low: add INSTALL section for pre-existing Postfix systems.
index 53aa735bcb384e81070d17b61358538514cbd844..52faccc7f89cfa1f34ceea61709ad9759465e5b2 100644 (file)
@@ -7,7 +7,7 @@ DIRS    = src/util src/global src/dns src/master src/postfix src/smtpstone \
        src/pipe src/showq src/postalias src/postcat src/postconf src/postdrop \
        src/postkick src/postlock src/postlog src/postmap src/postqueue \
        src/postsuper src/qmqpd src/spawn src/flush src/verify \
-       src/virtual src/proxymap src/anvil
+       src/virtual src/proxymap src/anvil src/scache
 MANDIRS        = proto man html
 
 default: update
index 62c1d804a74269689e39979e3c6f2b99f876c4fe..8ea7eb0f7df63083f152e60126757ed9649da126 100644 (file)
@@ -265,6 +265,18 @@ queues.
     processes. This overcomes chroot restrictions, and reduces the number of
     open lookup tables by sharing one open table among multiple processes.
 
+  * The scache(8) server maintains the session cache for the Postfix smtp(8)
+    client. When session caching is enabled for selected destinations, the smtp
+    (8) client does not disconnect immediately after a mail transaction, but
+    gives the connection to the session cache server. The smtp(8) client
+    continues with some other mail delivery request. Meanwhile, the session
+    cache server keeps the connection open for a limited amount of time. During
+    that time, any smtp(8) process can ask the scache(8) server for that cached
+    session and use it for mail delivery.
+
+     smtp(8)  ->  scache(8)  ->  smtp(8)
+                                     
+
   * The showq(8) servers list the Postfix queue status. This is the queue
     listing service that does the work for the mailq(1) and postqueue(1)
     commands.
index 6105973ea5691f7d58287cfb850d465df05b9791..aa0ad76c764bf2543e2c0f3e15320bf597399c22 100644 (file)
@@ -7,6 +7,74 @@ snapshot release).  Patches are issued for the official release
 and change the patchlevel and the release date. Patches are never
 issued for snapshot releases.
 
+Incompatible changes with snapshot Postfix-2.2-20040720
+=======================================================
+
+The default SMTP/LMTP timeouts for sending RSET are reduced to 20s.
+
+Major changes with snapshot Postfix-2.2-20040720
+================================================
+
+Selective SMTP session caching. Instead of disconnecting immediately
+after a mail transaction, the SMTP client can save the open session
+to a session cache daemon, so that any SMTP client process can use
+the same session for another mail transaction.
+
+This feature introduces the scache (session cache) server, which
+is added to your master.cf file when you upgrade Postfix.
+
+*** You need to execute "postfix reload" when upgrading from Postfix
+*** version 2.1 or later.
+
+*** You need to execute "postfix stop" when upgrading from Postfix
+*** version 2.0 or earlier. Execute "postfix start" when done.
+
+Session caching is enabled with the new smtp_connection_cache_domains
+parameter. Specify a list of destinations or lookup tables:
+
+- a domain name (the right-hand side of an email address),
+
+- a relay host (including optional [] and/or non-default TCP port),
+using the exact same spelling as in main.cf or in the transport map,
+
+- a /file/name with domains and/or relay hosts,
+
+- a type:table with domains and/or relay hosts on the left-hand
+side; the right-hand side result from type:table lookups is ignored.
+
+The following optimizes deliveries to hosts that your machine relays
+mail to:
+
+    smtp_connection_cache_domains = $relay_domains $relayhost
+
+A setting that tries to optimize deliveries to problem sites:
+
+    smtp_connection_cache_domains = hotmail.com...
+
+Cached SMTP sessions are allowed to remain unused for only a limited
+amount of time (smtp_connection_cache_time_limit, default:  2
+seconds). This limits the impact on remote server resources.
+Specify larger values only with permission from the remote sites.
+
+To avoid triggering remote problems, the same SMTP session is used
+only a limited number of times (smtp_connection_cache_reuse_limit,
+default:  10).
+
+Robustness note: to prevent mail from being delivered to the wrong
+server, the session caching feature explicitly labels each cached
+session with destination domain and IP address information. A
+session cache lookup succeeds only when the correct information is
+specified.
+
+Limitations:
+
+- SMTP session caching does not work with TLS (the necessary support
+for object passivation and re-activation does not exist).
+
+- SMTP session caching assumes that SASL credentials are valid for
+all hostnames or domain names that map onto the same IP address
+and TCP port.
+
 Major changes with snapshot Postfix-2.2-20040621
 ================================================
 
@@ -14,5 +82,5 @@ Control over the working directory when executing an external
 command.  With the pipe(8) mailer, specify directory=pathname, and
 with local(8) specify "command_execution_directory = expression"
 where "expression" is subject to $home etc. macro expansion. The
-result of macro expansion is restricted by the set of charaacters
+result of macro expansion is restricted by the set of characters
 specified with execution_directory_expansion_filter.
index 0c2475ee08a6c5b6b56307802aae04c90ba3f04b..00ec103e143b8b520cdba311c81e469d65abef79 100644 (file)
@@ -101,6 +101,7 @@ local     unix  -       n       n       -       -       local
 virtual   unix  -       n       n       -       -       virtual
 lmtp      unix  -       -       n       -       -       lmtp
 anvil     unix  -       -       n       -       1       anvil
+scache   unix  -       -       n       -       1       scache
 #
 # Interfaces to non-Postfix software. Be sure to examine the manual
 # pages of the non-Postfix software to find out what options it wants.
index c19add3e33daec6ef8cc968b9e83a0d7750ad1d8..f23d9b5c1a92543e57fca7f8d70bcda4150ac03b 100644 (file)
@@ -620,6 +620,15 @@ anvil        unix  -       -       n       -       1       anvil
 EOF
     }
 
+    # Add missing scache service to master.cf.
+
+    grep '^scache.*scache' $config_directory/master.cf >/dev/null || {
+       echo Editing $config_directory/master.cf, adding missing entry for scache service
+       cat >>$config_directory/master.cf <<EOF || exit 1
+scache   unix  -       -       n       -       1       scache
+EOF
+    }
+
     # Report (but do not remove) obsolete files.
 
     test -n "$obsolete" && {
index 7f48a82f7867fab76450a5858939dc24603c30dc..89b1cd4f8f13b6987869cd2f05e9f590a3c40c92 100644 (file)
@@ -75,6 +75,7 @@ $daemon_directory/pipe:f:root:-:755
 $daemon_directory/proxymap:f:root:-:755
 $daemon_directory/qmgr:f:root:-:755
 $daemon_directory/qmqpd:f:root:-:755
+$daemon_directory/scache:f:root:-:755
 $daemon_directory/showq:f:root:-:755
 $daemon_directory/smtp:f:root:-:755
 $daemon_directory/smtpd:f:root:-:755
@@ -165,6 +166,7 @@ $manpage_directory/man8/pipe.8:f:root:-:644
 $manpage_directory/man8/proxymap.8:f:root:-:644
 $manpage_directory/man8/qmgr.8:f:root:-:644
 $manpage_directory/man8/qmqpd.8:f:root:-:644
+$manpage_directory/man8/scache.8:f:root:-:644
 $manpage_directory/man8/showq.8:f:root:-:644
 $manpage_directory/man8/smtp.8:f:root:-:644
 $manpage_directory/man8/smtpd.8:f:root:-:644
index d04f63a53e2478b21bb37c8dd57dd05e387ce718..f48b8ac28fb0fe2eb99765dfee5fdf633c17cb87 100755 (executable)
@@ -196,7 +196,7 @@ sub sender_permitted_from {
 
   if    ($result eq "pass")  { return "DUNNO"; }
   elsif ($result eq "fail")  { return "REJECT " . ($smtp_comment || $header_comment); }
-  elsif ($result eq "error") { return "450 temporary failure: $smtp_comemnt"; }
+  elsif ($result eq "error") { return "450 temporary failure: $smtp_comment"; }
   else                       { return "DUNNO"; }
   # unknown, softfail, and none all return DUNNO
 
index f889fc05f41c6aa6968f4198f50fa16aec7a85e9..ba523410f9fec4f4c54f43bb852f36e634ab041c 100644 (file)
@@ -6,7 +6,8 @@ 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 \
        oqmgr.8.html spawn.8.html flush.8.html virtual.8.html qmqpd.8.html \
-       trace.8.html verify.8.html proxymap.8.html anvil.8.html
+       trace.8.html verify.8.html proxymap.8.html anvil.8.html \
+       scache.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 \
@@ -57,6 +58,10 @@ anvil.8.html: ../src/anvil/anvil.c
        PATH=../mantools:$$PATH; \
        srctoman $? | $(AWK) | nroff -man | uniq | $(MAN2HTML) | postlink >$@
 
+scache.8.html: ../src/scache/scache.c
+       PATH=../mantools:$$PATH; \
+       srctoman $? | $(AWK) | nroff -man | uniq | $(MAN2HTML) | postlink >$@
+
 lmtp.8.html: ../src/lmtp/lmtp.c
        PATH=../mantools:$$PATH; \
        srctoman $? | $(AWK) | nroff -man | uniq | $(MAN2HTML) | postlink >$@
index 02ab2e223573c1115010550305a15b13ab0fc3e1..f3e1c747bb88584f7d20ab118fa4f9c679c9646b 100644 (file)
@@ -535,6 +535,26 @@ service to Postfix processes. This overcomes chroot restrictions,
 and reduces the number of open lookup tables by sharing one open
 table among multiple processes. </p>
 
+<li> <p> The <a href="scache.8.html">scache(8)</a> server maintains the session cache for the
+Postfix <a href="smtp.8.html">smtp(8)</a> client. When session caching is enabled for selected
+destinations, the <a href="smtp.8.html">smtp(8)</a> client does not disconnect immediately
+after a mail transaction, but gives the connection to the session
+cache server.  The <a href="smtp.8.html">smtp(8)</a> client continues with some other mail
+delivery request. Meanwhile, the session cache server keeps the
+connection open for a limited amount of time.  During that time,
+any <a href="smtp.8.html">smtp(8)</a> process can ask the <a href="scache.8.html">scache(8)</a> server for that cached
+session and use it for mail delivery.  </p>
+
+<table>
+
+<tr> <td> <td align="center" bgcolor="#f0f0ff"> <br> <a href="smtp.8.html">smtp(8)</a> <br>
+&nbsp; </td> <td> <tt> -&gt; </tt> </td> <td> <td align="center"
+bgcolor="#f0f0ff"> <br> <a href="scache.8.html">scache(8)</a> <br> &nbsp; </td> <td> <tt> -&gt;
+</tt> </td> <td> <td align="center" bgcolor="#f0f0ff"> <br> <a href="smtp.8.html">smtp(8)</a>
+<br> &nbsp; </td>
+
+</table>
+
 <li> <p> The <a href="showq.8.html">showq(8)</a> servers list the Postfix queue status. This
 is the queue listing service that does the work for the <a href="mailq.1.html">mailq(1)</a>
 and <a href="postqueue.1.html">postqueue(1)</a> commands.  </p>
index cfc6ce6e781c2fd6ea138ae8f90c9891f99b8d4b..c9d3beb2f3470474da34e1e2e682b0916afc2347 100644 (file)
@@ -131,8 +131,8 @@ CLEANUP(8)                                             CLEANUP(8)
               strings.
 
        <b><a href="postconf.5.html#mime_nesting_limit">mime_nesting_limit</a> (100)</b>
-              The maximal nesting level of  multipart  mail  that
-              the MIME processor will handle.
+              The maximal recursion level that the MIME processor
+              will handle.
 
        <b><a href="postconf.5.html#strict_8bitmime">strict_8bitmime</a> (no)</b>
               Enable  both  <a href="postconf.5.html#strict_7bit_headers">strict_7bit_headers</a>  and strict_8bit-
@@ -258,8 +258,8 @@ CLEANUP(8)                                             CLEANUP(8)
               strings.
 
        <b><a href="postconf.5.html#mime_nesting_limit">mime_nesting_limit</a> (100)</b>
-              The  maximal  nesting  level of multipart mail that
-              the MIME processor will handle.
+              The maximal recursion level that the MIME processor
+              will handle.
 
        <b><a href="postconf.5.html#queue_file_attribute_count_limit">queue_file_attribute_count_limit</a> (100)</b>
               The maximal number of (name=value) attributes  that
index fe7c8b75a82beddfed570403acd6fe094dc5c5b0..d8d363cac98ddc8998c5acc0858a5bef4966990d 100644 (file)
@@ -138,131 +138,131 @@ LDAP_TABLE(5)                                       LDAP_TABLE(5)
 
               <b>%u</b>     When the input key is an address of the form
                      user@domain, <b>%u</b>  is  replaced  by  the  (RFC
-                     2254)  quoted  local part of the address. If
-                     no domain is specified, <b>%u</b>  is  replaced  by
-                     the entire search string.
+                     2254) quoted local part of the address. Oth-
+                     erwise, <b>%u</b> is replaced by the entire  search
+                     string.
 
               <b>%d</b>     When the input key is an address of the form
                      user@domain, <b>%d</b>  is  replaced  by  the  (RFC
                      2254)  quoted  domain  part  of the address.
-                     When the input key has no domain  qualifier,
-                     <b>%d</b>  is replaced by the entire search string.
+                     Otherwise, <b>%d</b>  is  replaced  by  the  entire
+                     search string.
 
-              The "domain" parameter described below  limits  the
-              input  keys  to addresses in matching domains. When
-              the "domain" parameter is non-empty,  LDAP  queries
-              for  unqualified  addresses  or  addresses  in non-
+              The  "domain"  parameter described below limits the
+              input keys to addresses in matching  domains.  When
+              the  "domain"  parameter is non-empty, LDAP queries
+              for unqualified  addresses  or  addresses  in  non-
               matching  domains  are  suppressed  and  return  no
               results.
 
               NOTE: DO NOT put quotes around the query filter.
 
        <b>result_filter (default: %s</b>)
-              Format  template applied to result attributes. Sup-
-              ports the same expansions as the query_filter,  and
-              can  be  easily  used  to append (or prepend) text.
-              This parameter supports the  following  '%'  expan-
+              Format template applied to result attributes.  Sup-
+              ports  the same expansions as the query_filter, and
+              can be easily used to  append  (or  prepend)  text.
+              This  parameter  supports  the following '%' expan-
               sions:
 
-              <b>%s</b>     This  is replaced by the value of the result
+              <b>%s</b>     This is replaced by the value of the  result
                      attribute.
 
-              <b>%u</b>     When the result attribute is an  address  of
-                     the  form  user@domain, <b>%u</b> is replaced local
-                     part of the address, if the result attribute
-                     is unqualified, <b>%u</b> is replaced by the entire
+              <b>%u</b>     When   the  result  attribute  value  is  an
+                     address  of  the  form  user@domain,  <b>%u</b>  is
+                     replaced  by  the local part of the address.
+                     Otherwise, <b>%u</b>  is  replaced  by  the  entire
                      attribute value.
 
-              <b>%d</b>     When a result attribute is an address of the
-                     form  user@domain,  <b>%d</b>  is  replaced  by the
-                     domain part of the attribute value.   If  an
-                     attribute   value   is   unqualified  <b>%d</b>  is
-                     replaced by the entire attribute value.
+              <b>%d</b>     When  a result attribute value is an address
+                     of the form user@domain, <b>%d</b> is  replaced  by
+                     the  domain  part  of  the  attribute value.
+                     Otherwise, <b>%d</b>  is  replaced  by  the  entire
+                     attribute value.
 
               For  example,  using  "result_filter  =  <a href="smtp.8.html">smtp</a>:[%s]"
               allows one to use a mailHost attribute as the basis
-              of a <a href="transport.5.html">transport(5)</a> table. After applying the  result
-              filter,  multiple  values are concatenated as comma
-              separated   strings.   The   expansion_limit    and
-              size_limit  parameters explained below allow one to
-              restrict the number of values in the result,  which
-              is  especially useful for maps that should return a
+              of  a <a href="transport.5.html">transport(5)</a> table. After applying the result
+              filter, multiple values are concatenated  as  comma
+              separated    strings.   The   expansion_limit   and
+              size_limit parameters explained below allow one  to
+              restrict  the number of values in the result, which
+              is especially useful for maps that should return  a
               single value.
 
-              The default value <b>%s</b> specifies that each  attribute
+              The  default value <b>%s</b> specifies that each attribute
               value should be used as is.
 
               NOTE: DO NOT put quotes around the result filter!
 
        <b>domain (default: no domain list)</b>
-              This  is a list of domain names, paths to files, or
-              dictionaries. When specified, only fully  qualified
-              search  keys  with  a  *non-empty*  localpart and a
-              matching domain are  eligible  for  lookup:  'user'
-              lookups,  bare domain lookups and "@domain" lookups
-              are not performed. This  can  significantly  reduce
+              This is a list of domain names, paths to files,  or
+              dictionaries.  When specified, only fully qualified
+              search keys with  a  *non-empty*  localpart  and  a
+              matching  domain  are  eligible  for lookup: 'user'
+              lookups, bare domain lookups and "@domain"  lookups
+              are  not  performed.  This can significantly reduce
               the query load on the LDAP server.
                   domain = postfix.org, hash:/etc/postfix/search-
               domains
 
-              It is best not to use LDAP  to  store  the  domains
+              It  is  best  not  to use LDAP to store the domains
               eligible for LDAP lookups.
 
-              NOTE:  DO  NOT  define  this parameter for <a href="local.8.html">local(8)</a>
+              NOTE: DO NOT define  this  parameter  for  <a href="local.8.html">local(8)</a>
               aliases.
 
        <b>result_attribute (default: maildrop)</b>
-              The attribute(s) Postfix will read from any  direc-
+              The  attribute(s) Postfix will read from any direc-
               tory entries returned by the lookup, to be resolved
               to an email address.
                   result_attribute = mailbox,maildrop
 
        <b>special_result_attribute (No default)</b>
               The attribute(s) of directory entries that can con-
-              tain  DNs or URLs. If found, a recursive subsequent
+              tain DNs or URLs. If found, a recursive  subsequent
               search is done using their values.
                   special_result_attribute = member
 
-              DN recursion retrieves the  same  result_attributes
+              DN  recursion  retrieves the same result_attributes
               as the main query, including the special attributes
-              for further  recursion.  URI  processing  retrieves
-              only  those attributes that are included in the URI
-              definition    and    are    *also*    listed     in
-              "result_attribute".  If  the  URI  lists any of the
-              map's special result  attributes,  these  are  also
+              for  further  recursion.  URI  processing retrieves
+              only those attributes that are included in the  URI
+              definition     and    are    *also*    listed    in
+              "result_attribute". If the URI  lists  any  of  the
+              map's  special  result  attributes,  these are also
               retrieved and used recursively.
 
        <b>scope (default: sub)</b>
-              The  LDAP  search  scope: <b>sub</b>, <b>base</b>, or <b>one</b>.  These
+              The LDAP search scope: <b>sub</b>, <b>base</b>,  or  <b>one</b>.   These
               translate into LDAP_SCOPE_SUBTREE, LDAP_SCOPE_BASE,
               and LDAP_SCOPE_ONELEVEL.
 
        <b>bind (default: yes)</b>
-              Whether  or  not  to bind to the LDAP server. Newer
+              Whether or not to bind to the  LDAP  server.  Newer
               LDAP implementations don't require clients to bind,
               which saves time. Example:
                   bind = no
 
-              If  you do need to bind, you might consider config-
-              uring Postfix to connect to the local machine on  a
-              port  that's  an SSL tunnel to your LDAP server. If
-              your LDAP server doesn't natively support SSL,  put
+              If you do need to bind, you might consider  config-
+              uring  Postfix to connect to the local machine on a
+              port that's an SSL tunnel to your LDAP  server.  If
+              your  LDAP server doesn't natively support SSL, put
               a tunnel (wrapper, proxy, whatever you want to call
-              it) on that system too.  This  should  prevent  the
-              password  from traversing the network in the clear.
+              it)  on  that  system  too. This should prevent the
+              password from traversing the network in the  clear.
 
        <b>bind_dn (default: empty)</b>
-              If you do have to bind, do  it  with  this  distin-
+              If  you  do  have  to bind, do it with this distin-
               guished name. Example:
                   bind_dn = uid=postfix, dc=your, dc=com
 
        <b>bind_pw (default: empty)</b>
-              The  password  for the distinguished name above. If
+              The password for the distinguished name  above.  If
               you have to use this, you probably want to make the
               map configuration file readable only by the Postfix
-              user. When using the obsolete <a href="ldap_table.5.html">ldap</a>:ldapsource  syn-
+              user.  When using the obsolete <a href="ldap_table.5.html">ldap</a>:ldapsource syn-
               tax, with map parameters in main.cf, it is not pos-
-              sible to securely store the bind password. This  is
+              sible  to securely store the bind password. This is
               because main.cf needs to be world readable to allow
               local accounts to submit mail via the sendmail com-
               mand. Example:
@@ -273,43 +273,43 @@ LDAP_TABLE(5)                                       LDAP_TABLE(5)
        <b>cache_expiry (IGNORED with a warning)</b>
 
        <b>cache_size (IGNORED with a warning)</b>
-              The  above  parameters  are  NO LONGER SUPPORTED by
+              The above parameters are  NO  LONGER  SUPPORTED  by
               Postfix.   Cache  support  has  been  dropped  from
               OpenLDAP as of release 2.1.13.
 
        <b>recursion_limit (default: 1000)</b>
-              A  limit on the nesting depth of DN and URL special
-              result attribute evaluation. The limit  must  be  a
+              A limit on the nesting depth of DN and URL  special
+              result  attribute  evaluation.  The limit must be a
               non-zero positive number.
 
        <b>expansion_limit (default: 0)</b>
-              A  limit  on  the  total  number of result elements
-              returned (as a comma separated list)  by  a  lookup
-              against  the  map.   A setting of zero disables the
-              limit. Lookups fail with a temporary error  if  the
-              limit  is exceeded.  Setting the limit to 1 ensures
+              A limit on the  total  number  of  result  elements
+              returned  (as  a  comma separated list) by a lookup
+              against the map.  A setting of  zero  disables  the
+              limit.  Lookups  fail with a temporary error if the
+              limit is exceeded.  Setting the limit to 1  ensures
               that lookups do not return multiple values.
 
        <b>size_limit (default: $expansion_limit)</b>
-              A limit on the number of LDAP entries  returned  by
-              any  single  LDAP  query  performed  as part of the
-              lookup. A setting of 0 disables the limit.   Expan-
-              sion  of DN and URL references involves nested LDAP
-              queries, each of which is separately  subjected  to
+              A  limit  on the number of LDAP entries returned by
+              any single LDAP query  performed  as  part  of  the
+              lookup.  A setting of 0 disables the limit.  Expan-
+              sion of DN and URL references involves nested  LDAP
+              queries,  each  of which is separately subjected to
               this limit.
 
-              Note:  even a single LDAP entry can generate multi-
-              ple lookup results, via multiple result  attributes
-              and/or  multi-valued  result attributes. This limit
+              Note: even a single LDAP entry can generate  multi-
+              ple  lookup results, via multiple result attributes
+              and/or multi-valued result attributes.  This  limit
               caps the per query resource utilization on the LDAP
-              server,  not  the  final multiplicity of the lookup
-              result. It is  analogous  to  the  "-z"  option  of
+              server, not the final multiplicity  of  the  lookup
+              result.  It  is  analogous  to  the  "-z" option of
               "ldapsearch".
 
        <b>dereference (default: 0)</b>
-              When  to  dereference LDAP aliases. (Note that this
+              When to dereference LDAP aliases. (Note  that  this
               has nothing do with Postfix aliases.) The permitted
-              values  are  those  legal  for the OpenLDAP/UM LDAP
+              values are those legal  for  the  OpenLDAP/UM  LDAP
               implementations:
 
               0      never
@@ -321,99 +321,99 @@ LDAP_TABLE(5)                                       LDAP_TABLE(5)
               3      always
 
               See ldap.h or the ldap_open(3) or ldapsearch(1) man
-              pages for more information. And if you're using  an
+              pages  for more information. And if you're using an
               LDAP package that has other possible values, please
-              bring  it  to  the  attention   of   the   postfix-
+              bring   it   to   the  attention  of  the  postfix-
               users@postfix.org mailing list.
 
        <b>chase_referrals (default: 0)</b>
-              Sets  (or clears) LDAP_OPT_REFERRALS (requires LDAP
+              Sets (or clears) LDAP_OPT_REFERRALS (requires  LDAP
               version 3 support).
 
        <b>version (default: 2)</b>
               Specifies the LDAP protocol version to use.
 
        <b>debuglevel (default: 0)</b>
-              What level to set for  debugging  in  the  OpenLDAP
+              What  level  to  set  for debugging in the OpenLDAP
               libraries.
 
 <b>LDAP SSL AND STARTTLS PARAMETERS</b>
-       If  you're  using the OpenLDAP libraries compiled with SSL
-       support, Postfix can connect to LDAP SSL servers  and  can
+       If you're using the OpenLDAP libraries compiled  with  SSL
+       support,  Postfix  can connect to LDAP SSL servers and can
        issue the STARTTLS command.
 
-       LDAP  SSL service can be requested by using a LDAP SSL URL
+       LDAP SSL service can be requested by using a LDAP SSL  URL
        in the server_host parameter:
            server_host = ldaps://ldap.your.com:636
 
        STARTTLS can be turned on with the start_tls parameter:
            start_tls = yes
 
-       Both forms require LDAP protocol version 3, which  has  to
+       Both  forms  require LDAP protocol version 3, which has to
        be set explicitly with:
            version = 3
 
        If any of the Postfix programs querying the map is config-
-       ured in master.cf to run chrooted,  all  the  certificates
+       ured  in  master.cf  to run chrooted, all the certificates
        and keys involved have to be copied to the chroot jail. Of
-       course, the private keys should only be  readable  by  the
+       course,  the  private  keys should only be readable by the
        user "postfix".
 
-       The  following  parameters  are  relevant  to LDAP SSL and
+       The following parameters are  relevant  to  LDAP  SSL  and
        STARTTLS:
 
        <b>start_tls (default: no)</b>
               Whether or not to issue STARTTLS upon connection to
-              the  server.  Don't set this with LDAP SSL (the SSL
+              the server.  Don't set this with LDAP SSL (the  SSL
               session is setup automatically when the TCP connec-
               tion is opened).
 
-       <b>tls_ca_cert_dir   (No   default;   set   either   this  or</b>
+       <b>tls_ca_cert_dir  (No   default;   set   either   this   or</b>
        <b>tls_ca_cert_file)</b>
               Directory  containing  X509  Certificate  Authority
-              certificates in PEM format which are to  be  recog-
-              nized  by  the  client  in SSL/TLS connections. The
-              files each contain one CA certificate.   The  files
-              are  looked  up  by the CA subject name hash value,
-              which must hence be available. If more than one  CA
-              certificate  with  the  same name hash value exist,
-              the extension must be different  (e.g.  9d66eef0.0,
-              9d66eef0.1  etc).  The  search  is performed in the
-              ordering of the  extension  number,  regardless  of
+              certificates  in  PEM format which are to be recog-
+              nized by the client  in  SSL/TLS  connections.  The
+              files  each  contain one CA certificate.  The files
+              are looked up by the CA subject  name  hash  value,
+              which  must hence be available. If more than one CA
+              certificate with the same name  hash  value  exist,
+              the  extension  must be different (e.g. 9d66eef0.0,
+              9d66eef0.1 etc). The search  is  performed  in  the
+              ordering  of  the  extension  number, regardless of
               other  properties  of  the  certificates.  Use  the
               c_rehash utility (from the OpenSSL distribution) to
               create the necessary links.
 
-       <b>tls_ca_cert_file   (No   default;   set   either  this  or</b>
+       <b>tls_ca_cert_file  (No  default;   set   either   this   or</b>
        <b>tls_ca_cert_dir)</b>
               File containing the X509 Certificate Authority cer-
-              tificates in PEM format which are to be  recognized
-              by  the client in SSL/TLS connections. This setting
+              tificates  in PEM format which are to be recognized
+              by the client in SSL/TLS connections. This  setting
               takes precedence over tls_ca_cert_dir.
 
        <b>tls_cert (No default; you must set this)</b>
-              File containing client's  X509  certificate  to  be
+              File  containing  client's  X509  certificate to be
               used by the client in SSL/ TLS connections.
 
        <b>tls_key (No default; you must set this)</b>
-              File  containing  the  private key corresponding to
+              File containing the private  key  corresponding  to
               the above tls_cert.
 
        <b>tls_require_cert (default: no)</b>
               Whether or not to request server's X509 certificate
-              and  check  its  validity when establishing SSL/TLS
+              and check its validity  when  establishing  SSL/TLS
               connections.
 
        <b>tls_random_file (No default)</b>
-              Path of a file to  obtain  random  bits  from  when
-              /dev/[u]random  is not available, to be used by the
+              Path  of  a  file  to  obtain random bits from when
+              /dev/[u]random is not available, to be used by  the
               client in SSL/TLS connections.
 
        <b>tls_cipher_suite (No default)</b>
               Cipher suite to use in SSL/TLS negotiations.
 
 <b>EXAMPLE</b>
-       Here's a basic example for using LDAP to look up  <a href="local.8.html">local(8)</a>
+       Here's  a basic example for using LDAP to look up <a href="local.8.html">local(8)</a>
        aliases.  Assume that in main.cf, you have:
            <a href="postconf.5.html#alias_maps">alias_maps</a> = hash:/etc/aliases,
                <a href="ldap_table.5.html">ldap</a>:/etc/postfix/ldap-aliases.cf
@@ -422,14 +422,14 @@ LDAP_TABLE(5)                                       LDAP_TABLE(5)
            server_host = ldap.my.com
            search_base = dc=my, dc=com
 
-       Upon  receiving  mail  for a local address "ldapuser" that
-       isn't found in the  /etc/aliases  database,  Postfix  will
-       search   the   LDAP   server  listening  at  port  389  on
-       ldap.my.com.  It will bind  anonymously,  search  for  any
-       directory  entries  whose mailacceptinggeneralid attribute
-       is "ldapuser", read the  "maildrop"  attributes  of  those
-       found,  and build a list of their maildrops, which will be
-       treated as <a href="http://www.faqs.org/rfcs/rfc822.html">RFC822</a> addresses to which the message  will  be
+       Upon receiving mail for a local  address  "ldapuser"  that
+       isn't  found  in  the  /etc/aliases database, Postfix will
+       search  the  LDAP  server  listening  at   port   389   on
+       ldap.my.com.   It  will  bind  anonymously, search for any
+       directory entries whose  mailacceptinggeneralid  attribute
+       is  "ldapuser",  read  the  "maildrop" attributes of those
+       found, and build a list of their maildrops, which will  be
+       treated  as  <a href="http://www.faqs.org/rfcs/rfc822.html">RFC822</a> addresses to which the message will be
        delivered.
 
 <b>SEE ALSO</b>
@@ -443,13 +443,13 @@ LDAP_TABLE(5)                                       LDAP_TABLE(5)
        <a href="LDAP_README.html">LDAP_README</a>, Postfix LDAP client guide
 
 <b>LICENSE</b>
-       The Secure Mailer license must be  distributed  with  this
+       The  Secure  Mailer  license must be distributed with this
        software.
 
 <b>AUTHOR(S)</b>
-       Carsten  Hoeger,  Hery  Rakotoarisoa,  John Hensley, Keith
-       Stevenson, LaMont Jones, Liviu Daia, Manuel Guesdon,  Mike
-       Mattice,  Prabhat K Singh, Sami Haahtinen, Samuel Tardieu,
+       Carsten Hoeger, Hery  Rakotoarisoa,  John  Hensley,  Keith
+       Stevenson,  LaMont Jones, Liviu Daia, Manuel Guesdon, Mike
+       Mattice, Prabhat K Singh, Sami Haahtinen, Samuel  Tardieu,
        Victor Duchovni, and many others.
 
                                                     LDAP_TABLE(5)
index 6bf9ef68717a8c8e322d50995a373d6cbcd33eb7..4b9ca85b69001202e2c24dbd4220896c2a108a8e 100644 (file)
@@ -48,11 +48,18 @@ PIPE(8)                                                   PIPE(8)
        file at the end of a service definition.  The syntax is as
        follows:
 
+       <b>directory=</b><i>pathname</i> (optional, default: <b>$<a href="postconf.5.html#queue_directory">queue_directory</a></b>)
+              Change to the named directory before executing  the
+              external  command.  Delivery is deferred in case of
+              failure.
+
+              This feature is available as of Postfix 2.2.
+
        <b>eol=string</b> (optional, default: <b>\n</b>)
               The output record delimiter.  Typically  one  would
               use  either <b>\r\n</b> or <b>\n</b>. The usual C-style backslash
               escape sequences are recognized: <b>\a \b \f \n \r  \t</b>
-              <b>\v \</b><i>octal</i> and <b>\\</b>.
+              <b>\v \</b><i>ddd</i> (up to three octal digits) and <b>\\</b>.
 
        <b>flags=BDFORhqu.</b>&gt; (optional)
               Optional  message  processing  flags. By default, a
@@ -111,10 +118,10 @@ PIPE(8)                                                   PIPE(8)
                      most <b>@</b> character) to lower  case.   This  is
                      recommended for delivery via <b>UUCP</b>.
 
-              <b>.</b>      Prepend  <b>.</b>  to lines starting with "<b>.</b>". This
+              <b>.</b>      Prepend "<b>.</b>" to lines starting with "<b>.</b>". This
                      is needed by, for example, <b>BSMTP</b> software.
 
-              &gt;      Prepend &gt; to lines starting  with  "<b>From</b>  ".
+              &gt;      Prepend "&gt;" to lines starting with "<b>From</b>  ".
                      This is expected by, for example, <b>UUCP</b> soft-
                      ware.
 
@@ -216,13 +223,6 @@ PIPE(8)                                                   PIPE(8)
        $(<i>name</i>) are also recognized.  Specify <b>$$</b> where a single  <b>$</b>
        is wanted.
 
-       Available in Postfix 2.2 and later:
-
-       <b>directory=</b><i>pathname</i> (optional)
-              Change  to the specified directory before executing
-              the command.  Failure causes mail  delivery  to  be
-              deferred.
-
 <b>DIAGNOSTICS</b>
        Command  exit status codes are expected to follow the con-
        ventions defined in &lt;<b>sysexits.h</b>&gt;.
index 3edff6d40a10927bbd830a314b0dd14dbef22662..c0cde5e6152be1643b623930753235992ae2a5d7 100644 (file)
@@ -921,10 +921,10 @@ This feature is available in Postfix 2.0 and later.
 <DT><b><a name="bounce_size_limit">bounce_size_limit</a>
 (default: 50000)</b></DT><DD>
 
-<p>
-The maximal amount of original message text that is sent in a
-non-delivery notification. Specify a byte count.
-</p>
+<p> The maximal amount of original message text that is sent in a
+non-delivery notification. Specify a byte count. If you increase
+this limit, then you should increase the <a href="postconf.5.html#mime_nesting_limit">mime_nesting_limit</a> value
+proportionally.  </p>
 
 
 </DD>
@@ -1184,8 +1184,8 @@ address patterns that cause the verbose logging level to increase
 by the amount specified in $<a href="postconf.5.html#debug_peer_level">debug_peer_level</a>.  </p>
 
 <p> Specify domain names, network/netmask patterns, "/file/name"
-patterns or "<a href="DATABASE_README.html">type:table</a>" lookup tables. The result from lookup
-tables is ignored.  </p>
+patterns or "<a href="DATABASE_README.html">type:table</a>" lookup tables. The right-hand side result
+from "<a href="DATABASE_README.html">type:table</a>" lookups is ignored.  </p>
 
 <p> Pattern matching of domain names is controlled by the
 <a href="postconf.5.html#parent_domain_matches_subdomains">parent_domain_matches_subdomains</a> parameter.  </p>
@@ -2725,13 +2725,12 @@ The default time unit is s (seconds).
 </DD>
 
 <DT><b><a name="lmtp_rset_timeout">lmtp_rset_timeout</a>
-(default: 120s)</b></DT><DD>
+(default: 20s)</b></DT><DD>
 
-<p>
-The LMTP client time limit for sending the RSET command, and for
-receiving the server response. The LMTP client sends RSET in order
-to find out if a cached connection is still alive.
-</p>
+<p> The LMTP client time limit for sending the RSET command, and
+for receiving the server response. The LMTP client sends RSET in
+order to finish a recipient address probe, or to verify that a
+cached connection is still alive.  </p>
 
 <p>
 Time units: s (seconds), m (minutes), h (hours), d (days), w (weeks).
@@ -3617,8 +3616,8 @@ This feature is available in Postfix 2.0 and later.
 (default: 100)</b></DT><DD>
 
 <p>
-The maximal nesting level of multipart mail that the MIME processor
-will handle. Postfix refuses mail that is nested deeper.
+The maximal recursion level that the MIME processor will handle.
+Postfix refuses mail that is nested deeper than the specified limit.
 </p>
 
 <p>
@@ -5030,6 +5029,27 @@ The default time unit is s (seconds).
 </p>
 
 
+</DD>
+
+<DT><b><a name="session_cache_service">session_cache_service</a>
+(default: scache)</b></DT><DD>
+
+<p> The name of the <a href="scache.8.html">scache(8)</a> session cache service.  This service
+maintains a limited pool of cached sessions.  </p>
+
+
+</DD>
+
+<DT><b><a name="session_cache_ttl_limit">session_cache_ttl_limit</a>
+(default: 2s)</b></DT><DD>
+
+<p> The maximal time-to-live value that the session cache server
+allows. Requests that specify a larger TTL will be stored with the
+maximum allowed TTL. The purpose of this additional control is to
+protect the infrastructure against careless people. The cache TTL
+is already bounded by $<a href="postconf.5.html#max_idle">max_idle</a>.  </p>
+
+
 </DD>
 
 <DT><b><a name="setgid_group">setgid_group</a>
@@ -5134,6 +5154,62 @@ The default time unit is s (seconds).
 </p>
 
 
+</DD>
+
+<DT><b><a name="smtp_connection_cache_domains">smtp_connection_cache_domains</a>
+(default: empty)</b></DT><DD>
+
+<p> The SMTP destinations for which SMTP connection caching is
+enabled.  With SMTP connection caching, a connection is not closed
+immediately after completion of a mail transaction.  Instead, the
+connection is kept open for up to $<a href="postconf.5.html#smtp_connection_cache_time_limit">smtp_connection_cache_time_limit</a>
+seconds.  This allows connections to be reused for other deliveries,
+and can improve mail delivery performance. </p>
+
+<p> Specify a comma or white space separated list of destinations
+or pseudo-destinations:
+</p>
+
+<ul>
+
+<li> a domain name (the right-hand side of an email address),
+
+<li> a relay host (including optional [] and/or non-default TCP
+port), using the exact same spelling as in main.cf or in the
+transport map,
+
+<li> a /file/name with domains and/or relay hosts,
+
+<li> a "<a href="DATABASE_README.html">type:table</a>" with domains and/or relay hosts on the left-hand
+side.  The right-hand side result from "<a href="DATABASE_README.html">type:table</a>" lookups is
+ignored.
+
+</ul>
+
+<p></p>
+
+
+</DD>
+
+<DT><b><a name="smtp_connection_cache_reuse_limit">smtp_connection_cache_reuse_limit</a>
+(default: 10)</b></DT><DD>
+
+<p> When SMTP session caching is enabled, the number of times that
+an SMTP session is reused before it is closed.
+</p>
+
+
+</DD>
+
+<DT><b><a name="smtp_connection_cache_time_limit">smtp_connection_cache_time_limit</a>
+(default: 2s)</b></DT><DD>
+
+<p> When SMTP session caching is enabled, the amount of time that
+an unused SMTP client socket is kept open before it is closed.  Do
+not specify larger values without permission from the remote sites.
+</p>
+
+
 </DD>
 
 <DT><b><a name="smtp_data_done_timeout">smtp_data_done_timeout</a>
@@ -5510,10 +5586,12 @@ The default time unit is s (seconds).
 </DD>
 
 <DT><b><a name="smtp_rset_timeout">smtp_rset_timeout</a>
-(default: 120s)</b></DT><DD>
+(default: 20s)</b></DT><DD>
 
 <p> The SMTP client time limit for sending the RSET command, and
-for receiving the server response.  </p>
+for receiving the server response. The SMTP client sends RSET in
+order to finish a recipient address probe, or to verify that a
+cached session is still usable.  </p>
 
 <p> This feature is available in Postfix 2.1 and later.  </p>
 
diff --git a/postfix/html/scache.8.html b/postfix/html/scache.8.html
new file mode 100644 (file)
index 0000000..ac46f7c
--- /dev/null
@@ -0,0 +1,156 @@
+<!doctype html public "-//W3C//DTD HTML 4.01 Transitional//EN"
+        "http://www.w3.org/TR/html4/loose.dtd">
+<html> <head>
+<meta http-equiv="Content-Type" content="text/html; charset=us-ascii">
+<title> Postfix manual - scache(8) </title>
+</head> <body> <pre>
+SCACHE(8)                                               SCACHE(8)
+
+<b>NAME</b>
+       scache - Postfix session cache server
+
+<b>SYNOPSIS</b>
+       <b>scache</b> [generic Postfix daemon options]
+
+<b>DESCRIPTION</b>
+       The  scache  server  maintains  the Postfix session cache.
+       This information can be used by, for example, the  Postfix
+       SMTP client.
+
+       The  session  cache  is organized into logical destination
+       names, physical endpoint names, and sessions.
+
+       As a specific example, logical SMTP  destinations  specify
+       (transport,  domain,  port),  and  physical SMTP endpoints
+       specify (transport, IP address, port).   An  SMTP  session
+       may be saved after a successful mail transaction.
+
+       In  the general case, one logical destination may refer to
+       zero or more physical endpoints, one physical endpoint may
+       be  referenced  by  zero or more logical destinations, and
+       one endpoint may refer to zero or more sessions.
+
+       The exact syntax of a logical destination or endpoint name
+       is  application  dependent;  the  <b>scache</b>  service does not
+       care.  A session is stored as a file  descriptor  together
+       with  application-dependent  information that is needed to
+       re-activate a session object. Again, the <b>scache</b> service is
+       completely  unaware about the details of that information.
+
+       All information is stored  with  a  finite  time  to  live
+       (ttl).  The session cache daemon terminates when no client
+       is connected for <b><a href="postconf.5.html#max_idle">max_idle</a></b> time units.
+
+       This server implements the following requests:
+
+       <b>save_endp</b> <i>ttl endpoint endpoint</i><b>_</b><i>properties file</i><b>_</b><i>descriptor</i>
+              Save the  specified  file  descriptor  and  session
+              property  data  under  the specified endpoint name.
+              The endpoint properties are used by the  client  to
+              re-activate  a passivated session object.  queue ID
+              is queued for the specified destination.
+
+       <b>find_endp</b> <i>endpoint</i>
+              Look  up  cached  properties  and  a  cached   file
+              descriptor for the specified endpoint.
+
+       <b>save_dest</b> <i>ttl destination destination</i><b>_</b><i>properties endpoint</i>
+              Save  the binding between a logical destination and
+              an endpoint under the  destination  name,  together
+              with  destination  specific session properties. The
+              destination properties are used by  the  client  to
+              re-activate a passivated session object.
+
+       <b>find_dest</b> <i>destination</i>
+              Look  up cached destination properties, cached end-
+              point properties, and a cached file descriptor  for
+              the specified logical destination.
+
+<b>SECURITY</b>
+       The  session  cache  server  is not security-sensitive. It
+       does not talk to the network, and  it  does  not  talk  to
+       local  users.  The scache server can run chrooted at fixed
+       low privilege.
+
+       The session cache server is not a trusted process. It must
+       not  be  used to store information that is security sensi-
+       tive.
+
+<b>DIAGNOSTICS</b>
+       Problems and transactions are logged to <b>syslogd</b>(8).
+
+<b>BUGS</b>
+       Sessions cannot be cached across multiple machines.
+
+       When a session expires from the cache it is closed without
+       protocol specific handshake.
+
+<b>CONFIGURATION PARAMETERS</b>
+       Changes   to   <b>main.cf</b>  are  picked  up  automatically  as
+       <a href="scache.8.html">scache(8)</a> processes run for only a limited amount of time.
+       Use the command "<b>postfix reload</b>" to speed up a change.
+
+       The  text  below  provides  only  a parameter summary. See
+       <a href="postconf.5.html">postconf(5)</a> for more details including examples.
+
+<b>RESOURCE CONTROLS</b>
+       <b><a href="postconf.5.html#session_cache_ttl_limit">session_cache_ttl_limit</a> (2s)</b>
+              The maximal time-to-live  value  that  the  session
+              cache server allows.
+
+<b>MISCELLANEOUS CONTROLS</b>
+       <b><a href="postconf.5.html#config_directory">config_directory</a> (see 'postconf -d' output)</b>
+              The  default  location  of  the Postfix main.cf and
+              master.cf configuration files.
+
+       <b><a href="postconf.5.html#daemon_timeout">daemon_timeout</a> (18000s)</b>
+              How much time a Postfix daemon process may take  to
+              handle  a  request  before  it  is  terminated by a
+              built-in watchdog timer.
+
+       <b><a href="postconf.5.html#ipc_timeout">ipc_timeout</a> (3600s)</b>
+              The time limit for sending or receiving information
+              over an internal communication channel.
+
+       <b><a href="postconf.5.html#max_idle">max_idle</a> (100s)</b>
+              The  maximum  amount  of  time that an idle Postfix
+              daemon process waits for the next  service  request
+              before exiting.
+
+       <b><a href="postconf.5.html#process_id">process_id</a> (read-only)</b>
+              The  process ID of a Postfix command or daemon pro-
+              cess.
+
+       <b><a href="postconf.5.html#process_name">process_name</a> (read-only)</b>
+              The process name of a  Postfix  command  or  daemon
+              process.
+
+       <b><a href="postconf.5.html#syslog_facility">syslog_facility</a> (mail)</b>
+              The syslog facility of Postfix logging.
+
+       <b><a href="postconf.5.html#syslog_name">syslog_name</a> (postfix)</b>
+              The  mail system name that is prepended to the pro-
+              cess  name  in  syslog  records,  so  that  "smtpd"
+              becomes, for example, "postfix/smtpd".
+
+<b>SEE ALSO</b>
+       <a href="smtp.8.html">smtp(8)</a>, SMTP client
+       <a href="postconf.5.html">postconf(5)</a>, configuration parameters
+       <a href="master.8.html">master(8)</a>, process manager
+       syslogd(8), system logging
+
+<b>LICENSE</b>
+       The  Secure  Mailer  license must be distributed with this
+       software.
+
+<b>HISTORY</b>
+       This service was introduced with Postfix version 2.2.
+
+<b>AUTHOR(S)</b>
+       Wietse Venema
+       IBM T.J. Watson Research
+       P.O. Box 704
+       Yorktown Heights, NY 10598, USA
+
+                                                        SCACHE(8)
+</pre> </body> </html>
index e4657fa490191e21786125f281b799a1fd5e6164..4264f3b546f25ffa3946eacd7472a232de93a6ab 100644 (file)
@@ -34,9 +34,14 @@ SMTP(8)                                                   SMTP(8)
        fails  due  to  a  recoverable  error  condition, the SMTP
        client will try to deliver the mail to an alternate  host.
 
+       After  a  successful  mail  transaction,  a session may be
+       saved to the <a href="scache.8.html"><b>scache(8)</a></b> session cache server,  so  that  it
+       may  be  used by any SMTP client for a subsequent transac-
+       tion.  Session caching is disabled by default.
+
 <b>SECURITY</b>
        The SMTP client is moderately security-sensitive. It talks
-       to SMTP servers and to DNS servers  on  the  network.  The
+       to  SMTP  servers  and  to DNS servers on the network. The
        SMTP client can be run chrooted at fixed low privilege.
 
 <b>STANDARDS</b>
@@ -52,20 +57,29 @@ SMTP(8)                                                   SMTP(8)
        <a href="http://www.faqs.org/rfcs/rfc2920.html">RFC 2920</a> (SMTP Pipelining)
 
 <b>DIAGNOSTICS</b>
-       Problems  and transactions are logged to <b>syslogd</b>(8).  Cor-
-       rupted message files are marked so that the queue  manager
+       Problems and transactions are logged to <b>syslogd</b>(8).   Cor-
+       rupted  message files are marked so that the queue manager
        can move them to the <b>corrupt</b> queue for further inspection.
 
-       Depending on the setting of the <b><a href="postconf.5.html#notify_classes">notify_classes</a></b>  parameter,
-       the  postmaster is notified of bounces, protocol problems,
+       Depending  on the setting of the <b><a href="postconf.5.html#notify_classes">notify_classes</a></b> parameter,
+       the postmaster is notified of bounces, protocol  problems,
        and of other trouble.
 
+<b>BUGS</b>
+       SMTP session caching does not work with TLS. The necessary
+       support for object passivation and re-activation does  not
+       exist.
+
+       SMTP  session  caching  assumes  that SASL credentials are
+       valid for all destinations  that  map  onto  the  same  IP
+       address and TCP port.
+
 <b>CONFIGURATION PARAMETERS</b>
        Changes to <b>main.cf</b> are picked up automatically, as <a href="smtp.8.html">smtp(8)</a>
-       processes  run  for only a limited amount of time. Use the
+       processes run for only a limited amount of time.  Use  the
        command "<b>postfix reload</b>" to speed up a change.
 
-       The text below provides  only  a  parameter  summary.  See
+       The  text  below  provides  only  a parameter summary. See
        <a href="postconf.5.html">postconf(5)</a> for more details including examples.
 
 <b>COMPATIBILITY CONTROLS</b>
@@ -79,7 +93,7 @@ SMTP(8)                                                   SMTP(8)
               Never send EHLO at the start of an SMTP session.
 
        <b><a href="postconf.5.html#smtp_defer_if_no_mx_address_found">smtp_defer_if_no_mx_address_found</a> (no)</b>
-              Defer  mail  delivery when no MX record resolves to
+              Defer mail delivery when no MX record  resolves  to
               an IP address.
 
        <b><a href="postconf.5.html#smtp_line_length_limit">smtp_line_length_limit</a> (990)</b>
@@ -87,17 +101,17 @@ SMTP(8)                                                   SMTP(8)
               that Postfix will send via SMTP.
 
        <b><a href="postconf.5.html#smtp_pix_workaround_delay_time">smtp_pix_workaround_delay_time</a> (10s)</b>
-              How  long  the  Postfix  SMTP  client pauses before
+              How long the  Postfix  SMTP  client  pauses  before
               sending ".&lt;CR&gt;&lt;LF&gt;" in order to work around the PIX
               firewall "&lt;CR&gt;&lt;LF&gt;.&lt;CR&gt;&lt;LF&gt;" bug.
 
        <b><a href="postconf.5.html#smtp_pix_workaround_threshold_time">smtp_pix_workaround_threshold_time</a> (500s)</b>
-              How  long  a  message must be queued before the PIX
-              firewall  "&lt;CR&gt;&lt;LF&gt;.&lt;CR&gt;&lt;LF&gt;"  bug  workaround   is
+              How long a message must be queued  before  the  PIX
+              firewall   "&lt;CR&gt;&lt;LF&gt;.&lt;CR&gt;&lt;LF&gt;"  bug  workaround  is
               turned on.
 
        <b><a href="postconf.5.html#smtp_quote_rfc821_envelope">smtp_quote_rfc821_envelope</a> (yes)</b>
-              Quote  addresses in SMTP MAIL FROM and RCPT TO com-
+              Quote addresses in SMTP MAIL FROM and RCPT TO  com-
               mands as required by <a href="http://www.faqs.org/rfcs/rfc821.html">RFC 821</a>.
 
        <b><a href="postconf.5.html#smtp_skip_5xx_greeting">smtp_skip_5xx_greeting</a> (yes)</b>
@@ -105,7 +119,7 @@ SMTP(8)                                                   SMTP(8)
               (go away, do not try again later).
 
        <b><a href="postconf.5.html#smtp_skip_quit_response">smtp_skip_quit_response</a> (yes)</b>
-              Do  not wait for the response to the SMTP QUIT com-
+              Do not wait for the response to the SMTP QUIT  com-
               mand.
 
        Available in Postfix version 2.0 and earlier:
@@ -118,7 +132,7 @@ SMTP(8)                                                   SMTP(8)
        Available in Postfix version 2.0 and later:
 
        <b><a href="postconf.5.html#disable_mime_output_conversion">disable_mime_output_conversion</a> (no)</b>
-              Disable  the  conversion of 8BITMIME format to 7BIT
+              Disable the conversion of 8BITMIME format  to  7BIT
               format.
 
        <b><a href="postconf.5.html#mime_boundary_length_limit">mime_boundary_length_limit</a> (2048)</b>
@@ -126,50 +140,50 @@ SMTP(8)                                                   SMTP(8)
               strings.
 
        <b><a href="postconf.5.html#mime_nesting_limit">mime_nesting_limit</a> (100)</b>
-              The  maximal  nesting  level of multipart mail that
-              the MIME processor will handle.
+              The maximal recursion level that the MIME processor
+              will handle.
 
 <b>EXTERNAL CONTENT INSPECTION CONTROLS</b>
        Available in Postfix version 2.1 and later:
 
        <b><a href="postconf.5.html#smtp_send_xforward_command">smtp_send_xforward_command</a> (no)</b>
-              Send the non-standard  XFORWARD  command  when  the
-              Postfix  SMTP  server EHLO response announces XFOR-
+              Send  the  non-standard  XFORWARD  command when the
+              Postfix SMTP server EHLO response  announces  XFOR-
               WARD support.
 
 <b>SASL AUTHENTICATION CONTROLS</b>
        <b><a href="postconf.5.html#smtp_sasl_auth_enable">smtp_sasl_auth_enable</a> (no)</b>
-              Enable SASL  authentication  in  the  Postfix  SMTP
+              Enable  SASL  authentication  in  the  Postfix SMTP
               client.
 
        <b><a href="postconf.5.html#smtp_sasl_password_maps">smtp_sasl_password_maps</a> (empty)</b>
-              Optional  SMTP  client lookup tables with one user-
-              name:password entry per remote hostname or  domain.
+              Optional SMTP client lookup tables with  one  user-
+              name:password  entry per remote hostname or domain.
 
        <b><a href="postconf.5.html#smtp_sasl_security_options">smtp_sasl_security_options</a> (noplaintext, noanonymous)</b>
-              What  authentication  mechanisms  the  Postfix SMTP
+              What authentication  mechanisms  the  Postfix  SMTP
               client is allowed to use.
 
 <b>RESOURCE AND RATE CONTROLS</b>
        <b><a href="postconf.5.html#smtp_destination_concurrency_limit">smtp_destination_concurrency_limit</a>      ($<a href="postconf.5.html#default_destination_concurrency_limit">default_destina</a>-</b>
        <b><a href="postconf.5.html#default_destination_concurrency_limit">tion_concurrency_limit</a>)</b>
-              The maximal number of parallel  deliveries  to  the
-              same  destination  via  the  smtp  message delivery
+              The  maximal  number  of parallel deliveries to the
+              same destination  via  the  smtp  message  delivery
               transport.
 
        <b><a href="postconf.5.html#smtp_destination_recipient_limit">smtp_destination_recipient_limit</a>        ($<a href="postconf.5.html#default_destination_recipient_limit">default_destina</a>-</b>
        <b><a href="postconf.5.html#default_destination_recipient_limit">tion_recipient_limit</a>)</b>
-              The maximal number of recipients per  delivery  via
+              The  maximal  number of recipients per delivery via
               the smtp message delivery transport.
 
        <b><a href="postconf.5.html#smtp_connect_timeout">smtp_connect_timeout</a> (30s)</b>
-              The  SMTP  client  time  limit for completing a TCP
+              The SMTP client time limit  for  completing  a  TCP
               connection,  or  zero  (use  the  operating  system
               built-in time limit).
 
        <b><a href="postconf.5.html#smtp_helo_timeout">smtp_helo_timeout</a> (300s)</b>
-              The  SMTP client time limit for sending the HELO or
-              EHLO command, and for receiving the initial  server
+              The SMTP client time limit for sending the HELO  or
+              EHLO  command, and for receiving the initial server
               response.
 
        <b><a href="postconf.5.html#smtp_xforward_timeout">smtp_xforward_timeout</a> (300s)</b>
@@ -177,30 +191,30 @@ SMTP(8)                                                   SMTP(8)
               command, and for receiving the server response.
 
        <b><a href="postconf.5.html#smtp_mail_timeout">smtp_mail_timeout</a> (300s)</b>
-              The SMTP client time limit  for  sending  the  MAIL
-              FROM   command,   and   for  receiving  the  server
+              The  SMTP  client  time  limit for sending the MAIL
+              FROM  command,  and  for   receiving   the   server
               response.
 
        <b><a href="postconf.5.html#smtp_rcpt_timeout">smtp_rcpt_timeout</a> (300s)</b>
-              The SMTP client time limit  for  sending  the  SMTP
-              RCPT  TO  command,  and  for  receiving  the server
+              The  SMTP  client  time  limit for sending the SMTP
+              RCPT TO  command,  and  for  receiving  the  server
               response.
 
        <b><a href="postconf.5.html#smtp_data_init_timeout">smtp_data_init_timeout</a> (120s)</b>
-              The SMTP client time limit  for  sending  the  SMTP
-              DATA   command,   and   for  receiving  the  server
+              The  SMTP  client  time  limit for sending the SMTP
+              DATA  command,  and  for   receiving   the   server
               response.
 
        <b><a href="postconf.5.html#smtp_data_xfer_timeout">smtp_data_xfer_timeout</a> (180s)</b>
-              The SMTP client time limit  for  sending  the  SMTP
+              The  SMTP  client  time  limit for sending the SMTP
               message content.
 
        <b><a href="postconf.5.html#smtp_data_done_timeout">smtp_data_done_timeout</a> (600s)</b>
-              The  SMTP  client  time  limit for sending the SMTP
+              The SMTP client time limit  for  sending  the  SMTP
               ".", and for receiving the server response.
 
        <b><a href="postconf.5.html#smtp_quit_timeout">smtp_quit_timeout</a> (300s)</b>
-              The SMTP client time limit  for  sending  the  QUIT
+              The  SMTP  client  time  limit for sending the QUIT
               command, and for receiving the server response.
 
        Available in Postfix version 2.1 and later:
@@ -211,14 +225,30 @@ SMTP(8)                                                   SMTP(8)
               lookups, or zero (no limit).
 
        <b><a href="postconf.5.html#smtp_mx_session_limit">smtp_mx_session_limit</a> (2)</b>
-              The  maximal  number  of SMTP sessions per delivery
-              request before giving up or delivering to  a  fall-
+              The maximal number of SMTP  sessions  per  delivery
+              request  before  giving up or delivering to a fall-
               back relay host, or zero (no limit).
 
-       <b><a href="postconf.5.html#smtp_rset_timeout">smtp_rset_timeout</a> (120s)</b>
-              The  SMTP  client  time  limit for sending the RSET
+       <b><a href="postconf.5.html#smtp_rset_timeout">smtp_rset_timeout</a> (20s)</b>
+              The SMTP client time limit  for  sending  the  RSET
               command, and for receiving the server response.
 
+       Available in Postfix version 2.2 and later:
+
+       <b><a href="postconf.5.html#smtp_connection_cache_domains">smtp_connection_cache_domains</a> (empty)</b>
+              The  SMTP  destinations  for  which SMTP connection
+              caching is enabled.
+
+       <b><a href="postconf.5.html#smtp_connection_cache_reuse_limit">smtp_connection_cache_reuse_limit</a> (10)</b>
+              When SMTP session caching is enabled, the number of
+              times  that  an SMTP session is reused before it is
+              closed.
+
+       <b><a href="postconf.5.html#smtp_connection_cache_time_limit">smtp_connection_cache_time_limit</a> (2s)</b>
+              When SMTP session caching is enabled, the amount of
+              time that an unused SMTP client socket is kept open
+              before it is closed.
+
 <b>TROUBLE SHOOTING CONTROLS</b>
        <b><a href="postconf.5.html#debug_peer_level">debug_peer_level</a> (2)</b>
               The increment  in  verbose  logging  level  when  a
@@ -320,6 +350,7 @@ SMTP(8)                                                   SMTP(8)
 <b>SEE ALSO</b>
        <a href="qmgr.8.html">qmgr(8)</a>, queue manager
        <a href="bounce.8.html">bounce(8)</a>, delivery status reports
+       <a href="scache.8.html">scache(8)</a>, session cache server
        <a href="postconf.5.html">postconf(5)</a>, configuration parameters
        <a href="master.8.html">master(8)</a>, process manager
        syslogd(8), system logging
@@ -337,5 +368,17 @@ SMTP(8)                                                   SMTP(8)
        P.O. Box 704
        Yorktown Heights, NY 10598, USA
 
+       Command pipelining in cooperation with:
+       Jon Ribbens
+       Oaktree Internet Solutions Ltd.,
+       Internet House,
+       Canal Basin,
+       Coventry,
+       CV1 4LY, United Kingdom.
+
+       Connection caching in cooperation with:
+       Victor Duchovni
+       Morgan Stanley
+
                                                           SMTP(8)
 </pre> </body> </html>
index 5010210c86e282c22dc6e6a893c2954aac29c7e4..9f3a3f683ae00ed5d860dd808b30487573a9c81e 100644 (file)
@@ -6,7 +6,8 @@ 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/oqmgr.8 man8/spawn.8 man8/flush.8 man8/virtual.8 man8/qmqpd.8 \
-       man8/verify.8 man8/trace.8 man8/proxymap.8 man8/anvil.8
+       man8/verify.8 man8/trace.8 man8/proxymap.8 man8/anvil.8 \
+       man8/scache.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,6 +51,11 @@ man8/anvil.8: ../src/anvil/anvil.c
            (cmp -s junk $? || mv junk $?)
        ../mantools/srctoman $? >$@
 
+man8/scache.8: ../src/scache/scache.c
+       ../mantools/fixman ../proto/postconf.proto $? >junk && \
+           (cmp -s junk $? || mv junk $?)
+       ../mantools/srctoman $? >$@
+
 man8/error.8: ../src/error/error.c
        ../mantools/fixman ../proto/postconf.proto $? >junk && \
            (cmp -s junk $? || mv junk $?)
index bc4824eb26baec04d671b183d458d2d1dc173a04..bb7e97f3fce5ce68ef844a14b8e0ef9d42414892 100644 (file)
@@ -150,13 +150,13 @@ metacharacters.
 .IP "\fB\fB%u\fR\fR"
 When the input key is an address of the form user@domain,
 \fB%u\fR is replaced by the (RFC 2254) quoted local part of the
-address. If no domain is specified, \fB%u\fR is replaced by the
-entire search string.
+address. Otherwise, \fB%u\fR is replaced by the entire
+search string.
 .IP "\fB\fB%d\fR\fR"
 When the input key is an address of the form user@domain,
 \fB%d\fR is replaced by the (RFC 2254) quoted domain part of the
-address. When the input key has no domain qualifier, \fB%d\fR is
-replaced by the entire search string.
+address. Otherwise, \fB%d\fR is replaced by the entire
+search string.
 .RE
 .IP
 The "domain" parameter described below limits the input
@@ -175,15 +175,15 @@ following '%' expansions:
 .IP "\fB\fB%s\fR\fR"
 This is replaced by the value of the result attribute.
 .IP "\fB%u\fR
-When the result attribute is an address of the form
-user@domain, \fB%u\fR is replaced local part of the address, if
-the result attribute is unqualified, \fB%u\fR is replaced by the
-entire attribute value.
+When the result attribute value is an address of the form
+user@domain, \fB%u\fR is replaced by the local part of the
+address.  Otherwise, \fB%u\fR is replaced by the entire
+attribute value.
 .IP "\fB\fB%d\fR\fR"
-When a result attribute is an address of the form user@domain,
-\fB%d\fR is replaced by the domain part of the attribute value.
-If an attribute value is unqualified \fB%d\fR is replaced by the
-entire attribute value.
+When a result attribute value is an address of the form
+user@domain, \fB%d\fR is replaced by the domain part of
+the attribute value.  Otherwise, \fB%d\fR is replaced by
+the entire attribute value.
 .RE
 .IP
 For example, using "result_filter = smtp:[%s]" allows one
index c473165cd918110bf5694fe8195578ab45478358..cac7ae0728b30ae9eae87c89b4db56304c32eaf6 100644 (file)
@@ -472,7 +472,9 @@ of failed delivery attempts and generates non-delivery notifications.
 This feature is available in Postfix 2.0 and later.
 .SH bounce_size_limit (default: 50000)
 The maximal amount of original message text that is sent in a
-non-delivery notification. Specify a byte count.
+non-delivery notification. Specify a byte count. If you increase
+this limit, then you should increase the mime_nesting_limit value
+proportionally.
 .SH broken_sasl_auth_clients (default: no)
 Enable inter-operability with SMTP clients that implement an obsolete
 version of the AUTH command (RFC 2554). Examples of such clients
@@ -598,8 +600,8 @@ address patterns that cause the verbose logging level to increase
 by the amount specified in $debug_peer_level.
 .PP
 Specify domain names, network/netmask patterns, "/file/name"
-patterns or "type:table" lookup tables. The result from lookup
-tables is ignored.
+patterns or "type:table" lookup tables. The right-hand side result
+from "type:table" lookups is ignored.
 .PP
 Pattern matching of domain names is controlled by the
 parent_domain_matches_subdomains parameter.
@@ -1353,10 +1355,11 @@ for receiving the server response.
 .PP
 Time units: s (seconds), m (minutes), h (hours), d (days), w (weeks).
 The default time unit is s (seconds).
-.SH lmtp_rset_timeout (default: 120s)
-The LMTP client time limit for sending the RSET command, and for
-receiving the server response. The LMTP client sends RSET in order
-to find out if a cached connection is still alive.
+.SH lmtp_rset_timeout (default: 20s)
+The LMTP client time limit for sending the RSET command, and
+for receiving the server response. The LMTP client sends RSET in
+order to finish a recipient address probe, or to verify that a
+cached connection is still alive.
 .PP
 Time units: s (seconds), m (minutes), h (hours), d (days), w (weeks).
 The default time unit is s (seconds).
@@ -1825,8 +1828,8 @@ message headers, as described in the header_checks(5) manual page.
 .PP
 This feature is available in Postfix 2.0 and later.
 .SH mime_nesting_limit (default: 100)
-The maximal nesting level of multipart mail that the MIME processor
-will handle. Postfix refuses mail that is nested deeper.
+The maximal recursion level that the MIME processor will handle.
+Postfix refuses mail that is nested deeper than the specified limit.
 .PP
 This feature is available in Postfix 2.0 and later.
 .SH minimal_backoff_time (default: 1000s)
@@ -2633,6 +2636,15 @@ appears to be malfunctioning.
 .PP
 Time units: s (seconds), m (minutes), h (hours), d (days), w (weeks).
 The default time unit is s (seconds).
+.SH session_cache_service (default: scache)
+The name of the scache(8) session cache service.  This service
+maintains a limited pool of cached sessions.
+.SH session_cache_ttl_limit (default: 2s)
+The maximal time-to-live value that the session cache server
+allows. Requests that specify a larger TTL will be stored with the
+maximum allowed TTL. The purpose of this additional control is to
+protect the infrastructure against careless people. The cache TTL
+is already bounded by $max_idle.
 .SH setgid_group (default: postdrop)
 The group ownership of set-gid Postfix commands and of group-writable
 Postfix directories. When this parameter value is changed you need
@@ -2681,6 +2693,36 @@ the operating system).
 .PP
 Time units: s (seconds), m (minutes), h (hours), d (days), w (weeks).
 The default time unit is s (seconds).
+.SH smtp_connection_cache_domains (default: empty)
+The SMTP destinations for which SMTP connection caching is
+enabled.  With SMTP connection caching, a connection is not closed
+immediately after completion of a mail transaction.  Instead, the
+connection is kept open for up to $smtp_connection_cache_time_limit
+seconds.  This allows connections to be reused for other deliveries,
+and can improve mail delivery performance.
+.PP
+Specify a comma or white space separated list of destinations
+or pseudo-destinations:
+.IP \(bu
+a domain name (the right-hand side of an email address),
+.IP \(bu
+a relay host (including optional [] and/or non-default TCP
+port), using the exact same spelling as in main.cf or in the
+transport map,
+.IP \(bu
+a /file/name with domains and/or relay hosts,
+.IP \(bu
+a "type:table" with domains and/or relay hosts on the left-hand
+side.  The right-hand side result from "type:table" lookups is
+ignored.
+.PP
+.SH smtp_connection_cache_reuse_limit (default: 10)
+When SMTP session caching is enabled, the number of times that
+an SMTP session is reused before it is closed.
+.SH smtp_connection_cache_time_limit (default: 2s)
+When SMTP session caching is enabled, the amount of time that
+an unused SMTP client socket is kept open before it is closed.  Do
+not specify larger values without permission from the remote sites.
 .SH smtp_data_done_timeout (default: 600s)
 The SMTP client time limit for sending the SMTP ".", and for receiving
 the server response.
@@ -2852,9 +2894,11 @@ for receiving the server response.
 .PP
 Time units: s (seconds), m (minutes), h (hours), d (days), w (weeks).
 The default time unit is s (seconds).
-.SH smtp_rset_timeout (default: 120s)
+.SH smtp_rset_timeout (default: 20s)
 The SMTP client time limit for sending the RSET command, and
-for receiving the server response.
+for receiving the server response. The SMTP client sends RSET in
+order to finish a recipient address probe, or to verify that a
+cached session is still usable.
 .PP
 This feature is available in Postfix 2.1 and later.
 .SH smtp_sasl_auth_enable (default: no)
index 22c64cf308e660f47b88ca3169aa45b3626fbb37..0e91dcb67b0ffa0ffe286b8f2f125b6b547014c7 100644 (file)
@@ -124,8 +124,7 @@ Turn off MIME processing while receiving mail.
 .IP "\fBmime_boundary_length_limit (2048)\fR"
 The maximal length of MIME multipart boundary strings.
 .IP "\fBmime_nesting_limit (100)\fR"
-The maximal nesting level of multipart mail that the MIME processor
-will handle.
+The maximal recursion level that the MIME processor will handle.
 .IP "\fBstrict_8bitmime (no)\fR"
 Enable both strict_7bit_headers and strict_8bitmime_body.
 .IP "\fBstrict_7bit_headers (no)\fR"
@@ -222,8 +221,7 @@ message header.
 .IP "\fBmime_boundary_length_limit (2048)\fR"
 The maximal length of MIME multipart boundary strings.
 .IP "\fBmime_nesting_limit (100)\fR"
-The maximal nesting level of multipart mail that the MIME processor
-will handle.
+The maximal recursion level that the MIME processor will handle.
 .IP "\fBqueue_file_attribute_count_limit (100)\fR"
 The maximal number of (name=value) attributes that may be stored
 in a Postfix queue file.
index 8dc1369df0751bda55359eb5839bdd5baa45f25f..d450e3774968db5daad6a6be615e6f1e180be256 100644 (file)
@@ -51,11 +51,16 @@ entry for the pipe-based delivery transport.
 .fi
 The external command attributes are given in the \fBmaster.cf\fR
 file at the end of a service definition.  The syntax is as follows:
+.IP "\fBdirectory=\fIpathname\fR (optional, default: \fB$queue_directory\fR)"
+Change to the named directory before executing the external command.
+Delivery is deferred in case of failure.
+.sp
+This feature is available as of Postfix 2.2.
 .IP "\fBeol=string\fR (optional, default: \fB\en\fR)"
 The output record delimiter. Typically one would use either
 \fB\er\en\fR or \fB\en\fR. The usual C-style backslash escape
 sequences are recognized: \fB\ea \eb \ef \en \er \et \ev
-\e\fIoctal\fR and \fB\e\e\fR.
+\e\fIddd\fR (up to three octal digits) and \fB\e\e\fR.
 .IP "\fBflags=BDFORhqu.>\fR (optional)"
 Optional message processing flags. By default, a message is
 copied unchanged.
@@ -105,10 +110,10 @@ Fold the command-line \fB$recipient\fR address localpart (text to
 the left of the right-most \fB@\fR character) to lower case.
 This is recommended for delivery via \fBUUCP\fR.
 .IP \fB.\fR
-Prepend \fB.\fR to lines starting with "\fB.\fR". This is needed
+Prepend "\fB.\fR" to lines starting with "\fB.\fR". This is needed
 by, for example, \fBBSMTP\fR software.
 .IP \fB>\fR
-Prepend \fB>\fR to lines starting with "\fBFrom \fR". This is expected
+Prepend "\fB>\fR" to lines starting with "\fBFrom \fR". This is expected
 by, for example, \fBUUCP\fR software.
 .RE
 .IP "\fBsize\fR=\fIsize_limit\fR (optional)"
@@ -183,11 +188,6 @@ This information is modified by the \fBu\fR flag for case folding.
 In addition to the form ${\fIname\fR}, the forms $\fIname\fR and
 $(\fIname\fR) are also recognized.  Specify \fB$$\fR where a single
 \fB$\fR is wanted.
-.PP
-Available in Postfix 2.2 and later:
-.IP "\fBdirectory=\fIpathname\fR (optional)"
-Change to the specified directory before executing the command.
-Failure causes mail delivery to be deferred.
 .SH DIAGNOSTICS
 .ad
 .fi
diff --git a/postfix/man/man8/scache.8 b/postfix/man/man8/scache.8
new file mode 100644 (file)
index 0000000..283283c
--- /dev/null
@@ -0,0 +1,152 @@
+.TH SCACHE 8 
+.ad
+.fi
+.SH NAME
+scache
+\-
+Postfix session cache server
+.SH "SYNOPSIS"
+.na
+.nf
+\fBscache\fR [generic Postfix daemon options]
+.SH DESCRIPTION
+.ad
+.fi
+The scache server maintains the Postfix session cache. This
+information can be used by, for example, the Postfix SMTP client.
+
+The session cache is organized into logical destination
+names, physical endpoint names, and sessions.
+
+As a specific example, logical SMTP destinations specify
+(transport, domain, port), and physical SMTP endpoints
+specify (transport, IP address, port).  An SMTP session
+may be saved after a successful mail transaction.
+
+In the general case, one logical destination may refer to
+zero or more physical endpoints, one physical endpoint may
+be referenced by zero or more logical destinations, and
+one endpoint may refer to zero or more sessions.
+
+The exact syntax of a logical destination or endpoint name
+is application dependent; the \fBscache\fR service does
+not care.  A session is stored as a file descriptor together
+with application-dependent information that is needed to
+re-activate a session object. Again, the \fBscache\fR
+service is completely unaware about the details of that
+information.
+
+All information is stored with a finite time to live (ttl).
+The session cache daemon terminates when no client is
+connected for \fBmax_idle\fR time units.
+
+This server implements the following requests:
+.IP "\fBsave_endp\fI ttl endpoint endpoint_properties file_descriptor\fR"
+Save the specified file descriptor and session property data
+under the specified endpoint name. The endpoint properties
+are used by the client to re-activate a passivated session
+object.
+queue ID is queued for the specified destination.
+.IP "\fBfind_endp\fI endpoint\fR"
+Look up cached properties and a cached file descriptor for the
+specified endpoint.
+.IP "\fBsave_dest\fI ttl destination destination_properties endpoint\fR"
+Save the binding between a logical destination and an
+endpoint under the destination name, together with destination
+specific session properties. The destination properties
+are used by the client to re-activate a passivated session
+object.
+.IP "\fBfind_dest\fI destination\fR"
+Look up cached destination properties, cached endpoint properties,
+and a cached file descriptor for the specified logical destination.
+.SH "SECURITY"
+.na
+.nf
+.ad
+.fi
+The session cache server is not security-sensitive. It does not
+talk to the network, and it does not talk to local users.
+The scache server can run chrooted at fixed low privilege.
+
+The session cache server is not a trusted process. It must
+not be used to store information that is security sensitive.
+.SH DIAGNOSTICS
+.ad
+.fi
+Problems and transactions are logged to \fBsyslogd\fR(8).
+.SH BUGS
+.ad
+.fi
+Sessions cannot be cached across multiple machines.
+
+When a session expires from the cache it is closed without
+protocol specific handshake.
+.SH "CONFIGURATION PARAMETERS"
+.na
+.nf
+.ad
+.fi
+Changes to \fBmain.cf\fR are picked up automatically as scache(8)
+processes run for only a limited amount of time. Use the command
+"\fBpostfix reload\fR" to speed up a change.
+
+The text below provides only a parameter summary. See
+postconf(5) for more details including examples.
+.SH "RESOURCE CONTROLS"
+.na
+.nf
+.ad
+.fi
+.IP "\fBsession_cache_ttl_limit (2s)\fR"
+The maximal time-to-live value that the session cache server
+allows.
+.SH "MISCELLANEOUS CONTROLS"
+.na
+.nf
+.ad
+.fi
+.IP "\fBconfig_directory (see 'postconf -d' output)\fR"
+The default location of the Postfix main.cf and master.cf
+configuration files.
+.IP "\fBdaemon_timeout (18000s)\fR"
+How much time a Postfix daemon process may take to handle a
+request before it is terminated by a built-in watchdog timer.
+.IP "\fBipc_timeout (3600s)\fR"
+The time limit for sending or receiving information over an internal
+communication channel.
+.IP "\fBmax_idle (100s)\fR"
+The maximum amount of time that an idle Postfix daemon process
+waits for the next service request before exiting.
+.IP "\fBprocess_id (read-only)\fR"
+The process ID of a Postfix command or daemon process.
+.IP "\fBprocess_name (read-only)\fR"
+The process name of a Postfix command or daemon process.
+.IP "\fBsyslog_facility (mail)\fR"
+The syslog facility of Postfix logging.
+.IP "\fBsyslog_name (postfix)\fR"
+The mail system name that is prepended to the process name in syslog
+records, so that "smtpd" becomes, for example, "postfix/smtpd".
+.SH "SEE ALSO"
+.na
+.nf
+smtp(8), SMTP client
+postconf(5), configuration parameters
+master(8), process manager
+syslogd(8), system logging
+.SH "LICENSE"
+.na
+.nf
+.ad
+.fi
+The Secure Mailer license must be distributed with this software.
+.SH "HISTORY"
+.na
+.nf
+This service was introduced with Postfix version 2.2.
+.SH "AUTHOR(S)"
+.na
+.nf
+Wietse Venema
+IBM T.J. Watson Research
+P.O. Box 704
+Yorktown Heights, NY 10598, USA
index be03f343daf7118654939a3097d368f8e3ea3230..b7feea688dea715db2ec85b803dfc7d949fe9feb 100644 (file)
@@ -31,6 +31,11 @@ to each listed address until it finds a server that responds.
 When a server is not reachable, or when mail delivery fails due
 to a recoverable error condition, the SMTP client will try to
 deliver the mail to an alternate host.
+
+After a successful mail transaction, a session may be saved
+to the \fBscache(8)\fR session cache server, so that it
+may be used by any SMTP client for a subsequent transaction.
+Session caching is disabled by default.
 .SH "SECURITY"
 .na
 .nf
@@ -62,6 +67,15 @@ move them to the \fBcorrupt\fR queue for further inspection.
 Depending on the setting of the \fBnotify_classes\fR parameter,
 the postmaster is notified of bounces, protocol problems, and of
 other trouble.
+.SH BUGS
+.ad
+.fi
+SMTP session caching does not work with TLS. The necessary
+support for object passivation and re-activation does not
+exist.
+
+SMTP session caching assumes that SASL credentials are valid for
+all destinations that map onto the same IP address and TCP port.
 .SH "CONFIGURATION PARAMETERS"
 .na
 .nf
@@ -121,8 +135,7 @@ Disable the conversion of 8BITMIME format to 7BIT format.
 .IP "\fBmime_boundary_length_limit (2048)\fR"
 The maximal length of MIME multipart boundary strings.
 .IP "\fBmime_nesting_limit (100)\fR"
-The maximal nesting level of multipart mail that the MIME processor
-will handle.
+The maximal recursion level that the MIME processor will handle.
 .SH "EXTERNAL CONTENT INSPECTION CONTROLS"
 .na
 .nf
@@ -191,9 +204,20 @@ result from mail exchanger lookups, or zero (no limit).
 The maximal number of SMTP sessions per delivery request before
 giving up or delivering to a fall-back relay host, or zero (no
 limit).
-.IP "\fBsmtp_rset_timeout (120s)\fR"
+.IP "\fBsmtp_rset_timeout (20s)\fR"
 The SMTP client time limit for sending the RSET command, and
 for receiving the server response.
+.PP
+Available in Postfix version 2.2 and later:
+.IP "\fBsmtp_connection_cache_domains (empty)\fR"
+The SMTP destinations for which SMTP connection caching is
+enabled.
+.IP "\fBsmtp_connection_cache_reuse_limit (10)\fR"
+When SMTP session caching is enabled, the number of times that
+an SMTP session is reused before it is closed.
+.IP "\fBsmtp_connection_cache_time_limit (2s)\fR"
+When SMTP session caching is enabled, the amount of time that
+an unused SMTP client socket is kept open before it is closed.
 .SH "TROUBLE SHOOTING CONTROLS"
 .na
 .nf
@@ -270,6 +294,7 @@ records, so that "smtpd" becomes, for example, "postfix/smtpd".
 .nf
 qmgr(8), queue manager
 bounce(8), delivery status reports
+scache(8), session cache server
 postconf(5), configuration parameters
 master(8), process manager
 syslogd(8), system logging
@@ -296,3 +321,15 @@ Wietse Venema
 IBM T.J. Watson Research
 P.O. Box 704
 Yorktown Heights, NY 10598, USA
+
+Command pipelining in cooperation with:
+Jon Ribbens
+Oaktree Internet Solutions Ltd.,
+Internet House,
+Canal Basin,
+Coventry,
+CV1 4LY, United Kingdom.
+
+Connection caching in cooperation with:
+Victor Duchovni
+Morgan Stanley
index 79d5730ad67f56fab995a896787a05d1d186321c..7e1ade71472f25e208b7c35a830888fe17d01e64 100644 (file)
@@ -5,7 +5,9 @@
 #
 # Input format: the leader text is copied verbatim; each paragraph
 # starts with [class, class] where a class specifies one or more
-# categories that the change should be listed under.
+# categories that the change should be listed under. Adding class
+# info is the only manual processing needed to go from a RELEASE_NOTES
+# file to the transformed representation.
 # 
 # Output format: each category is printed with a little header and
 # each paragraph is tagged with [Incompat yyyymmdd] or with [Feature
index bcbb1f05bbb45d98fe5809a2408ac9e375cb466c..8b854afa63701705d90e0ed25bfacdb2af508359 100755 (executable)
@@ -293,11 +293,20 @@ while (<>) {
     s;\bsendmail_path\b;<a href="postconf.5.html#sendmail_path">$&</a>;g;
     s;\bservice_throttle_time\b;<a href="postconf.5.html#service_throttle_time">$&</a>;g;
     s;\bsetgid_group\b;<a href="postconf.5.html#setgid_group">$&</a>;g;
+
+    s;\bsession_cache_service\b;<a href="postconf.5.html#session_cache_service">$&</a>;g;
+    s;\bsession_cache_ttl_limit\b;<a href="postconf.5.html#session_cache_ttl_limit">$&</a>;g;
+
     s;\bshow_user_unknown_table_name\b;<a href="postconf.5.html#show_user_unknown_table_name">$&</a>;g;
     s;\bshowq_service_name\b;<a href="postconf.5.html#showq_service_name">$&</a>;g;
     s;\bsmtp_always_send_ehlo\b;<a href="postconf.5.html#smtp_always_send_ehlo">$&</a>;g;
     s;\bsmtp_bind_address\b;<a href="postconf.5.html#smtp_bind_address">$&</a>;g;
     s;\bsmtp_connect_timeout\b;<a href="postconf.5.html#smtp_connect_timeout">$&</a>;g;
+
+    s;\bsmtp_connection_cache_reuse_limit\b;<a href="postconf.5.html#smtp_connection_cache_reuse_limit">$&</a>;g;
+    s;\bsmtp_connection_cache_time_limit\b;<a href="postconf.5.html#smtp_connection_cache_time_limit">$&</a>;g;
+    s;\bsmtp_connection_cache_domains\b;<a href="postconf.5.html#smtp_connection_cache_domains">$&</a>;g;
+
     s;\bsmtp_data_done_timeout\b;<a href="postconf.5.html#smtp_data_done_timeout">$&</a>;g;
     s;\bsmtp_data_init_timeout\b;<a href="postconf.5.html#smtp_data_init_timeout">$&</a>;g;
     s;\bsmtp_data_xfer_timeout\b;<a href="postconf.5.html#smtp_data_xfer_timeout">$&</a>;g;
@@ -478,6 +487,7 @@ while (<>) {
     s/[<bB>]*proxymap[<\/bB>]*\(8\)/<a href="proxymap.8.html">$&<\/a>/g;
     s/[<bB>]*reg[-<\/bB>]*\n*[ <bB>]*exp[<\/bBiI>]*_[<\/iIbB>]*table[<\/bB>]*\(5\)/<a href="regexp_table.5.html">$&<\/a>/g;
     s/[<bB>]*relocated[<\/bB>]*\(5\)/<a href="relocated.5.html">$&<\/a>/g;
+    s/[<bB>]*scache[<\/bB>]*\(8\)/<a href="scache.8.html">$&<\/a>/g;
     s/[<bB>]*trans[-<\/bB>]*\n*[ <bB>]*port[<\/bB>]*\(5\)/<a href="transport.5.html">$&<\/a>/g;
     s/[<bB>]*verify[<\/bB>]*\(8\)/<a href="verify.8.html">$&<\/a>/g;
     s/[<bB>]*virtual[<\/bB>]*\(5\)/<a href="virtual.5.html">$&<\/a>/g;
index 5401aed86171d6c9c47c5c234e1c05e6d1fc92f9..08f8314ea19939fe433b0fdc8dfbfbad3982467d 100644 (file)
@@ -535,6 +535,26 @@ service to Postfix processes. This overcomes chroot restrictions,
 and reduces the number of open lookup tables by sharing one open
 table among multiple processes. </p>
 
+<li> <p> The scache(8) server maintains the session cache for the
+Postfix smtp(8) client. When session caching is enabled for selected
+destinations, the smtp(8) client does not disconnect immediately
+after a mail transaction, but gives the connection to the session
+cache server.  The smtp(8) client continues with some other mail
+delivery request. Meanwhile, the session cache server keeps the
+connection open for a limited amount of time.  During that time,
+any smtp(8) process can ask the scache(8) server for that cached
+session and use it for mail delivery.  </p>
+
+<table>
+
+<tr> <td> <td align="center" bgcolor="#f0f0ff"> <br> smtp(8) <br>
+&nbsp; </td> <td> <tt> -&gt; </tt> </td> <td> <td align="center"
+bgcolor="#f0f0ff"> <br> scache(8) <br> &nbsp; </td> <td> <tt> -&gt;
+</tt> </td> <td> <td align="center" bgcolor="#f0f0ff"> <br> smtp(8)
+<br> &nbsp; </td>
+
+</table>
+
 <li> <p> The showq(8) servers list the Postfix queue status. This
 is the queue listing service that does the work for the mailq(1)
 and postqueue(1) commands.  </p>
index 49df5bf1723908437073bad37ea86bb09f91f32f..1aa6127974d843dc34313f90873bc677fdbd5d50 100644 (file)
 # .IP "\fB\fB%u\fR\fR"
 #      When the input key is an address of the form user@domain,
 #      \fB%u\fR is replaced by the (RFC 2254) quoted local part of the
-#      address. If no domain is specified, \fB%u\fR is replaced by the
-#      entire search string.
+#      address. Otherwise, \fB%u\fR is replaced by the entire
+#      search string.
 # .IP "\fB\fB%d\fR\fR"
 #      When the input key is an address of the form user@domain,
 #      \fB%d\fR is replaced by the (RFC 2254) quoted domain part of the
-#      address. When the input key has no domain qualifier, \fB%d\fR is
-#      replaced by the entire search string.
+#      address. Otherwise, \fB%d\fR is replaced by the entire
+#      search string.
 # .RE
 # .IP
 #      The "domain" parameter described below limits the input
 # .IP "\fB\fB%s\fR\fR"
 #      This is replaced by the value of the result attribute.
 # .IP "\fB%u\fR
-#      When the result attribute is an address of the form
-#      user@domain, \fB%u\fR is replaced local part of the address, if
-#      the result attribute is unqualified, \fB%u\fR is replaced by the
-#      entire attribute value.
+#      When the result attribute value is an address of the form
+#      user@domain, \fB%u\fR is replaced by the local part of the
+#      address.  Otherwise, \fB%u\fR is replaced by the entire
+#      attribute value.
 # .IP "\fB\fB%d\fR\fR"
-#      When a result attribute is an address of the form user@domain,
-#      \fB%d\fR is replaced by the domain part of the attribute value.
-#      If an attribute value is unqualified \fB%d\fR is replaced by the
-#      entire attribute value.
+#      When a result attribute value is an address of the form
+#      user@domain, \fB%d\fR is replaced by the domain part of
+#      the attribute value.  Otherwise, \fB%d\fR is replaced by
+#      the entire attribute value.
 # .RE
 # .IP
 #      For example, using "result_filter = smtp:[%s]" allows one
index a71bccf556c5e4619e30bf68c5cb36a185909271..9ee27caccfc2db1517825fac12a90028f241ea30 100644 (file)
@@ -645,10 +645,10 @@ This feature is available in Postfix 2.1 and later.
 
 %PARAM bounce_size_limit 50000
 
-<p>
-The maximal amount of original message text that is sent in a
-non-delivery notification. Specify a byte count.
-</p>
+<p> The maximal amount of original message text that is sent in a
+non-delivery notification. Specify a byte count. If you increase
+this limit, then you should increase the mime_nesting_limit value
+proportionally.  </p>
 
 %PARAM canonical_maps 
 
@@ -726,8 +726,8 @@ address patterns that cause the verbose logging level to increase
 by the amount specified in $debug_peer_level.  </p>
 
 <p> Specify domain names, network/netmask patterns, "/file/name"
-patterns or "type:table" lookup tables. The result from lookup
-tables is ignored.  </p>
+patterns or "type:table" lookup tables. The right-hand side result
+from "type:table" lookups is ignored.  </p>
 
 <p> Pattern matching of domain names is controlled by the
 parent_domain_matches_subdomains parameter.  </p>
@@ -1698,13 +1698,12 @@ Time units: s (seconds), m (minutes), h (hours), d (days), w (weeks).
 The default time unit is s (seconds).
 </p>
 
-%PARAM lmtp_rset_timeout 120s
+%PARAM lmtp_rset_timeout 20s
 
-<p>
-The LMTP client time limit for sending the RSET command, and for
-receiving the server response. The LMTP client sends RSET in order
-to find out if a cached connection is still alive.
-</p>
+<p> The LMTP client time limit for sending the RSET command, and
+for receiving the server response. The LMTP client sends RSET in
+order to finish a recipient address probe, or to verify that a
+cached connection is still alive.  </p>
 
 <p>
 Time units: s (seconds), m (minutes), h (hours), d (days), w (weeks).
@@ -3173,6 +3172,50 @@ for example:
         smtp ... smtp -o smtp_bind_address=11.22.33.44
 </pre>
 
+%PARAM smtp_connection_cache_time_limit 2s
+
+<p> When SMTP session caching is enabled, the amount of time that
+an unused SMTP client socket is kept open before it is closed.  Do
+not specify larger values without permission from the remote sites.
+</p>
+
+%PARAM smtp_connection_cache_reuse_limit 10
+
+<p> When SMTP session caching is enabled, the number of times that
+an SMTP session is reused before it is closed. 
+</p>
+
+%PARAM smtp_connection_cache_domains
+
+<p> The SMTP destinations for which SMTP connection caching is
+enabled.  With SMTP connection caching, a connection is not closed
+immediately after completion of a mail transaction.  Instead, the
+connection is kept open for up to $smtp_connection_cache_time_limit
+seconds.  This allows connections to be reused for other deliveries,
+and can improve mail delivery performance. </p>
+
+<p> Specify a comma or white space separated list of destinations
+or pseudo-destinations:
+</p>
+
+<ul>
+
+<li> a domain name (the right-hand side of an email address),
+
+<li> a relay host (including optional [] and/or non-default TCP
+port), using the exact same spelling as in main.cf or in the
+transport map,
+
+<li> a /file/name with domains and/or relay hosts,
+
+<li> a "type:table" with domains and/or relay hosts on the left-hand
+side.  The right-hand side result from "type:table" lookups is
+ignored.
+
+</ul>
+
+<p></p>
+
 %PARAM smtp_connect_timeout 30s
 
 <p>
@@ -5953,8 +5996,8 @@ This feature is available in Postfix 2.0 and later.
 %PARAM mime_nesting_limit 100
 
 <p>
-The maximal nesting level of multipart mail that the MIME processor
-will handle. Postfix refuses mail that is nested deeper.
+The maximal recursion level that the MIME processor will handle.
+Postfix refuses mail that is nested deeper than the specified limit.
 </p>
 
 <p>
@@ -6290,10 +6333,12 @@ Randomize the order of equal-preference MX host addresses.  This
 is a performance feature of the Postfix SMTP client.
 </p>
 
-%PARAM smtp_rset_timeout 120s
+%PARAM smtp_rset_timeout 20s
 
 <p> The SMTP client time limit for sending the RSET command, and
-for receiving the server response.  </p>
+for receiving the server response. The SMTP client sends RSET in
+order to finish a recipient address probe, or to verify that a
+cached session is still usable.  </p>
 
 <p> This feature is available in Postfix 2.1 and later.  </p>
 
@@ -6870,3 +6915,16 @@ and b) addresses that are aliased to addresses in other local or
 remote domains.  Available before Postfix version 2.0. With Postfix 2.1
 and later, this is replaced by separate controls: virtual_alias_domains
 and virtual_alias_maps. </p>
+
+%PARAM session_cache_service scache
+
+<p> The name of the scache(8) session cache service.  This service
+maintains a limited pool of cached sessions.  </p>
+
+%PARAM session_cache_ttl_limit 2s
+
+<p> The maximal time-to-live value that the session cache server
+allows. Requests that specify a larger TTL will be stored with the
+maximum allowed TTL. The purpose of this additional control is to
+protect the infrastructure against careless people. The cache TTL
+is already bounded by $max_idle.  </p>
index b88d2b255d75ff4b0ab393484b1cc76d2715f38b..23d6d3062750d0133ff0464e632ae133f201e40e 100644 (file)
@@ -46,7 +46,7 @@ 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 *"\([^"]*\)".*/{' \
+           $(CC) -E $(DEFS) $(INCL) $$i | grep -v '[<>]' | 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
index 6a3f352b3f9a4d888d2170c3669de0415a1ddba3..86030bf3d4a53d4b1896526501d8efb6ca047d01 100644 (file)
@@ -51,7 +51,7 @@ 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 *"\([^"]*\)".*/{' \
+           $(CC) -E $(DEFS) $(INCL) $$i | grep -v '[<>]' | 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
index 4d4e632e0fe6144be0e40019a7188b39da264f88..b1a884c5826f84d0b9aa7626ad8d959991750a9a 100644 (file)
@@ -75,7 +75,7 @@ cleanup_masquerade_test: cleanup_masquerade cleanup_masq.ref
 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 *"\([^"]*\)".*/{' \
+           $(CC) -E $(DEFS) $(INCL) $$i | grep -v '[<>]' | 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
index 97de4cf6662c2873d5b46cace23d6f57df685248..5414187141c4c41f488734fea324c77250a12b47 100644 (file)
 /* .IP "\fBmime_boundary_length_limit (2048)\fR"
 /*     The maximal length of MIME multipart boundary strings.
 /* .IP "\fBmime_nesting_limit (100)\fR"
-/*     The maximal nesting level of multipart mail that the MIME processor
-/*     will handle.
+/*     The maximal recursion level that the MIME processor will handle.
 /* .IP "\fBstrict_8bitmime (no)\fR"
 /*     Enable both strict_7bit_headers and strict_8bitmime_body.
 /* .IP "\fBstrict_7bit_headers (no)\fR"
 /* .IP "\fBmime_boundary_length_limit (2048)\fR"
 /*     The maximal length of MIME multipart boundary strings.
 /* .IP "\fBmime_nesting_limit (100)\fR"
-/*     The maximal nesting level of multipart mail that the MIME processor
-/*     will handle.
+/*     The maximal recursion level that the MIME processor will handle.
 /* .IP "\fBqueue_file_attribute_count_limit (100)\fR"
 /*     The maximal number of (name=value) attributes that may be stored
 /*     in a Postfix queue file.
index c96f672565efcaa7fa7f8ea19cc804c616d9315b..bdbad7385abbcddf2bf341a8b1ea660e89b61dde 100644 (file)
@@ -61,7 +61,7 @@ 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 *"\([^"]*\)".*/{' \
+           $(CC) -E $(DEFS) $(INCL) $$i | grep -v '[<>]' | 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
index e08583d6e8d5ae6d3b7f791feaa0a925bbed9d06..014536b6e235065d528a44d2b6bc54dac4afc0e6 100644 (file)
@@ -106,6 +106,7 @@ extern DNS_RR *dns_rr_copy(DNS_RR *);
 extern DNS_RR *dns_rr_append(DNS_RR *, DNS_RR *);
 extern DNS_RR *dns_rr_sort(DNS_RR *, int (*) (DNS_RR *, DNS_RR *));
 extern DNS_RR *dns_rr_shuffle(DNS_RR *);
+extern DNS_RR *dns_rr_remove(DNS_RR *, DNS_RR *);
 
  /*
   * dns_lookup.c
index 2dc8edd1b0c8ab1d0635e718171690d4913c0e37..a00a738d0ee8aaba4c5c67a001158e05b7d22b95 100644 (file)
 /*
 /*     DNS_RR  *dns_rr_shuffle(list)
 /*     DNS_RR  *list;
+/*
+/*     DNS_RR  *dns_rr_remove(list, record)
+/*     DNS_RR  *list;
+/*     DNS_RR  *record;
 /* DESCRIPTION
 /*     The routines in this module maintain memory for DNS resource record
 /*     information, and maintain lists of DNS resource records.
@@ -54,6 +58,9 @@
 /*     sorted list.
 /*
 /*     dns_rr_shuffle() randomly permutes a list of resource records.
+/*
+/*     dns_rr_remove() removes the specified record from the specified list.
+/*     The updated list is the result value.
 /* LICENSE
 /* .ad
 /* .fi
@@ -243,3 +250,20 @@ DNS_RR *dns_rr_shuffle(DNS_RR *list)
     myfree((char *) rr_array);
     return (list);
 }
+
+/* dns_rr_remove - remove record from list, return new list */
+
+DNS_RR *dns_rr_remove(DNS_RR *list, DNS_RR *record)
+{
+    if (list == 0) 
+       msg_panic("dns_rr_remove: record not found");
+
+    if (list == record) {
+       list = record->next;
+       record->next = 0;
+       dns_rr_free(record);
+    } else {
+       list->next = dns_rr_remove(list->next, record);
+    }
+    return (list);
+}
index 82311bcf013fc3d3b07948eee49af52891b72101..bbeb61947526739866ec9dd8e6bd81854d83ec44 100644 (file)
@@ -46,7 +46,7 @@ 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 *"\([^"]*\)".*/{' \
+           $(CC) -E $(DEFS) $(INCL) $$i | grep -v '[<>]' | 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
index 4708bb12cb0fdf9e09f51439f55a738e9da04118..194a5673821613ad7bc49c785de506e02a6965f0 100644 (file)
@@ -46,7 +46,7 @@ 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 *"\([^"]*\)".*/{' \
+           $(CC) -E $(DEFS) $(INCL) $$i | grep -v '[<>]' | 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
index d22b4bc554f1fb380f13b6cd1a7dcf5dce36cd04..9956378003470323f95214cb3a667ca70ede2957 100644 (file)
@@ -45,7 +45,7 @@ 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 *"\([^"]*\)".*/{' \
+           $(CC) -E $(DEFS) $(INCL) $$i | grep -v '[<>]' | 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
index 8316f60396d2c6233fe7cc61a87c11225cff3ced..240749c1f2f0ab68d90b24b518d790ef7a4c5545 100644 (file)
@@ -22,7 +22,8 @@ SRCS  = abounce.c anvil_clnt.c been_here.c bounce.c bounce_log.c \
        sent.c smtp_stream.c split_addr.c string_list.c strip_addr.c \
        sys_exits.c timed_ipc.c tok822_find.c tok822_node.c tok822_parse.c \
        tok822_resolve.c tok822_rewrite.c tok822_tree.c trace.c verify.c \
-       verify_clnt.c verp_sender.c virtual8_maps.c xtext.c
+       verify_clnt.c verp_sender.c virtual8_maps.c xtext.c scache_single.c \
+       scache_clnt.c scache_multi.c
 OBJS   = abounce.o anvil_clnt.o been_here.o bounce.o bounce_log.o \
        canon_addr.o cfg_parser.o cleanup_strerror.o cleanup_strflags.o \
        clnt_stream.o debug_peer.o debug_process.o defer.o \
@@ -46,7 +47,8 @@ OBJS  = abounce.o anvil_clnt.o been_here.o bounce.o bounce_log.o \
        sent.o smtp_stream.o split_addr.o string_list.o strip_addr.o \
        sys_exits.o timed_ipc.o tok822_find.o tok822_node.o tok822_parse.o \
        tok822_resolve.o tok822_rewrite.o tok822_tree.o trace.o verify.o \
-       verify_clnt.o verp_sender.o virtual8_maps.o xtext.o
+       verify_clnt.o verp_sender.o virtual8_maps.o xtext.o scache_single.o \
+       scache_clnt.o scache_multi.o
 HDRS   = abounce.h anvil_clnt.h been_here.h bounce.h bounce_log.h \
        canon_addr.h cfg_parser.h cleanup_user.h clnt_stream.h config.h \
        debug_peer.h debug_process.h defer.h deliver_completed.h \
@@ -67,7 +69,7 @@ HDRS  = abounce.h anvil_clnt.h been_here.h bounce.h bounce_log.h \
        resolve_local.h rewrite_clnt.h sent.h smtp_stream.h split_addr.h \
        string_list.h strip_addr.h sys_exits.h timed_ipc.h tok822.h \
        trace.h verify.h verify_clnt.h verp_sender.h virtual8_maps.h \
-       xtext.h
+       xtext.h scache.h
 TESTSRC        = rec2stream.c stream2rec.c recdump.c
 DEFS   = -I. -I$(INC_DIR) -D$(SYSTYPE)
 CFLAGS = $(DEBUG) $(OPT) $(DEFS)
@@ -78,7 +80,7 @@ TESTPROG= domain_list dot_lockfile mail_addr_crunch mail_addr_find \
        off_cvt quote_822_local rec2stream recdump resolve_clnt \
        resolve_local rewrite_clnt stream2rec string_list tok822_parse \
        quote_821_local mail_conf_time mime_state strip_addr \
-       virtual8_maps verify_clnt xtext anvil_clnt
+       virtual8_maps verify_clnt xtext anvil_clnt scache
 
 LIBS   = ../../lib/libutil.a
 LIB_DIR        = ../../lib
@@ -236,19 +238,22 @@ verify_clnt: $(LIB) $(LIBS)
        $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(LIBS) $(SYSLIBS)
        mv junk $@.o
 
-xtext: $(LIB)
+xtext: $(LIB) $(LIBS)
        mv $@.o junk
        $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(LIBS) $(SYSLIBS)
        mv junk $@.o
 
-anvil_clnt: $(LIB)
+anvil_clnt: $(LIB) $(LIBS)
        mv $@.o junk
        $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(LIBS) $(SYSLIBS)
        mv junk $@.o
 
+scache: scache.c $(LIB) $(LIBS)
+       $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(LIBS) $(SYSLIBS)
+
 tests: tok822_test mime_test mime_nest mime_8bit mime_dom mime_trunc \
        mime_cvt mime_cvt2 mime_cvt3 strip_addr_test tok822_limit_test \
-       virtual8_test xtext_test
+       virtual8_test xtext_test scache_multi_test
 
 tok822_test: tok822_parse tok822_parse.in tok822_parse.ref
        ./tok822_parse <tok822_parse.in >tok822_parse.tmp 2>&1
@@ -342,6 +347,11 @@ resolve_clnt_test: resolve_clnt resolve_clnt.in resolve_clnt.ref
            resolve_clnt.ref | diff - resolve_clnt.tmp
        rm -f resolve_clnt.tmp
 
+scache_multi_test: scache scache_multi.in scache_multi.ref
+       ./scache <scache_multi.in >scache_multi.tmp
+       diff scache_multi.ref scache_multi.tmp
+       rm -f scache_multi.tmp
+
 printfck: $(OBJS) $(PROG)
        rm -rf printfck
        mkdir printfck
@@ -362,7 +372,7 @@ 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 *"\([^"]*\)".*/{' \
+           $(CC) -E $(DEFS) $(INCL) $$i | grep -v '[<>]' | 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
@@ -1234,6 +1244,47 @@ rewrite_clnt.o: ../../include/attr.h
 rewrite_clnt.o: mail_params.h
 rewrite_clnt.o: clnt_stream.h
 rewrite_clnt.o: rewrite_clnt.h
+scache.o: scache.c
+scache.o: ../../include/sys_defs.h
+scache.o: ../../include/msg.h
+scache.o: ../../include/vstream.h
+scache.o: ../../include/vbuf.h
+scache.o: ../../include/vstring.h
+scache.o: ../../include/vstring_vstream.h
+scache.o: ../../include/argv.h
+scache.o: ../../include/events.h
+scache.o: scache.h
+scache_clnt.o: scache_clnt.c
+scache_clnt.o: ../../include/sys_defs.h
+scache_clnt.o: ../../include/msg.h
+scache_clnt.o: ../../include/mymalloc.h
+scache_clnt.o: mail_proto.h
+scache_clnt.o: ../../include/vstream.h
+scache_clnt.o: ../../include/vbuf.h
+scache_clnt.o: ../../include/iostuff.h
+scache_clnt.o: ../../include/attr.h
+scache_clnt.o: mail_params.h
+scache_clnt.o: clnt_stream.h
+scache_clnt.o: scache.h
+scache_clnt.o: ../../include/vstring.h
+scache_multi.o: scache_multi.c
+scache_multi.o: ../../include/sys_defs.h
+scache_multi.o: ../../include/msg.h
+scache_multi.o: ../../include/ring.h
+scache_multi.o: ../../include/htable.h
+scache_multi.o: ../../include/vstring.h
+scache_multi.o: ../../include/vbuf.h
+scache_multi.o: ../../include/mymalloc.h
+scache_multi.o: ../../include/events.h
+scache_multi.o: scache.h
+scache_single.o: scache_single.c
+scache_single.o: ../../include/sys_defs.h
+scache_single.o: ../../include/msg.h
+scache_single.o: ../../include/vstring.h
+scache_single.o: ../../include/vbuf.h
+scache_single.o: ../../include/mymalloc.h
+scache_single.o: ../../include/events.h
+scache_single.o: scache.h
 sent.o: sent.c
 sent.o: ../../include/sys_defs.h
 sent.o: ../../include/msg.h
index b3f91f51d1d3430c3ed3161630738461db47f48a..db99a7c6db465b80162505871bc8beb238be35c7 100644 (file)
@@ -769,6 +769,18 @@ extern int var_hash_queue_depth;
 #define DEF_BESTMX_TRANSP      ""
 extern char *var_bestmx_transp;
 
+#define VAR_SMTP_CACHE_CONN    "smtp_connection_cache_time_limit"
+#define DEF_SMTP_CACHE_CONN    "2s"
+extern int var_smtp_cache_conn;
+
+#define VAR_SMTP_REUSE_LIMIT   "smtp_connection_cache_reuse_limit"
+#define DEF_SMTP_REUSE_LIMIT   10
+extern int var_smtp_reuse_limit;
+
+#define VAR_SMTP_CACHE_DEST    "smtp_connection_cache_domains"
+#define DEF_SMTP_CACHE_DEST    ""
+extern char *var_smtp_cache_dest;
+
 #define VAR_SMTP_CONN_TMOUT    "smtp_connect_timeout"
 #define DEF_SMTP_CONN_TMOUT    "30s"
 extern int var_smtp_conn_tmout;
@@ -802,7 +814,7 @@ extern int var_smtp_data1_tmout;
 extern int var_smtp_data2_tmout;
 
 #define VAR_SMTP_RSET_TMOUT    "smtp_rset_timeout"
-#define DEF_SMTP_RSET_TMOUT    "120s"
+#define DEF_SMTP_RSET_TMOUT    "20s"
 extern int var_smtp_rset_tmout;
 
 #define VAR_SMTP_QUIT_TMOUT    "smtp_quit_timeout"
@@ -933,6 +945,10 @@ extern char *var_smtpd_sasl_appname;
 #define DEF_SMTPD_SASL_REALM   ""
 extern char *var_smtpd_sasl_realm;
 
+#define VAR_SMTPD_SASL_EXCEPTIONS_NETWORKS     "smtpd_sasl_exceptions_networks"
+#define DEF_SMTPD_SASL_EXCEPTIONS_NETWORKS     ""
+extern char *var_smtpd_sasl_exceptions_networks;
+
 #define VAR_SMTPD_SND_AUTH_MAPS        "smtpd_sender_login_maps"
 #define DEF_SMTPD_SND_AUTH_MAPS        ""
 extern char *var_smtpd_snd_auth_maps;
@@ -991,10 +1007,6 @@ extern int var_lmtpd_err_sleep;
 #define DEF_LMTPD_JUNK_CMD     1000
 extern int var_lmtpd_junk_cmd_limit;
 
-#define VAR_SMTPD_SASL_EXCEPTIONS_NETWORKS     "smtpd_sasl_exceptions_networks"
-#define DEF_SMTPD_SASL_EXCEPTIONS_NETWORKS     ""
-extern char *var_smtpd_sasl_exceptions_networks;
-
  /*
   * SASL authentication support, LMTP server side.
   */
@@ -1053,7 +1065,7 @@ extern bool var_lmtp_skip_quit_resp;
 extern int var_lmtp_conn_tmout;
 
 #define VAR_LMTP_RSET_TMOUT    "lmtp_rset_timeout"
-#define DEF_LMTP_RSET_TMOUT    "120s"
+#define DEF_LMTP_RSET_TMOUT    "20s"
 extern int var_lmtp_rset_tmout;
 
 #define VAR_LMTP_LHLO_TMOUT    "lmtp_lhlo_timeout"
@@ -1776,6 +1788,17 @@ extern char *var_error_service;
 #define DEF_FLUSH_SERVICE              MAIL_SERVICE_FLUSH
 extern char *var_flush_service;
 
+ /*
+  * Session cache service.
+  */
+#define VAR_SCACHE_SERVICE             "session_cache_service"
+#define DEF_SCACHE_SERVICE             "scache"
+extern char *var_scache_service;
+
+#define VAR_SCACHE_TTL_LIM             "session_cache_ttl_limit"
+#define DEF_SCACHE_TTL_LIM             "2s"
+extern int var_scache_ttl_lim;
+
  /*
   * Address verification service.
   */
index 1fd24d1f81ce018e27d762f76792c14ec45b4391..4d9082a7bd6b40cfcf77abba643f68b9cec675cb 100644 (file)
@@ -54,6 +54,7 @@
 #define MAIL_SERVICE_TRACE     "trace"
 #define MAIL_SERVICE_RELAY     "relay"
 #define MAIL_SERVICE_PROXYMAP  "proxymap"
+#define MAIL_SERVICE_SCACHE    "scache"
 
  /*
   * Well-known socket or FIFO directories. The main difference is in file
@@ -121,6 +122,10 @@ extern char *mail_pathname(const char *, const char *);
 #define MAIL_ATTR_SASL_USERNAME        "sasl_username"
 #define MAIL_ATTR_SASL_SENDER  "sasl_sender"
 
+#define MAIL_ATTR_TTL          "ttl"
+#define MAIL_ATTR_LABEL                "label"
+#define MAIL_ATTR_PROP         "property"
+
  /*
   * Suffixes for sender_name, sender_domain etc.
   */
index 0ab9ea1e9478ddac02de7ef6bf66e446944c896c..13f3b1872d6f45a4e873e34888c2d85cf19a14b2 100644 (file)
@@ -20,7 +20,7 @@
   * Patches change the patchlevel and the release date. Snapshots change the
   * release date only.
   */
-#define MAIL_RELEASE_DATE      "20040628"
+#define MAIL_RELEASE_DATE      "20040720"
 #define MAIL_VERSION_NUMBER    "2.2"
 
 #define VAR_MAIL_VERSION       "mail_version"
index e40081245f61a9b44c95fe0ef874734102312a14..7beba3aab93eda810e8b577ee66336d70e5d513e 100644 (file)
@@ -99,7 +99,7 @@
 /*     Report errors that set the MIME_ERR_TRUNC_HEADER error flag
 /*     (see above).
 /* .IP MIME_OPT_REPORT_8BIT_IN_HEADER
-/*     Report errors that set the MIME_ERR_8BIT_IN_7BIT_BODY error
+/*     Report errors that set the MIME_ERR_8BIT_IN_HEADER error
 /*     flag (see above). This rarely stops legitimate mail.
 /* .IP MIME_OPT_REPORT_8BIT_IN_7BIT_BODY
 /*     Report errors that set the MIME_ERR_8BIT_IN_7BIT_BODY error
diff --git a/postfix/src/global/scache.c b/postfix/src/global/scache.c
new file mode 100644 (file)
index 0000000..bc30b17
--- /dev/null
@@ -0,0 +1,391 @@
+/*++
+/* NAME
+/*     scache 3
+/* SUMMARY
+/*     generic session cache API
+/* SYNOPSIS
+/*     #include <scache.h>
+/* DESCRIPTION
+/*     void    scache_free(scache)
+/*     SCACHE  *scache;
+/*
+/*     void    scache_save_endp(scache, endp_ttl, endp_label, endp_prop, fd)
+/*     SCACHE  *scache;
+/*     int     endp_ttl;
+/*     const char *endp_label;
+/*     const char *endp_prop;
+/*     int     fd;
+/*
+/*     int     scache_find_endp(scache, endp_label, endp_prop)
+/*     SCACHE  *scache;
+/*     const char *endp_label;
+/*     VSTRING *endp_prop;
+/*
+/*     void    scache_save_dest(scache, dest_ttl, dest_label,
+/*                             dest_prop, endp_label)
+/*     SCACHE  *scache;
+/*     int     dest_ttl;
+/*     const char *dest_label;
+/*     const char *dest_prop;
+/*     const char *endp_label;
+/*
+/*     int     scache_find_dest(dest_label, dest_prop, endp_prop)
+/*     SCACHE  *scache;
+/*     const char *dest_label;
+/*     VSTRING *dest_prop;
+/*     VSTRING *endp_prop;
+/* DESCRIPTION
+/*     This module implements a generic session cache interface.
+/*     Specific cache types are described in scache_single(3),
+/*     scache_clnt(3) and scache_multi(3). These documents also
+/*     describe now to instantiate a specific session cache type.
+/*
+/*     The code maintains two types of association: a) physical
+/*     endpoint to file descriptor, and b) logical endpoint
+/*     to physical endpoint. Physical endpoints are stored and
+/*     look up under their low-level session details such as
+/*     numerical addresses, while logical endpoints are stored
+/*     and looked up by the domain name that humans use. One logical
+/*     endpoint can refer to multiple physical endpoints, one
+/*     physical endpoint may be referenced by multiple logical
+/*     endpoints, and one physical endpoint may have multiple
+/*     sessions.
+/*
+/*     scache_free() destroys the specified session cache.
+/*
+/*     scache_save_endp() stores an open session under the specified
+/*     physical endpoint name.
+/*
+/*     scache_find_endp() looks up a saved session under the
+/*     specified physical endpoint name.
+/*
+/*     scache_save_dest() associates the specified physical endpoint
+/*     with the specified logical endpoint name.
+/*
+/*     scache_find_dest() looks up a saved session under the
+/*     specified physical endpoint name.
+/*
+/*     Arguments:
+/* .IP endp_ttl
+/*     How long the session should be cached.  When information
+/*     expires it is purged automatically.
+/* .IP endp_label
+/*      The transport name and the physical endpoint name under
+/*      which the session is stored and looked up.
+/*
+/*     In the case of SMTP, the physical endpoint includes the numerical
+/*     IP address, address family information, and the numerical TCP port.
+/* .IP endp_prop
+/*     Application-specific data with endpoint attributes.  It is up to
+/*     the application to passivate (flatten) and re-activate this content
+/*     upon storage and retrieval, respectively.
+/* .sp
+/*     In the case of SMTP, the endpoint attributes specify the
+/*     server hostname, IP address, numerical TCP port, as well
+/*     as ESMTP features advertised by the server, and when information
+/*     expires. All this in some application-specific format that is easy
+/*     to unravel when re-activating a cached session.
+/* .IP dest_ttl
+/*     How long the destination-to-endpoint binding should be
+/*     cached. When information expires it is purged automatically.
+/* .IP dest_label
+/*     The transport name and the logical destination under which the
+/*     destination-to-endpoint binding is stored and looked up.
+/*
+/*     In the case of SMTP, the logical destination is the DNS
+/*     host or domain name with or without [], plus the numerical TCP port.
+/* .IP dest_prop
+/*     Application-specific attributes that describe features of
+/*     this logical to physical binding. It is up to the application
+/*     to passivate (flatten) and re-activate this content.
+/*     upon storage and retrieval, respectively
+/* .sp
+/*     In case the of an SMTP logical destination to physical
+/*     endpoint binding, the attributes specify the logical
+/*     destination name, numerical port, whether the physical
+/*     endpoint is best mx host with respect to a logical or
+/*     fall-back destination, and when information expires.
+/* .IP fd
+/*     File descriptor with session to be cached.
+/* DIAGNOSTICS
+/*     scache_find_endp() and scache_find_dest() return -1 when
+/*     the lookup fails, and a file descriptor upon success.
+/*
+/*     Other diagnostics: fatal error: memory allocation problem;
+/*     panic: internal consistency failure.
+/* SEE ALSO
+/*     scache_single(3), single-session, in-memory cache
+/*     scache_clnt(3), session cache client
+/*     scache_multi(3), multi-session, in-memory cache
+/* LICENSE
+/* .ad
+/* .fi
+/*     The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/*     Wietse Venema
+/*     IBM T.J. Watson Research
+/*     P.O. Box 704
+/*     Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <vstream.h>
+#include <vstring.h>
+#include <vstring_vstream.h>
+#include <argv.h>
+#include <events.h>
+
+/* Global library. */
+
+#include <scache.h>
+
+#ifdef TEST
+
+ /*
+  * Driver program for cache regression tests. Although all variants are
+  * relatively simple to verify by hand for single session storage, more
+  * sophisticated instrumentation is needed to demonstrate that the
+  * multi-sessin cache manager properly handles collisions in the time domain
+  * and in the various name space domains.
+  */
+static SCACHE *scache;
+static VSTRING *endp_prop;
+static VSTRING *dest_prop;
+static int verbose_level = 3;
+
+ /*
+  * Cache mode lookup table. We don't do the client-server variant because
+  * that drags in a ton of configuration junk; the client-server protocol is
+  * relatively easy to verify manually.
+  */
+struct cache_type {
+    char   *mode;
+    SCACHE *(*create) (void);
+};
+
+static struct cache_type cache_types[] = {
+    "single", scache_single_create,
+    "multi", scache_multi_create,
+    0,
+};
+
+#define STR(x) vstring_str(x)
+
+/* cache_type - select session cache type */
+
+static void cache_type(ARGV *argv)
+{
+    struct cache_type *cp;
+
+    if (argv->argc != 2) {
+       msg_error("usage: %s mode", argv->argv[0]);
+       return;
+    }
+    if (scache != 0)
+       scache_free(scache);
+    for (cp = cache_types; cp->mode != 0; cp++) {
+       if (strcmp(cp->mode, argv->argv[1]) == 0) {
+           scache = cp->create();
+           return;
+       }
+    }
+    msg_error("unknown cache type: %s", argv->argv[1]);
+}
+
+/* handle_events - handle events while time advances */
+
+static void handle_events(ARGV *argv)
+{
+    int     delay;
+    time_t  before;
+    time_t  after;
+
+    if (argv->argc != 2 || (delay = atoi(argv->argv[1])) <= 0) {
+       msg_error("usage: %s time", argv->argv[0]);
+       return;
+    }
+    before = event_time();
+    event_drain(delay);
+    after = event_time();
+    if (after < before + delay)
+       sleep(before + delay - after);
+}
+
+/* save_endp - save endpoint->session binding */
+
+static void save_endp(ARGV *argv)
+{
+    int     ttl;
+    int     fd;
+
+    if (argv->argc != 5
+       || (ttl = atoi(argv->argv[1])) <= 0
+       || (fd = atoi(argv->argv[4])) <= 0) {
+       msg_error("usage: save_endp ttl endpoint endp_props fd");
+       return;
+    }
+    if (DUP2(0, fd) < 0)
+       msg_fatal("dup2(0, %d): %m", fd);
+    scache_save_endp(scache, ttl, argv->argv[2], argv->argv[3], fd);
+}
+
+/* find_endp - find endpoint->session binding */
+
+static void find_endp(ARGV *argv)
+{
+    int     fd;
+
+    if (argv->argc != 2) {
+       msg_error("usage: find_endp endpoint");
+       return;
+    }
+    if ((fd = scache_find_endp(scache, argv->argv[1], endp_prop)) >= 0)
+       close(fd);
+}
+
+/* save_dest - save destination->endpoint binding */
+
+static void save_dest(ARGV *argv)
+{
+    int     ttl;
+
+    if (argv->argc != 5 || (ttl = atoi(argv->argv[1])) <= 0) {
+       msg_error("usage: save_dest ttl destination dest_props endpoint");
+       return;
+    }
+    scache_save_dest(scache, ttl, argv->argv[2], argv->argv[3], argv->argv[4]);
+}
+
+/* find_dest - find destination->endpoint->session binding */
+
+static void find_dest(ARGV *argv)
+{
+    int     fd;
+
+    if (argv->argc != 2) {
+       msg_error("usage: find_dest destination");
+       return;
+    }
+    if ((fd = scache_find_dest(scache, argv->argv[1], dest_prop, endp_prop)) >= 0)
+       close(fd);
+}
+
+/* verbose - adjust noise level during cache manipulation */
+
+static void verbose(ARGV *argv)
+{
+    int     level;
+
+    if (argv->argc != 2 || (level = atoi(argv->argv[1])) < 0) {
+       msg_error("usage: verbose level");
+       return;
+    }
+    verbose_level = level;
+}
+
+ /*
+  * The command lookup table.
+  */
+struct action {
+    char   *command;
+    void    (*action) (ARGV *);
+    int     flags;
+};
+
+#define FLAG_NEED_CACHE        (1<<0)
+
+static void help(ARGV *);
+
+static struct action actions[] = {
+    "cache_type", cache_type, 0,
+    "save_endp", save_endp, FLAG_NEED_CACHE,
+    "find_endp", find_endp, FLAG_NEED_CACHE,
+    "save_dest", save_dest, FLAG_NEED_CACHE,
+    "find_dest", find_dest, FLAG_NEED_CACHE,
+    "sleep", handle_events, 0,
+    "verbose", verbose, 0,
+    "?", help, 0,
+    0,
+};
+
+/* help - list commands */
+
+static void help(ARGV *argv)
+{
+    struct action *ap;
+
+    vstream_printf("commands:");
+    for (ap = actions; ap->command != 0; ap++)
+       vstream_printf(" %s", ap->command);
+    vstream_printf("\n");
+    vstream_fflush(VSTREAM_OUT);
+}
+
+/* get_buffer - prompt for input or log input */
+
+static int get_buffer(VSTRING *buf, VSTREAM *fp, int interactive)
+{
+    int     status;
+
+    if (interactive) {
+       vstream_printf("> ");
+       vstream_fflush(VSTREAM_OUT);
+    }
+    if ((status = vstring_get_nonl(buf, fp)) != VSTREAM_EOF) {
+       if (!interactive) {
+           vstream_printf(">>> %s\n", STR(buf));
+           vstream_fflush(VSTREAM_OUT);
+       }
+    }
+    return (status);
+}
+
+/* at last, the main program */
+
+int     main(int unused_argc, char **unused_argv)
+{
+    VSTRING *buf = vstring_alloc(1);
+    ARGV   *argv;
+    struct action *ap;
+    int     interactive = isatty(0);
+
+    endp_prop = vstring_alloc(1);
+    dest_prop = vstring_alloc(1);
+
+    vstream_fileno(VSTREAM_ERR) = 1;
+
+    while (get_buffer(buf, VSTREAM_IN, interactive) != VSTREAM_EOF) {
+       argv = argv_split(STR(buf), " \t\r\n");
+       if (argv->argc > 0 && argv->argv[0][0] != '#') {
+           msg_verbose = verbose_level;
+           for (ap = actions; ap->command != 0; ap++) {
+               if (strcmp(ap->command, argv->argv[0]) == 0) {
+                   if ((ap->flags & FLAG_NEED_CACHE) != 0 && scache == 0)
+                       msg_error("no session cache");
+                   else
+                       ap->action(argv);
+                   break;
+               }
+           }
+           msg_verbose = 0;
+           if (ap->command == 0)
+               msg_error("bad command: %s", argv->argv[0]);
+       }
+       argv_free(argv);
+    }
+    scache_free(scache);
+    vstring_free(endp_prop);
+    vstring_free(dest_prop);
+    vstring_free(buf);
+    exit(0);
+}
+
+#endif
diff --git a/postfix/src/global/scache.h b/postfix/src/global/scache.h
new file mode 100644 (file)
index 0000000..c00dd6d
--- /dev/null
@@ -0,0 +1,147 @@
+#ifndef _SCACHE_H_INCLUDED_
+#define _SCACHE_H_INCLUDED_
+
+/*++
+/* NAME
+/*      scache 3h
+/* SUMMARY
+/*      generic session cache API
+/* SYNOPSIS
+/*      #include <scache.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+  * Utility library.
+  */
+#include <vstring.h>
+
+typedef struct SCACHE SCACHE;
+
+ /*
+  * In order to cache a session, we specify:
+  * 
+  * - TTL for this session.
+  * 
+  * - File descriptor.
+  * 
+  * - Transport name and physical endpoint. The transport name must be included,
+  * because fall-back destinations can be transport-dependent, and routing
+  * for a given destination may be context dependent.
+  * 
+  * In the case of SMTP, the physical endpoint is the numerical IP address and
+  * TCP port.
+  * 
+  * - Application-specific endpoint properties.
+  * 
+  * In the case of SMTP, the properties specify the ESMTP features advertised by
+  * the server.
+  * 
+  * Note: with message delivery transports that have only one endpoint per
+  * logical destination, the above is the only information that needs to be
+  * maintained in a connection cache.
+  */
+typedef void (*SCACHE_SAVE_ENDP_FN) (SCACHE *, int, const char *, const char *, int);
+typedef int (*SCACHE_FIND_ENDP_FN) (SCACHE *, const char *, VSTRING *);
+
+ /*
+  * The following information is stored in order to make a binding from
+  * logical destination to physical destination. One logical destination can
+  * have multiple physical destinations (and one physical destination can
+  * have multiple sessions).
+  * 
+  * - TTL for this binding.
+  * 
+  * - Transport name and logical destination.
+  * 
+  * In the case of SMTP: the next-hop (NOT: fall-back) destination domain and
+  * destination network port. It is not useful to create a link for an
+  * address literal (but it is not harmful either: it just wastes a few
+  * bits). This information specifies the destination domain in [] if no MX
+  * lookup is done.
+  * 
+  * - Application-specific properties.
+  * 
+  * In case the of SMTP, the properties specify a) whether a physical endpoint
+  * is best mx host with respect to a logical or fall-back destination (this
+  * information is needed by the loop detection code in smtp_proto.c).
+  * 
+  * - Transport name and physical endpoint (see above).
+  * 
+  * Note 1: there is no need to store the binding's MX preference or equivalent
+  * with respect to the logical destination; a client should store only the
+  * first successful session for a given delivery request (otherwise the
+  * client would keep talking to a less preferred server after the cached
+  * connection for a more preferred server expires). After a failed delivery,
+  * the client should not attempt to cache follow-up sessions with less
+  * preferred endpoints under the same logical destination.
+  * 
+  * Note 2: logical to physical bindings exist independently from cached
+  * sessions. The two types of information have independent TTLs; creation
+  * and destruction proceed independently. Thus, a logical to physical
+  * binding can refer to an endpoint for which all cached connections are
+  * occupied or expired.
+  */
+typedef void (*SCACHE_SAVE_DEST_FN) (SCACHE *, int, const char *, const char *, const char *);
+typedef int (*SCACHE_FIND_DEST_FN) (SCACHE *, const char *, VSTRING *, VSTRING *);
+
+ /*
+  * Generic session cache object. Actual session cache objects are derived
+  * types with some additional, cache dependent, private information.
+  */
+struct SCACHE {
+    SCACHE_SAVE_ENDP_FN save_endp;
+    SCACHE_FIND_ENDP_FN find_endp;
+    SCACHE_SAVE_DEST_FN save_dest;
+    SCACHE_FIND_DEST_FN find_dest;
+    void    (*free) (struct SCACHE *);
+};
+
+extern SCACHE *scache_single_create(void);
+extern SCACHE *scache_clnt_create(const char *, int, int);
+extern SCACHE *scache_multi_create(void);
+
+#define scache_save_endp(scache, ttl, endp_label, endp_prop, fd) \
+    (scache)->save_endp((scache), (ttl), (endp_label), (endp_prop), (fd))
+#define scache_find_endp(scache, endp_label, endp_prop) \
+    (scache)->find_endp((scache), (endp_label), (endp_prop))
+#define scache_save_dest(scache, ttl, dest_label, dest_prop, endp_label) \
+    (scache)->save_dest((scache), (ttl), (dest_label), (dest_prop), (endp_label))
+#define scache_find_dest(scache, dest_label, dest_prop, endp_prop) \
+    (scache)->find_dest((scache), (dest_label), (dest_prop), (endp_prop))
+#define scache_free(scache) (scache)->free(scache)
+
+ /*
+  * Cache types.
+  */
+#define SCACHE_TYPE_SINGLE     1       /* single-instance cache */
+#define SCACHE_TYPE_CLIENT     2       /* session cache client */
+#define SCACHE_TYPE_MULTI      3       /* multi-instance cache */
+
+ /*
+  * Client-server protocol.
+  */
+#define SCACHE_REQ_FIND_ENDP   "find_endp"
+#define SCACHE_REQ_SAVE_ENDP   "save_endp"
+#define SCACHE_REQ_FIND_DEST   "find_dest"
+#define SCACHE_REQ_SAVE_DEST   "save_dest"
+
+ /*
+  * Session cache server status codes.
+  */
+#define SCACHE_STAT_OK         0       /* request completed successfully */
+#define SCACHE_STAT_BAD                1       /* malformed request */
+#define SCACHE_STAT_FAIL       2       /* request completed unsuccessfully */
+
+/* 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
+/*--*/
+
+#endif
diff --git a/postfix/src/global/scache_clnt.c b/postfix/src/global/scache_clnt.c
new file mode 100644 (file)
index 0000000..ff2f62c
--- /dev/null
@@ -0,0 +1,312 @@
+/*++
+/* NAME
+/*     scache_clnt 3
+/* SUMMARY
+/*     session cache manager client
+/* SYNOPSIS
+/*     #include <scache.h>
+/* DESCRIPTION
+/*     SCACHE *scache_clnt_create(server, idle_limit, ttl_limit)
+/*     const char *server;
+/*     int     idle_limit;
+/*     int     ttl_limit;
+/* DESCRIPTION
+/*     This module implements the client-side protocol of the
+/*     session cache service.
+/*
+/*     scache_clnt_create() creates a session cache service client.
+/*
+/*     Arguments:
+/* .IP server
+/*     The session cache service name.
+/* .IP idle_limit
+/*     Idle time after which the client disconnects.
+/* .IP ttl_limit
+/*     Upper bound on the time that a connection is allowed to persist.
+/* .IP endp_ttl
+/*     How long the session should be cached.  When information
+/*     expires it is purged automatically.
+/* DIAGNOSTICS
+/*     Fatal error: memory allocation problem;
+/*     panic: internal consistency failure.
+/* SEE ALSO
+/*     scache(3), generic session cache API
+/* LICENSE
+/* .ad
+/* .fi
+/*     The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/*     Wietse Venema
+/*     IBM T.J. Watson Research
+/*     P.O. Box 704
+/*     Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <errno.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+
+/*#define msg_verbose 1*/
+
+/* Global library. */
+
+#include <mail_proto.h>
+#include <mail_params.h>
+#include <clnt_stream.h>
+#include <scache.h>
+
+/* Application-specific. */
+
+ /*
+  * SCACHE_CLNT is a derived type from the SCACHE super-class.
+  */
+typedef struct {
+    SCACHE  scache[1];                 /* super-class */
+    CLNT_STREAM *clnt_stream;          /* client endpoint */
+} SCACHE_CLNT;
+
+#define STR(x) vstring_str(x)
+
+/* scache_clnt_save_endp - save endpoint */
+
+static void scache_clnt_save_endp(SCACHE *scache, int endp_ttl,
+                                         const char *endp_label,
+                                         const char *endp_prop, int fd)
+{
+    SCACHE_CLNT *sp = (SCACHE_CLNT *) scache;
+    const char *myname = "scache_clnt_save_endp";
+    VSTREAM *stream;
+    int     status;
+
+    if (msg_verbose)
+       msg_info("%s: endp=%s prop=%s fd=%d",
+                myname, endp_label, endp_prop, fd);
+
+    /*
+     * Sanity check.
+     */
+    if (endp_ttl <= 0)
+       msg_panic("%s: bad endp_ttl: %d", myname, endp_ttl);
+
+    /*
+     * Keep trying until we get a complete response. The session cache
+     * service is CPU bound and making the client asynchronous would just
+     * complicate the code.
+     */
+    for (;;) {
+       stream = clnt_stream_access(sp->clnt_stream);
+       errno = 0;
+       if (attr_print(stream, ATTR_FLAG_NONE,
+                      ATTR_TYPE_STR, MAIL_ATTR_REQ, SCACHE_REQ_SAVE_ENDP,
+                      ATTR_TYPE_NUM, MAIL_ATTR_TTL, endp_ttl,
+                      ATTR_TYPE_STR, MAIL_ATTR_LABEL, endp_label,
+                      ATTR_TYPE_STR, MAIL_ATTR_PROP, endp_prop,
+                      ATTR_TYPE_END) != 0
+           || vstream_fflush(stream)
+           || LOCAL_SEND_FD(vstream_fileno(stream), fd) < 0
+           || attr_scan(stream, ATTR_FLAG_STRICT,
+                        ATTR_TYPE_NUM, MAIL_ATTR_STATUS, &status,
+                        ATTR_TYPE_END) != 1) {
+           if (msg_verbose || (errno != EPIPE && errno != ENOENT))
+               msg_warn("problem talking to service %s: %m",
+                        VSTREAM_PATH(stream));
+       } else {
+           if (close(fd) < 0)
+               msg_warn("%s: close(%d): %m", myname, fd);
+           break;
+       }
+       sleep(1);                               /* XXX make configurable */
+       clnt_stream_recover(sp->clnt_stream);
+    }
+}
+
+/* scache_clnt_find_endp - look up cached session */
+
+static int scache_clnt_find_endp(SCACHE *scache, const char *endp_label,
+                                        VSTRING *endp_prop)
+{
+    SCACHE_CLNT *sp = (SCACHE_CLNT *) scache;
+    const char *myname = "scache_clnt_find_endp";
+    VSTREAM *stream;
+    int     status;
+    int     fd;
+
+    /*
+     * Keep trying until we get a complete response. The session cache
+     * service is CPU bound and making the client asynchronous would just
+     * complicate the code.
+     */
+    for (;;) {
+       stream = clnt_stream_access(sp->clnt_stream);
+       errno = 0;
+       if (attr_print(stream, ATTR_FLAG_NONE,
+                      ATTR_TYPE_STR, MAIL_ATTR_REQ, SCACHE_REQ_FIND_ENDP,
+                      ATTR_TYPE_STR, MAIL_ATTR_LABEL, endp_label,
+                      ATTR_TYPE_END) != 0
+           || vstream_fflush(stream)
+           || attr_scan(stream, ATTR_FLAG_STRICT,
+                        ATTR_TYPE_NUM, MAIL_ATTR_STATUS, &status,
+                        ATTR_TYPE_STR, MAIL_ATTR_PROP, endp_prop,
+                        ATTR_TYPE_END) != 2
+           || (status == 0
+               && (fd = LOCAL_RECV_FD(vstream_fileno(stream))) < 0)) {
+           if (msg_verbose || (errno != EPIPE && errno != ENOENT))
+               msg_warn("problem talking to service %s: %m",
+                        VSTREAM_PATH(stream));
+       } else {
+           break;
+       }
+       sleep(1);                               /* XXX make configurable */
+       clnt_stream_recover(sp->clnt_stream);
+    }
+
+    if (status == 0) {
+       if (msg_verbose)
+           msg_info("%s: endp=%s prop=%s fd=%d",
+                    myname, endp_label, STR(endp_prop), fd);
+       return (fd);
+    }
+    if (msg_verbose)
+       msg_info("%s: not found: %s", myname, endp_label);
+    return (-1);
+}
+
+/* scache_clnt_save_dest - create destination/endpoint association */
+
+static void scache_clnt_save_dest(SCACHE *scache, int dest_ttl,
+                                         const char *dest_label,
+                                         const char *dest_prop,
+                                         const char *endp_label)
+{
+    SCACHE_CLNT *sp = (SCACHE_CLNT *) scache;
+    const char *myname = "scache_clnt_save_dest";
+    VSTREAM *stream;
+    int     status;
+
+    if (msg_verbose)
+       msg_info("%s: dest_label=%s dest_prop=%s endp_label=%s",
+                myname, dest_label, dest_prop, endp_label);
+
+    /*
+     * Sanity check.
+     */
+    if (dest_ttl <= 0)
+       msg_panic("%s: bad dest_ttl: %d", myname, dest_ttl);
+
+    /*
+     * Keep trying until we get a complete response. The session cache
+     * service is CPU bound and making the client asynchronous would just
+     * complicate the code.
+     */
+    for (;;) {
+       stream = clnt_stream_access(sp->clnt_stream);
+       errno = 0;
+       if (attr_print(stream, ATTR_FLAG_NONE,
+                      ATTR_TYPE_STR, MAIL_ATTR_REQ, SCACHE_REQ_SAVE_DEST,
+                      ATTR_TYPE_NUM, MAIL_ATTR_TTL, dest_ttl,
+                      ATTR_TYPE_STR, MAIL_ATTR_LABEL, dest_label,
+                      ATTR_TYPE_STR, MAIL_ATTR_PROP, dest_prop,
+                      ATTR_TYPE_STR, MAIL_ATTR_LABEL, endp_label,
+                      ATTR_TYPE_END) != 0
+           || vstream_fflush(stream)
+           || attr_scan(stream, ATTR_FLAG_STRICT,
+                        ATTR_TYPE_NUM, MAIL_ATTR_STATUS, &status,
+                        ATTR_TYPE_END) != 1) {
+           if (msg_verbose || (errno != EPIPE && errno != ENOENT))
+               msg_warn("problem talking to service %s: %m",
+                        VSTREAM_PATH(stream));
+       } else {
+           break;
+       }
+       sleep(1);                               /* XXX make configurable */
+       clnt_stream_recover(sp->clnt_stream);
+    }
+}
+
+/* scache_clnt_find_dest - look up cached session */
+
+static int scache_clnt_find_dest(SCACHE *scache, const char *dest_label,
+                                        VSTRING *dest_prop,
+                                        VSTRING *endp_prop)
+{
+    SCACHE_CLNT *sp = (SCACHE_CLNT *) scache;
+    const char *myname = "scache_clnt_find_dest";
+    VSTREAM *stream;
+    int     status;
+    int     fd;
+
+    /*
+     * Keep trying until we get a complete response. The session cache
+     * service is CPU bound and making the client asynchronous would just
+     * complicate the code.
+     */
+    for (;;) {
+       stream = clnt_stream_access(sp->clnt_stream);
+       errno = 0;
+       if (attr_print(stream, ATTR_FLAG_NONE,
+                      ATTR_TYPE_STR, MAIL_ATTR_REQ, SCACHE_REQ_FIND_DEST,
+                      ATTR_TYPE_STR, MAIL_ATTR_LABEL, dest_label,
+                      ATTR_TYPE_END) != 0
+           || vstream_fflush(stream)
+           || attr_scan(stream, ATTR_FLAG_STRICT,
+                        ATTR_TYPE_NUM, MAIL_ATTR_STATUS, &status,
+                        ATTR_TYPE_STR, MAIL_ATTR_PROP, dest_prop,
+                        ATTR_TYPE_STR, MAIL_ATTR_PROP, endp_prop,
+                        ATTR_TYPE_END) != 3
+           || (status == 0
+               && (fd = LOCAL_RECV_FD(vstream_fileno(stream))) < 0)) {
+           if (msg_verbose || (errno != EPIPE && errno != ENOENT))
+               msg_warn("problem talking to service %s: %m",
+                        VSTREAM_PATH(stream));
+       } else {
+           break;
+       }
+       sleep(1);                               /* XXX make configurable */
+       clnt_stream_recover(sp->clnt_stream);
+    }
+
+    if (status == 0) {
+       if (msg_verbose)
+           msg_info("%s: dest=%s dest_prop=%s endp_prop=%s fd=%d",
+                    myname, dest_label, STR(dest_prop), STR(endp_prop), fd);
+       return (fd);
+    }
+    if (msg_verbose)
+       msg_info("%s: not found: %s", myname, dest_label);
+
+    return (-1);
+}
+
+/* scache_clnt_free - destroy cache */
+
+static void scache_clnt_free(SCACHE *scache)
+{
+    SCACHE_CLNT *sp = (SCACHE_CLNT *) scache;
+
+    clnt_stream_free(sp->clnt_stream);
+    myfree((char *) sp);
+}
+
+/* scache_clnt_create - initialize */
+
+SCACHE *scache_clnt_create(const char *server, int idle_limit, int ttl_limit)
+{
+    SCACHE_CLNT *sp = (SCACHE_CLNT *) mymalloc(sizeof(*sp));
+
+    sp->scache->save_endp = scache_clnt_save_endp;
+    sp->scache->find_endp = scache_clnt_find_endp;
+    sp->scache->save_dest = scache_clnt_save_dest;
+    sp->scache->find_dest = scache_clnt_find_dest;
+    sp->scache->free = scache_clnt_free;
+
+    sp->clnt_stream = clnt_stream_create(MAIL_CLASS_PRIVATE, server,
+                                        idle_limit, ttl_limit);
+
+    return (sp->scache);
+}
diff --git a/postfix/src/global/scache_multi.c b/postfix/src/global/scache_multi.c
new file mode 100644 (file)
index 0000000..2e7ca01
--- /dev/null
@@ -0,0 +1,477 @@
+/*++
+/* NAME
+/*     scache_multi 3
+/* SUMMARY
+/*     multi-session cache
+/* SYNOPSIS
+/*     #include <scache.h>
+/* DESCRIPTION
+/*     SCACHE *scache_multi_create()
+/* DESCRIPTION
+/*     This module implements an in-memory, multi-session cache.
+/*
+/*     scache_multi_create() instantiates a session cache that
+/*     stores multiple sessions.
+/* DIAGNOSTICS
+/*     Fatal error: memory allocation problem;
+/*     panic: internal consistency failure.
+/* SEE ALSO
+/*     scache(3), generic session cache API
+/* LICENSE
+/* .ad
+/* .fi
+/*     The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/*     Wietse Venema
+/*     IBM T.J. Watson Research
+/*     P.O. Box 704
+/*     Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <unistd.h>
+#include <stddef.h>                    /* offsetof() */
+
+/* Utility library. */
+
+#include <msg.h>
+#include <ring.h>
+#include <htable.h>
+#include <vstring.h>
+#include <mymalloc.h>
+#include <events.h>
+
+/*#define msg_verbose 1*/
+
+/* Global library. */
+
+#include <scache.h>
+
+/* Application-specific. */
+
+ /*
+  * SCACHE_MULTI is a derived type from the SCACHE super-class.
+  * 
+  * Each destination has an entry in the destination hash table, and each
+  * destination->endpoint binding is kept in a circular list under its
+  * destination hash table entry.
+  * 
+  * Likewise, each endpoint has an entry in the endpoint hash table, and each
+  * endpoint->session binding is kept in a circular list under its hash table
+  * entry.
+  * 
+  * We do not attempt to limit the number of destination or endpoint entries,
+  * nor do we attempt to limit the number of sessions. Doing so would require
+  * a write-through cache. Currently, the CTABLE cache module supports only
+  * read-through caching.
+  * 
+  * This is no problem because the number of cached destinations is limited by
+  * design. Sites that specify a wild-card domain pattern, and thus cache
+  * every session in recent history, may be in for a surprise.
+  */
+typedef struct {
+    SCACHE  scache[1];                 /* super-class */
+    HTABLE *dest_cache;                        /* destination->endpoint bindings */
+    HTABLE *endp_cache;                        /* endpoint->session bindings */
+} SCACHE_MULTI;
+
+ /*
+  * Storage for a destination or endpoint list head. The list head has
+  * references to its own hash table entry, so that we can remove a list when
+  * it becomes empty. List items are stored in a circular list under the list
+  * head.
+  */
+typedef struct {
+    RING    ring[1];                   /* circular list linkage */
+    HTABLE *parent_table;              /* parent info */
+    char   *parent_key;                        /* parent info */
+} SCACHE_MULTI_HEAD;
+
+#define RING_TO_MULTI_HEAD(p) RING_TO_APPL((p), SCACHE_MULTI_HEAD, ring)
+
+ /*
+  * Storage for a destination->endpoint binding. This is an element in a
+  * circular list, whose list head specifies the destination.
+  */
+typedef struct {
+    RING    ring[1];                   /* circular list linkage */
+    char   *endp_label;                        /* endpoint name */
+    char   *dest_prop;                 /* binding properties */
+} SCACHE_MULTI_DEST;
+
+#define RING_TO_MULTI_DEST(p) RING_TO_APPL((p), SCACHE_MULTI_DEST, ring)
+
+static void scache_multi_expire_dest(int, char *);
+
+ /*
+  * Storage for an endpoint->session binding. This is an element in a
+  * circular list, whose list head specifies the endpoint.
+  */
+typedef struct {
+    RING    ring[1];                   /* circular list linkage */
+    int     fd;                                /* cached session */
+    char   *endp_prop;                 /* binding properties */
+} SCACHE_MULTI_ENDP;
+
+#define RING_TO_MULTI_ENDP(p) RING_TO_APPL((p), SCACHE_MULTI_ENDP, ring)
+
+static void scache_multi_expire_endp(int, char *);
+
+ /*
+  * When deleting a circular list element, are we deleting the entire
+  * circular list, or are we removing a single list element. We need this
+  * distinction to avoid a re-entrancy problem between htable_delete() and
+  * htable_free().
+  */
+#define BOTTOM_UP      1               /* one item */
+#define TOP_DOWN       2               /* whole list */
+
+/* scache_multi_drop_endp - destroy endpoint->session binding */
+
+static void scache_multi_drop_endp(SCACHE_MULTI_ENDP *endp, int direction)
+{
+    const char *myname = "scache_multi_drop_endp";
+    SCACHE_MULTI_HEAD *head;
+
+    if (msg_verbose)
+       msg_info("%s: endp_prop=%s fd=%d", myname,
+                endp->endp_prop, endp->fd);
+
+    /*
+     * Stop the timer.
+     */
+    event_cancel_timer(scache_multi_expire_endp, (char *) endp);
+
+    /*
+     * In bottom-up mode, remove the list head from the endpoint hash when
+     * the list becomes empty. Otherwise, remove the endpoint->session
+     * binding from the list.
+     */
+    if (direction == BOTTOM_UP
+       && ring_pred(endp->ring) == ring_succ(endp->ring)) {
+       head = RING_TO_MULTI_HEAD(ring_pred(endp->ring));
+       htable_delete(head->parent_table, head->parent_key, myfree);
+    } else
+       ring_detach(endp->ring);
+
+    /*
+     * Destroy the endpoint->session binding.
+     */
+    if (endp->fd >= 0 && close(endp->fd) != 0)
+       msg_warn("%s: close(%d): %m", myname, endp->fd);
+    myfree(endp->endp_prop);
+
+    myfree((char *) endp);
+}
+
+/* scache_multi_expire_endp - event timer call-back */
+
+static void scache_multi_expire_endp(int unused_event, char *context)
+{
+    SCACHE_MULTI_ENDP *endp = (SCACHE_MULTI_ENDP *) context;
+
+    scache_multi_drop_endp(endp, BOTTOM_UP);
+}
+
+/* scache_multi_free_endp - hash table destructor call-back */
+
+static void scache_multi_free_endp(char *ptr)
+{
+    SCACHE_MULTI_HEAD *head = (SCACHE_MULTI_HEAD *) ptr;
+    SCACHE_MULTI_ENDP *endp;
+    RING   *ring;
+
+    /*
+     * Delete each endpoint->session binding in the list, then delete the
+     * list head. Note: this changes the list, so we must iterate carefully.
+     */
+    while ((ring = ring_succ(head->ring)) != head->ring) {
+       endp = RING_TO_MULTI_ENDP(ring);
+       scache_multi_drop_endp(endp, TOP_DOWN);
+    }
+    myfree((char *) head);
+}
+
+/* scache_multi_save_endp - save endpoint->session binding */
+
+static void scache_multi_save_endp(SCACHE *scache, int ttl,
+                                          const char *endp_label,
+                                          const char *endp_prop, int fd)
+{
+    const char *myname = "scache_multi_save_endp";
+    SCACHE_MULTI *sp = (SCACHE_MULTI *) scache;
+    SCACHE_MULTI_HEAD *head;
+    SCACHE_MULTI_ENDP *endp;
+
+    if (ttl < 0)
+       msg_panic("%s: bad ttl: %d", myname, ttl);
+
+    /*
+     * Look up or instantiate the list head with the endpoint name.
+     */
+    if ((head = (SCACHE_MULTI_HEAD *)
+        htable_find(sp->endp_cache, endp_label)) == 0) {
+       head = (SCACHE_MULTI_HEAD *) mymalloc(sizeof(*head));
+       ring_init(head->ring);
+       head->parent_table = sp->endp_cache;
+       head->parent_key =
+           htable_enter(sp->endp_cache, endp_label, (char *) head)->key;
+    }
+
+    /*
+     * Add the endpoint->session binding to the list. There can never be a
+     * duplicate, because each session must have a different file descriptor.
+     */
+    endp = (SCACHE_MULTI_ENDP *) mymalloc(sizeof(*endp));
+    endp->fd = fd;
+    endp->endp_prop = mystrdup(endp_prop);
+    ring_prepend(head->ring, endp->ring);
+
+    /*
+     * Make sure this binding will go away eventually.
+     */
+    event_request_timer(scache_multi_expire_endp, (char *) endp, ttl);
+
+    if (msg_verbose)
+       msg_info("%s: endp_label=%s -> endp_prop=%s fd=%d",
+                myname, endp_label, endp_prop, fd);
+}
+
+/* scache_multi_find_endp - look up session for named endpoint */
+
+static int scache_multi_find_endp(SCACHE *scache, const char *endp_label,
+                                         VSTRING *endp_prop)
+{
+    const char *myname = "scache_multi_find_endp";
+    SCACHE_MULTI *sp = (SCACHE_MULTI *) scache;
+    SCACHE_MULTI_HEAD *head;
+    SCACHE_MULTI_ENDP *endp;
+    RING   *ring;
+    int     fd;
+
+    /*
+     * Look up the list head with the endpoint name.
+     */
+    if ((head = (SCACHE_MULTI_HEAD *)
+        htable_find(sp->endp_cache, endp_label)) == 0) {
+       if (msg_verbose)
+           msg_info("%s: no endpoint cache: endp_label=%s",
+                    myname, endp_label);
+       return (-1);
+    }
+
+    /*
+     * Use the first available session. Remove the session from the cache
+     * because we're giving it to someone else.
+     */
+    if ((ring = ring_succ(head->ring)) != head->ring) {
+       endp = RING_TO_MULTI_ENDP(ring);
+       fd = endp->fd;
+       endp->fd = -1;
+       vstring_strcpy(endp_prop, endp->endp_prop);
+       if (msg_verbose)
+           msg_info("%s: found: endp_label=%s -> endp_prop=%s fd=%d",
+                    myname, endp_label, endp->endp_prop, fd);
+       scache_multi_drop_endp(endp, BOTTOM_UP);
+       return (fd);
+    }
+    if (msg_verbose)
+       msg_info("%s: not found: endp_label=%s", myname, endp_label);
+    return (-1);
+}
+
+/* scache_multi_drop_dest - delete destination->endpoint binding */
+
+static void scache_multi_drop_dest(SCACHE_MULTI_DEST *dest, int direction)
+{
+    const char *myname = "scache_multi_drop_dest";
+    SCACHE_MULTI_HEAD *head;
+
+    if (msg_verbose)
+       msg_info("%s: dest_prop=%s endp_label=%s",
+                myname, dest->dest_prop, dest->endp_label);
+
+    /*
+     * Stop the timer.
+     */
+    event_cancel_timer(scache_multi_expire_dest, (char *) dest);
+
+    /*
+     * In bottom-up mode, remove the list head from the destination hash when
+     * the list becomes empty. Otherwise, remove the destination->endpoint
+     * binding from the list.
+     */
+    if (direction == BOTTOM_UP
+       && ring_pred(dest->ring) == ring_succ(dest->ring)) {
+       head = RING_TO_MULTI_HEAD(ring_pred(dest->ring));
+       htable_delete(head->parent_table, head->parent_key, myfree);
+    } else
+       ring_detach(dest->ring);
+
+    /*
+     * Destroy the destination->endpoint binding.
+     */
+    myfree(dest->dest_prop);
+    myfree(dest->endp_label);
+
+    myfree((char *) dest);
+}
+
+/* scache_multi_expire_dest - event timer call-back */
+
+static void scache_multi_expire_dest(int unused_event, char *context)
+{
+    SCACHE_MULTI_DEST *dest = (SCACHE_MULTI_DEST *) context;
+
+    scache_multi_drop_dest(dest, BOTTOM_UP);
+}
+
+/* scache_multi_free_dest - hash table destructor call-back */
+
+static void scache_multi_free_dest(char *ptr)
+{
+    SCACHE_MULTI_HEAD *head = (SCACHE_MULTI_HEAD *) ptr;
+    SCACHE_MULTI_DEST *dest;
+    RING   *ring;
+
+    /*
+     * Delete each destination->endpoint binding in the list, then delete the
+     * list head. Note: this changes the list, so we must iterate carefully.
+     */
+    while ((ring = ring_succ(head->ring)) != head->ring) {
+       dest = RING_TO_MULTI_DEST(ring);
+       scache_multi_drop_dest(dest, TOP_DOWN);
+    }
+    myfree((char *) head);
+}
+
+/* scache_multi_save_dest - save destination->endpoint binding */
+
+static void scache_multi_save_dest(SCACHE *scache, int ttl,
+                                          const char *dest_label,
+                                          const char *dest_prop,
+                                          const char *endp_label)
+{
+    const char *myname = "scache_multi_save_dest";
+    SCACHE_MULTI *sp = (SCACHE_MULTI *) scache;
+    SCACHE_MULTI_HEAD *head;
+    SCACHE_MULTI_DEST *dest;
+    RING   *ring;
+    int     refresh = 0;
+
+    if (ttl < 0)
+       msg_panic("%s: bad ttl: %d", myname, ttl);
+
+    /*
+     * Look up or instantiate the list head with the destination name.
+     */
+    if ((head = (SCACHE_MULTI_HEAD *)
+        htable_find(sp->dest_cache, dest_label)) == 0) {
+       head = (SCACHE_MULTI_HEAD *) mymalloc(sizeof(*head));
+       ring_init(head->ring);
+       head->parent_table = sp->dest_cache;
+       head->parent_key =
+           htable_enter(sp->dest_cache, dest_label, (char *) head)->key;
+    }
+
+    /*
+     * Look up or instantiate the destination->endpoint binding. Update the
+     * expiration time if this destination->endpoint binding already exists.
+     */
+    RING_FOREACH(ring, head->ring) {
+       dest = RING_TO_MULTI_DEST(ring);
+       if (strcmp(dest->endp_label, endp_label) == 0
+           && strcmp(dest->dest_prop, dest_prop) == 0) {
+           refresh = 1;
+           break;
+       }
+    }
+    if (refresh == 0) {
+       dest = (SCACHE_MULTI_DEST *) mymalloc(sizeof(*dest));
+       dest->endp_label = mystrdup(endp_label);
+       dest->dest_prop = mystrdup(dest_prop);
+       ring_prepend(head->ring, dest->ring);
+    }
+
+    /*
+     * Make sure this binding will go away eventually.
+     */
+    event_request_timer(scache_multi_expire_dest, (char *) dest, ttl);
+
+    if (msg_verbose)
+       msg_info("%s: dest_label=%s -> dest_prop=%s endp_label=%s%s",
+                myname, dest_label, dest_prop, endp_label,
+                refresh ? " (refreshed)" : "");
+}
+
+/* scache_multi_find_dest - look up session for named destination */
+
+static int scache_multi_find_dest(SCACHE *scache, const char *dest_label,
+                                         VSTRING *dest_prop,
+                                         VSTRING *endp_prop)
+{
+    const char *myname = "scache_multi_find_dest";
+    SCACHE_MULTI *sp = (SCACHE_MULTI *) scache;
+    SCACHE_MULTI_HEAD *head;
+    SCACHE_MULTI_DEST *dest;
+    RING   *ring;
+    int     fd;
+
+    /*
+     * Look up the list head with the destination name.
+     */
+    if ((head = (SCACHE_MULTI_HEAD *)
+        htable_find(sp->dest_cache, dest_label)) == 0) {
+       if (msg_verbose)
+           msg_info("%s: no destination cache: dest_label=%s",
+                    myname, dest_label);
+       return (-1);
+    }
+
+    /*
+     * Search endpoints for the first available session.
+     */
+    RING_FOREACH(ring, head->ring) {
+       dest = RING_TO_MULTI_DEST(ring);
+       fd = scache_multi_find_endp(scache, dest->endp_label, endp_prop);
+       if (fd >= 0) {
+           vstring_strcpy(dest_prop, dest->dest_prop);
+           return (fd);
+       }
+    }
+    if (msg_verbose)
+       msg_info("%s: not found: dest_label=%s", myname, dest_label);
+    return (-1);
+}
+
+/* scache_multi_free - destroy single-element cache object */
+
+static void scache_multi_free(SCACHE *scache)
+{
+    SCACHE_MULTI *sp = (SCACHE_MULTI *) scache;
+
+    htable_free(sp->dest_cache, scache_multi_free_dest);
+    htable_free(sp->endp_cache, scache_multi_free_endp);
+
+    myfree((char *) sp);
+}
+
+/* scache_multi_create - initialize */
+
+SCACHE *scache_multi_create(void)
+{
+    SCACHE_MULTI *sp = (SCACHE_MULTI *) mymalloc(sizeof(*sp));
+
+    sp->scache->save_endp = scache_multi_save_endp;
+    sp->scache->find_endp = scache_multi_find_endp;
+    sp->scache->save_dest = scache_multi_save_dest;
+    sp->scache->find_dest = scache_multi_find_dest;
+    sp->scache->free = scache_multi_free;
+
+    sp->dest_cache = htable_create(1);
+    sp->endp_cache = htable_create(1);
+
+    return (sp->scache);
+}
diff --git a/postfix/src/global/scache_multi.in b/postfix/src/global/scache_multi.in
new file mode 100644 (file)
index 0000000..1a65650
--- /dev/null
@@ -0,0 +1,52 @@
+# Initialize
+
+verbose 0
+cache_type multi
+
+# Destination name space collision test
+
+save_dest 2 a_dest a_prop b_endp
+sleep 1
+save_dest 2 a_dest a_prop b_endp
+sleep 1
+save_dest 2 a_dest a_prop b_endp
+sleep 2
+
+# Another destination name space collision test
+
+save_dest 2 a_dest a_prop b_endp
+sleep 1
+save_dest 2 a_dest a_prop2 b_endp
+sleep 1
+save_dest 2 a_dest a_prop2 b_endp2
+sleep 2
+
+# Endpoint name space collision test
+
+save_endp 2 b_endp b_prop 12
+save_endp 2 b_endp b_prop 13
+sleep 3
+
+# Combined destiation and endpoint collision test with lookup
+
+save_dest 2 a_dest a_prop b_endp
+save_dest 2 a_dest a_prop2 b_endp
+save_dest 2 a_dest a_prop2 b_endp2
+save_endp 2 b_endp b_prop 12
+save_endp 2 b_endp b_prop 13
+find_dest a_dest
+find_dest a_dest
+find_dest a_dest
+
+# Another combined destiation and endpoint collision test with lookup
+
+save_endp 2 b_endp2 b_prop 12
+save_endp 2 b_endp2 b_prop 13
+save_endp 2 b_endp2 b_prop 14
+find_dest a_dest
+find_dest a_dest
+find_dest a_dest
+find_dest a_dest
+
+# Let the exit handler clean up the destiation->endpoint bindings.
+
diff --git a/postfix/src/global/scache_multi.ref b/postfix/src/global/scache_multi.ref
new file mode 100644 (file)
index 0000000..8bf63cb
--- /dev/null
@@ -0,0 +1,101 @@
+>>> # Initialize
+>>> 
+>>> verbose 0
+>>> cache_type multi
+>>> 
+>>> # Destination name space collision test
+>>> 
+>>> save_dest 2 a_dest a_prop b_endp
+unknown: scache_multi_save_dest: dest_label=a_dest -> dest_prop=a_prop endp_label=b_endp
+>>> sleep 1
+>>> save_dest 2 a_dest a_prop b_endp
+unknown: scache_multi_save_dest: dest_label=a_dest -> dest_prop=a_prop endp_label=b_endp (refreshed)
+>>> sleep 1
+>>> save_dest 2 a_dest a_prop b_endp
+unknown: scache_multi_save_dest: dest_label=a_dest -> dest_prop=a_prop endp_label=b_endp (refreshed)
+>>> sleep 2
+unknown: scache_multi_drop_dest: dest_prop=a_prop endp_label=b_endp
+>>> 
+>>> # Another destination name space collision test
+>>> 
+>>> save_dest 2 a_dest a_prop b_endp
+unknown: scache_multi_save_dest: dest_label=a_dest -> dest_prop=a_prop endp_label=b_endp
+>>> sleep 1
+>>> save_dest 2 a_dest a_prop2 b_endp
+unknown: scache_multi_save_dest: dest_label=a_dest -> dest_prop=a_prop2 endp_label=b_endp
+>>> sleep 1
+unknown: scache_multi_drop_dest: dest_prop=a_prop endp_label=b_endp
+>>> save_dest 2 a_dest a_prop2 b_endp2
+unknown: scache_multi_save_dest: dest_label=a_dest -> dest_prop=a_prop2 endp_label=b_endp2
+>>> sleep 2
+unknown: scache_multi_drop_dest: dest_prop=a_prop2 endp_label=b_endp
+unknown: scache_multi_drop_dest: dest_prop=a_prop2 endp_label=b_endp2
+>>> 
+>>> # Endpoint name space collision test
+>>> 
+>>> save_endp 2 b_endp b_prop 12
+unknown: scache_multi_save_endp: endp_label=b_endp -> endp_prop=b_prop fd=12
+>>> save_endp 2 b_endp b_prop 13
+unknown: scache_multi_save_endp: endp_label=b_endp -> endp_prop=b_prop fd=13
+>>> sleep 3
+unknown: scache_multi_drop_endp: endp_prop=b_prop fd=12
+unknown: scache_multi_drop_endp: endp_prop=b_prop fd=13
+>>> 
+>>> # Combined destiation and endpoint collision test with lookup
+>>> 
+>>> save_dest 2 a_dest a_prop b_endp
+unknown: scache_multi_save_dest: dest_label=a_dest -> dest_prop=a_prop endp_label=b_endp
+>>> save_dest 2 a_dest a_prop2 b_endp
+unknown: scache_multi_save_dest: dest_label=a_dest -> dest_prop=a_prop2 endp_label=b_endp
+>>> save_dest 2 a_dest a_prop2 b_endp2
+unknown: scache_multi_save_dest: dest_label=a_dest -> dest_prop=a_prop2 endp_label=b_endp2
+>>> save_endp 2 b_endp b_prop 12
+unknown: scache_multi_save_endp: endp_label=b_endp -> endp_prop=b_prop fd=12
+>>> save_endp 2 b_endp b_prop 13
+unknown: scache_multi_save_endp: endp_label=b_endp -> endp_prop=b_prop fd=13
+>>> find_dest a_dest
+unknown: scache_multi_find_endp: found: endp_label=b_endp -> endp_prop=b_prop fd=12
+unknown: scache_multi_drop_endp: endp_prop=b_prop fd=-1
+>>> find_dest a_dest
+unknown: scache_multi_find_endp: found: endp_label=b_endp -> endp_prop=b_prop fd=13
+unknown: scache_multi_drop_endp: endp_prop=b_prop fd=-1
+>>> find_dest a_dest
+unknown: scache_multi_find_endp: no endpoint cache: endp_label=b_endp
+unknown: scache_multi_find_endp: no endpoint cache: endp_label=b_endp
+unknown: scache_multi_find_endp: no endpoint cache: endp_label=b_endp2
+unknown: scache_multi_find_dest: not found: dest_label=a_dest
+>>> 
+>>> # Another combined destiation and endpoint collision test with lookup
+>>> 
+>>> save_endp 2 b_endp2 b_prop 12
+unknown: scache_multi_save_endp: endp_label=b_endp2 -> endp_prop=b_prop fd=12
+>>> save_endp 2 b_endp2 b_prop 13
+unknown: scache_multi_save_endp: endp_label=b_endp2 -> endp_prop=b_prop fd=13
+>>> save_endp 2 b_endp2 b_prop 14
+unknown: scache_multi_save_endp: endp_label=b_endp2 -> endp_prop=b_prop fd=14
+>>> find_dest a_dest
+unknown: scache_multi_find_endp: no endpoint cache: endp_label=b_endp
+unknown: scache_multi_find_endp: no endpoint cache: endp_label=b_endp
+unknown: scache_multi_find_endp: found: endp_label=b_endp2 -> endp_prop=b_prop fd=12
+unknown: scache_multi_drop_endp: endp_prop=b_prop fd=-1
+>>> find_dest a_dest
+unknown: scache_multi_find_endp: no endpoint cache: endp_label=b_endp
+unknown: scache_multi_find_endp: no endpoint cache: endp_label=b_endp
+unknown: scache_multi_find_endp: found: endp_label=b_endp2 -> endp_prop=b_prop fd=13
+unknown: scache_multi_drop_endp: endp_prop=b_prop fd=-1
+>>> find_dest a_dest
+unknown: scache_multi_find_endp: no endpoint cache: endp_label=b_endp
+unknown: scache_multi_find_endp: no endpoint cache: endp_label=b_endp
+unknown: scache_multi_find_endp: found: endp_label=b_endp2 -> endp_prop=b_prop fd=14
+unknown: scache_multi_drop_endp: endp_prop=b_prop fd=-1
+>>> find_dest a_dest
+unknown: scache_multi_find_endp: no endpoint cache: endp_label=b_endp
+unknown: scache_multi_find_endp: no endpoint cache: endp_label=b_endp
+unknown: scache_multi_find_endp: no endpoint cache: endp_label=b_endp2
+unknown: scache_multi_find_dest: not found: dest_label=a_dest
+>>> 
+>>> # Let the exit handler clean up the destiation->endpoint bindings.
+>>> 
+unknown: scache_multi_drop_dest: dest_prop=a_prop endp_label=b_endp
+unknown: scache_multi_drop_dest: dest_prop=a_prop2 endp_label=b_endp
+unknown: scache_multi_drop_dest: dest_prop=a_prop2 endp_label=b_endp2
diff --git a/postfix/src/global/scache_single.c b/postfix/src/global/scache_single.c
new file mode 100644 (file)
index 0000000..c6ad17e
--- /dev/null
@@ -0,0 +1,299 @@
+/*++
+/* NAME
+/*     scache_single 3
+/* SUMMARY
+/*     single-item session cache
+/* SYNOPSIS
+/*     #include <scache.h>
+/* DESCRIPTION
+/*     SCACHE *scache_single_create()
+/* DESCRIPTION
+/*     This module implements an in-memory, single-session cache.
+/*
+/*     scache_single_create() creates a session cache instance
+/*     that stores a single session.
+/* DIAGNOSTICS
+/*     Fatal error: memory allocation problem;
+/*     panic: internal consistency failure.
+/* SEE ALSO
+/*     scache(3), generic session cache API
+/* LICENSE
+/* .ad
+/* .fi
+/*     The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/*     Wietse Venema
+/*     IBM T.J. Watson Research
+/*     P.O. Box 704
+/*     Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <unistd.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <vstring.h>
+#include <mymalloc.h>
+#include <events.h>
+
+/*#define msg_verbose 1*/
+
+/* Global library. */
+
+#include <scache.h>
+
+/* Application-specific. */
+
+ /*
+  * Data structure for one saved connection. It is left up to the application
+  * to serialize attributes upon passivation, and to de-serialize them upon
+  * re-activation.
+  */
+typedef struct {
+    VSTRING *endp_label;               /* physical endpoint name */
+    VSTRING *endp_prop;                        /* endpoint properties, serialized */
+    int     fd;                                /* the session */
+} SCACHE_SINGLE_ENDP;
+
+ /*
+  * Data structure for a logical name to physical endpoint binding. It is
+  * left up to the application to serialize attributes upon passivation, and
+  * to de-serialize then upon re-activation.
+  */
+typedef struct {
+    VSTRING *dest_label;               /* logical destination name */
+    VSTRING *dest_prop;                        /* binding properties, serialized */
+    VSTRING *endp_label;               /* physical endpoint name */
+} SCACHE_SINGLE_DEST;
+
+ /*
+  * SCACHE_SINGLE is a derived type from the SCACHE super-class.
+  */
+typedef struct {
+    SCACHE  scache[1];                 /* super-class */
+    SCACHE_SINGLE_ENDP endp;           /* one cached session */
+    SCACHE_SINGLE_DEST dest;           /* one cached binding */
+} SCACHE_SINGLE;
+
+static void scache_single_expire_endp(int, char *);
+static void scache_single_expire_dest(int, char *);
+
+#define SCACHE_SINGLE_ENDP_BUSY(sp)    (VSTRING_LEN(sp->endp.endp_label) > 0)
+#define SCACHE_SINGLE_DEST_BUSY(sp)    (VSTRING_LEN(sp->dest.dest_label) > 0)
+
+#define STR(x) vstring_str(x)
+
+/* scache_single_free_endp - discard endpoint */
+
+static void scache_single_free_endp(SCACHE_SINGLE *sp)
+{
+    const char *myname = "scache_single_free_endp";
+
+    if (msg_verbose)
+       msg_info("%s: %s", myname, STR(sp->endp.endp_label));
+
+    event_cancel_timer(scache_single_expire_endp, (char *) sp);
+    if (sp->endp.fd >= 0 && close(sp->endp.fd) < 0)
+       msg_warn("close session endpoint %s: %m", STR(sp->endp.endp_label));
+    VSTRING_RESET(sp->endp.endp_label);
+    VSTRING_TERMINATE(sp->endp.endp_label);
+    VSTRING_RESET(sp->endp.endp_prop);
+    VSTRING_TERMINATE(sp->endp.endp_prop);
+    sp->endp.fd = -1;
+}
+
+/* scache_single_expire_endp - discard expired session */
+
+static void scache_single_expire_endp(int unused_event, char *context)
+{
+    SCACHE_SINGLE *sp = (SCACHE_SINGLE *) context;
+
+    scache_single_free_endp(sp);
+}
+
+/* scache_single_save_endp - save endpoint */
+
+static void scache_single_save_endp(SCACHE *scache, int endp_ttl,
+                                           const char *endp_label,
+                                           const char *endp_prop, int fd)
+{
+    SCACHE_SINGLE *sp = (SCACHE_SINGLE *) scache;
+    const char *myname = "scache_single_save_endp";
+
+    if (endp_ttl <= 0)
+       msg_panic("%s: bad endp_ttl: %d", myname, endp_ttl);
+
+    if (SCACHE_SINGLE_ENDP_BUSY(sp))
+       scache_single_free_endp(sp);            /* dump the cached fd */
+
+    vstring_strcpy(sp->endp.endp_label, endp_label);
+    vstring_strcpy(sp->endp.endp_prop, endp_prop);
+    sp->endp.fd = fd;
+    event_request_timer(scache_single_expire_endp, (char *) sp, endp_ttl);
+
+    if (msg_verbose)
+       msg_info("%s: %s fd=%d", myname, endp_label, fd);
+}
+
+/* scache_single_find_endp - look up cached session */
+
+static int scache_single_find_endp(SCACHE *scache, const char *endp_label,
+                                          VSTRING *endp_prop)
+{
+    SCACHE_SINGLE *sp = (SCACHE_SINGLE *) scache;
+    const char *myname = "scache_single_find_endp";
+    int     fd;
+
+    if (!SCACHE_SINGLE_ENDP_BUSY(sp)) {
+       if (msg_verbose)
+           msg_info("%s: no endpoint cache: %s", myname, endp_label);
+       return (-1);
+    }
+    if (strcmp(STR(sp->endp.endp_label), endp_label) == 0) {
+       vstring_strcpy(endp_prop, STR(sp->endp.endp_prop));
+       fd = sp->endp.fd;
+       sp->endp.fd = -1;
+       scache_single_free_endp(sp);
+       if (msg_verbose)
+           msg_info("%s: found: %s fd=%d", myname, endp_label, fd);
+       return (fd);
+    }
+    if (msg_verbose)
+       msg_info("%s: not found: %s", myname, endp_label);
+    return (-1);
+}
+
+/* scache_single_free_dest - discard destination/endpoint association */
+
+static void scache_single_free_dest(SCACHE_SINGLE *sp)
+{
+    const char *myname = "scache_single_free_dest";
+
+    if (msg_verbose)
+       msg_info("%s: %s -> %s", myname, STR(sp->dest.dest_label),
+                STR(sp->dest.endp_label));
+
+    event_cancel_timer(scache_single_expire_dest, (char *) sp);
+    VSTRING_RESET(sp->dest.dest_label);
+    VSTRING_TERMINATE(sp->dest.dest_label);
+    VSTRING_RESET(sp->dest.dest_prop);
+    VSTRING_TERMINATE(sp->dest.dest_prop);
+    VSTRING_RESET(sp->dest.endp_label);
+    VSTRING_TERMINATE(sp->dest.endp_label);
+}
+
+/* scache_single_expire_dest - discard expired destination/endpoint binding */
+
+static void scache_single_expire_dest(int unused_event, char *context)
+{
+    SCACHE_SINGLE *sp = (SCACHE_SINGLE *) context;
+
+    scache_single_free_dest(sp);
+}
+
+/* scache_single_save_dest - create destination/endpoint association */
+
+static void scache_single_save_dest(SCACHE *scache, int dest_ttl,
+                                           const char *dest_label,
+                                           const char *dest_prop,
+                                           const char *endp_label)
+{
+    SCACHE_SINGLE *sp = (SCACHE_SINGLE *) scache;
+    const char *myname = "scache_single_save_dest";
+    int     refresh;
+
+    if (dest_ttl <= 0)
+       msg_panic("%s: bad dest_ttl: %d", myname, dest_ttl);
+
+    /*
+     * Optimize: reset timer only, if nothing has changed.
+     */
+    refresh =
+       (SCACHE_SINGLE_DEST_BUSY(sp)
+        && strcmp(STR(sp->dest.dest_label), dest_label) == 0
+        && strcmp(STR(sp->dest.dest_prop), dest_prop) == 0
+        && strcmp(STR(sp->dest.endp_label), endp_label) == 0);
+
+    if (refresh == 0) {
+       vstring_strcpy(sp->dest.dest_label, dest_label);
+       vstring_strcpy(sp->dest.dest_prop, dest_prop);
+       vstring_strcpy(sp->dest.endp_label, endp_label);
+    }
+    event_request_timer(scache_single_expire_dest, (char *) sp, dest_ttl);
+
+    if (msg_verbose)
+       msg_info("%s: %s -> %s%s", myname, dest_label, endp_label,
+                refresh ? " (refreshed)" : "");
+}
+
+/* scache_single_find_dest - look up cached session */
+
+static int scache_single_find_dest(SCACHE *scache, const char *dest_label,
+                                    VSTRING *dest_prop, VSTRING *endp_prop)
+{
+    SCACHE_SINGLE *sp = (SCACHE_SINGLE *) scache;
+    const char *myname = "scache_single_find_dest";
+    int     fd;
+
+    if (!SCACHE_SINGLE_DEST_BUSY(sp)) {
+       if (msg_verbose)
+           msg_info("%s: no destination cache: %s", myname, dest_label);
+       return (-1);
+    }
+    if (strcmp(STR(sp->dest.dest_label), dest_label) == 0) {
+       if (msg_verbose)
+           msg_info("%s: found: %s", myname, dest_label);
+       if ((fd = scache_single_find_endp(scache, STR(sp->dest.endp_label), endp_prop)) >= 0) {
+           vstring_strcpy(dest_prop, STR(sp->dest.dest_prop));
+           return (fd);
+       }
+    }
+    if (msg_verbose)
+       msg_info("%s: not found: %s", myname, dest_label);
+    return (-1);
+}
+
+/* scache_single_free - destroy single-element cache object */
+
+static void scache_single_free(SCACHE *scache)
+{
+    SCACHE_SINGLE *sp = (SCACHE_SINGLE *) scache;
+
+    vstring_free(sp->endp.endp_label);
+    vstring_free(sp->endp.endp_prop);
+    if (sp->endp.fd >= 0)
+       close(sp->endp.fd);
+
+    vstring_free(sp->dest.dest_label);
+    vstring_free(sp->dest.dest_prop);
+    vstring_free(sp->dest.endp_label);
+
+    myfree((char *) sp);
+}
+
+/* scache_single_create - initialize */
+
+SCACHE *scache_single_create(void)
+{
+    SCACHE_SINGLE *sp = (SCACHE_SINGLE *) mymalloc(sizeof(*sp));
+
+    sp->scache->save_endp = scache_single_save_endp;
+    sp->scache->find_endp = scache_single_find_endp;
+    sp->scache->save_dest = scache_single_save_dest;
+    sp->scache->find_dest = scache_single_find_dest;
+    sp->scache->free = scache_single_free;
+
+    sp->endp.endp_label = vstring_alloc(10);
+    sp->endp.endp_prop = vstring_alloc(10);
+    sp->endp.fd = -1;
+
+    sp->dest.dest_label = vstring_alloc(10);
+    sp->dest.dest_prop = vstring_alloc(10);
+    sp->dest.endp_label = vstring_alloc(10);
+
+    return (sp->scache);
+}
index 9b3ca14d8eb27b1d1649aa2a0fc61cfffc3ab2f8..84c822c4505a7b3393058ef9f95ca25c5de3c2cf 100644 (file)
@@ -51,7 +51,7 @@ 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 *"\([^"]*\)".*/{' \
+           $(CC) -E $(DEFS) $(INCL) $$i | grep -v '[<>]' | 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
index 732b788d910413772b45ccddb2c35c229b9f3798..60b4c78dc6d08e90c732b01f87d57b13a068ceb9 100644 (file)
@@ -53,7 +53,7 @@ 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 *"\([^"]*\)".*/{' \
+           $(CC) -E $(DEFS) $(INCL) $$i | grep -v '[<>]' | 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
@@ -96,6 +96,7 @@ command.o: ../../include/vstring.h
 command.o: ../../include/vbuf.h
 command.o: ../../include/vstream.h
 command.o: ../../include/argv.h
+command.o: ../../include/mac_parse.h
 command.o: ../../include/defer.h
 command.o: ../../include/bounce.h
 command.o: ../../include/deliver_request.h
index 7a5a4bab451bce5829213a994a88635f9aa762dc..c444c99195ca0fd43e28309144ca3657048f7f20 100644 (file)
@@ -72,7 +72,7 @@ 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 *"\([^"]*\)".*/{' \
+           $(CC) -E $(DEFS) $(INCL) $$i | grep -v '[<>]' | 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
index 714a9bc177ce1ef5ccd0f703d9e9426dd50ee41d..63b9590c493730fcbe652a62b08ff8f896f0881c 100644 (file)
@@ -51,7 +51,7 @@ 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 *"\([^"]*\)".*/{' \
+           $(CC) -E $(DEFS) $(INCL) $$i | grep -v '[<>]' | 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
index 405f1a30151ef0519f84b10eb9f2ae8917034b38..8d832721e48d7c13cfba8cb0360a4c8b78472d41 100644 (file)
@@ -46,7 +46,7 @@ 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 *"\([^"]*\)".*/{' \
+           $(CC) -E $(DEFS) $(INCL) $$i | grep -v '[<>]' | 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
index 0add8c3d0437a8403c9207e18fd93e6f13969f37..dd7066f10f96fc8d40b6b3c39fe6be4b021b9df8 100644 (file)
@@ -46,7 +46,7 @@ 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 *"\([^"]*\)".*/{' \
+           $(CC) -E $(DEFS) $(INCL) $$i | grep -v '[<>]' | 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
index dcb52da65168477cfad6c84f81d1f1a5cccfc05c..1b721772659c2b3e2bd13e7617d7a721c8f50fbb 100644 (file)
 /* .fi
 /*     The external command attributes are given in the \fBmaster.cf\fR
 /*     file at the end of a service definition.  The syntax is as follows:
+/* .IP "\fBdirectory=\fIpathname\fR (optional, default: \fB$queue_directory\fR)"
+/*     Change to the named directory before executing the external command.
+/*     Delivery is deferred in case of failure.
+/* .sp
+/*     This feature is available as of Postfix 2.2.
 /* .IP "\fBeol=string\fR (optional, default: \fB\en\fR)"
 /*     The output record delimiter. Typically one would use either
 /*     \fB\er\en\fR or \fB\en\fR. The usual C-style backslash escape
 /*     sequences are recognized: \fB\ea \eb \ef \en \er \et \ev
-/*     \e\fIoctal\fR and \fB\e\e\fR.
+/*     \e\fIddd\fR (up to three octal digits) and \fB\e\e\fR.
 /* .IP "\fBflags=BDFORhqu.>\fR (optional)"
 /*     Optional message processing flags. By default, a message is
 /*     copied unchanged.
 /*     the left of the right-most \fB@\fR character) to lower case.
 /*     This is recommended for delivery via \fBUUCP\fR.
 /* .IP \fB.\fR
-/*     Prepend \fB.\fR to lines starting with "\fB.\fR". This is needed
+/*     Prepend "\fB.\fR" to lines starting with "\fB.\fR". This is needed
 /*     by, for example, \fBBSMTP\fR software.
 /* .IP \fB>\fR
-/*     Prepend \fB>\fR to lines starting with "\fBFrom \fR". This is expected
+/*     Prepend "\fB>\fR" to lines starting with "\fBFrom \fR". This is expected
 /*     by, for example, \fBUUCP\fR software.
 /* .RE
 /* .IP "\fBsize\fR=\fIsize_limit\fR (optional)"
 /*     In addition to the form ${\fIname\fR}, the forms $\fIname\fR and
 /*     $(\fIname\fR) are also recognized.  Specify \fB$$\fR where a single
 /*     \fB$\fR is wanted.
-/* .PP
-/*     Available in Postfix 2.2 and later:
-/* .IP "\fBdirectory=\fIpathname\fR (optional)"
-/*     Change to the specified directory before executing the command.
-/*     Failure causes mail delivery to be deferred.
 /* DIAGNOSTICS
 /*     Command exit status codes are expected to
 /*     follow the conventions defined in <\fBsysexits.h\fR>.
index a7b236e916a557ad2111a27088339056c72be19c..5049eb1b414c99b05d4d94554e6ffeae981e18f5 100644 (file)
@@ -70,7 +70,7 @@ 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 *"\([^"]*\)".*/{' \
+           $(CC) -E $(DEFS) $(INCL) $$i | grep -v '[<>]' | 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
index e1ba3bdc9aa5d6626e735f416e050165af726ea6..bacd2dd16bdfcaebf299c960a8f1215778a0b307 100644 (file)
@@ -46,7 +46,7 @@ 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 *"\([^"]*\)".*/{' \
+           $(CC) -E $(DEFS) $(INCL) $$i | grep -v '[<>]' | 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
index 56ec89628fa22fd68f562f0ba2e0c6efe21319f9..2910a674ccec143421718cf10ab6678780a8bb3c 100644 (file)
@@ -60,7 +60,7 @@ 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 *"\([^"]*\)".*/{' \
+           $(CC) -E $(DEFS) $(INCL) $$i | grep -v '[<>]' | 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
index cb914ad96b28dfc80fe381ea8157604e3cd53c0e..e6946e11b00d372835996f09314af03a288e5e74 100644 (file)
@@ -46,7 +46,7 @@ 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 *"\([^"]*\)".*/{' \
+           $(CC) -E $(DEFS) $(INCL) $$i | grep -v '[<>]' | 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
index ac53549d39713c753552f5ff4b3b5b105cf1403a..4249375d6afd10fe3ce49a674593ed07c31882c5 100644 (file)
@@ -50,7 +50,7 @@ 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 *"\([^"]*\)".*/{' \
+           $(CC) -E $(DEFS) $(INCL) $$i | grep -v '[<>]' | 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
index 38c0fba9a09051acc20c8c19b1bddfabb81978d6..3e63ead93b3ab082e43ef7748a12cb04aa51fd76 100644 (file)
@@ -46,7 +46,7 @@ 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 *"\([^"]*\)".*/{' \
+           $(CC) -E $(DEFS) $(INCL) $$i | grep -v '[<>]' | 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
index 4e4ea3ad0549327df26babaac651fcbfb00d98d7..04c283f6cd5e94d006617d0804e69ae65af52cba 100644 (file)
@@ -46,7 +46,7 @@ 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 *"\([^"]*\)".*/{' \
+           $(CC) -E $(DEFS) $(INCL) $$i | grep -v '[<>]' | 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
index 0bae7d356590612eb9a13c342a57fd415d4b0368..e914094f16c97cff828b7603b5b5f7d890aa3e27 100644 (file)
@@ -50,7 +50,7 @@ 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 *"\([^"]*\)".*/{' \
+           $(CC) -E $(DEFS) $(INCL) $$i | grep -v '[<>]' | 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
index 61d03f9760331deceb55ada565aa93fd52fe9087..00e1c94c7bf7fc76ba9a45b71962a2c1837cbbe1 100644 (file)
@@ -70,7 +70,7 @@ 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 *"\([^"]*\)".*/{' \
+           $(CC) -E $(DEFS) $(INCL) $$i | grep -v '[<>]' | 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
index ba9e66f163834e32fe37f9df77d6713f29d7de57..a90fe8d1dd34cd5d0338220e44532e21c13aa915 100644 (file)
@@ -46,7 +46,7 @@ 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 *"\([^"]*\)".*/{' \
+           $(CC) -E $(DEFS) $(INCL) $$i | grep -v '[<>]' | 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
index 1b9d8079d9a51c0598c61c4ff401465f0f2d830d..98eb5f61a30aa84e4310e3c5c44b9d45d0771fcf 100644 (file)
@@ -46,7 +46,7 @@ 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 *"\([^"]*\)".*/{' \
+           $(CC) -E $(DEFS) $(INCL) $$i | grep -v '[<>]' | 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
index e42f4389c6500c2f54ad50f37c75b90033a1b518..42c9148fd06f988ae33df2d84333807a74f7d3fc 100644 (file)
@@ -46,7 +46,7 @@ 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 *"\([^"]*\)".*/{' \
+           $(CC) -E $(DEFS) $(INCL) $$i | grep -v '[<>]' | 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
index 9fd75b80febabaa73a95a0443821db9c0fee95d9..3239af2855d31dad990b44b34f33edd65c58286f 100644 (file)
@@ -53,7 +53,7 @@ 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 *"\([^"]*\)".*/{' \
+           $(CC) -E $(DEFS) $(INCL) $$i | grep -v '[<>]' | 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
index e3fdf333c4433b604b2046e8d5713e9354536d8f..3f24a9351f293cca2d8ecf2a2adb6a3100994a48 100644 (file)
@@ -58,7 +58,7 @@ 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 *"\([^"]*\)".*/{' \
+           $(CC) -E $(DEFS) $(INCL) $$i | grep -v '[<>]' | 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
diff --git a/postfix/src/scache/.indent.pro b/postfix/src/scache/.indent.pro
new file mode 120000 (symlink)
index 0000000..5c837ec
--- /dev/null
@@ -0,0 +1 @@
+../../.indent.pro
\ No newline at end of file
diff --git a/postfix/src/scache/Makefile.in b/postfix/src/scache/Makefile.in
new file mode 100644 (file)
index 0000000..092a93b
--- /dev/null
@@ -0,0 +1,69 @@
+SHELL  = /bin/sh
+SRCS   = scache.c
+OBJS   = scache.o
+HDRS   = 
+TESTSRC        =
+DEFS   = -I. -I$(INC_DIR) -D$(SYSTYPE)
+CFLAGS = $(DEBUG) $(OPT) $(DEFS)
+TESTPROG= 
+PROG   = scache
+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)
+
+tests: test
+
+update: ../../libexec/$(PROG)
+
+../../libexec/$(PROG): $(PROG)
+       cp $(PROG) ../../libexec
+
+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 | grep -v '[<>]' | 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'
+scache.o: scache.c
+scache.o: ../../include/sys_defs.h
+scache.o: ../../include/msg.h
+scache.o: ../../include/iostuff.h
+scache.o: ../../include/htable.h
+scache.o: ../../include/ring.h
+scache.o: ../../include/mail_params.h
+scache.o: ../../include/mail_proto.h
+scache.o: ../../include/vstream.h
+scache.o: ../../include/vbuf.h
+scache.o: ../../include/attr.h
+scache.o: ../../include/scache.h
+scache.o: ../../include/vstring.h
+scache.o: ../../include/mail_server.h
+scache.o: ../../include/mail_conf.h
diff --git a/postfix/src/scache/scache.c b/postfix/src/scache/scache.c
new file mode 100644 (file)
index 0000000..70d271d
--- /dev/null
@@ -0,0 +1,407 @@
+/*++
+/* NAME
+/*     scache 8
+/* SUMMARY
+/*     Postfix session cache server
+/* SYNOPSIS
+/*     \fBscache\fR [generic Postfix daemon options]
+/* DESCRIPTION
+/*     The scache server maintains the Postfix session cache. This
+/*     information can be used by, for example, the Postfix SMTP client.
+/*
+/*     The session cache is organized into logical destination
+/*     names, physical endpoint names, and sessions.
+/*
+/*     As a specific example, logical SMTP destinations specify
+/*     (transport, domain, port), and physical SMTP endpoints
+/*     specify (transport, IP address, port).  An SMTP session
+/*     may be saved after a successful mail transaction.
+/*
+/*     In the general case, one logical destination may refer to
+/*     zero or more physical endpoints, one physical endpoint may
+/*     be referenced by zero or more logical destinations, and
+/*     one endpoint may refer to zero or more sessions.
+/*
+/*     The exact syntax of a logical destination or endpoint name
+/*     is application dependent; the \fBscache\fR service does
+/*     not care.  A session is stored as a file descriptor together
+/*     with application-dependent information that is needed to
+/*     re-activate a session object. Again, the \fBscache\fR
+/*     service is completely unaware about the details of that
+/*     information.
+/*
+/*     All information is stored with a finite time to live (ttl).
+/*     The session cache daemon terminates when no client is
+/*     connected for \fBmax_idle\fR time units.
+/*
+/*     This server implements the following requests:
+/* .IP "\fBsave_endp\fI ttl endpoint endpoint_properties file_descriptor\fR"
+/*     Save the specified file descriptor and session property data
+/*     under the specified endpoint name. The endpoint properties
+/*     are used by the client to re-activate a passivated session
+/*     object.
+/*     queue ID is queued for the specified destination.
+/* .IP "\fBfind_endp\fI endpoint\fR"
+/*     Look up cached properties and a cached file descriptor for the
+/*     specified endpoint.
+/* .IP "\fBsave_dest\fI ttl destination destination_properties endpoint\fR"
+/*     Save the binding between a logical destination and an
+/*     endpoint under the destination name, together with destination
+/*     specific session properties. The destination properties
+/*     are used by the client to re-activate a passivated session
+/*     object.
+/* .IP "\fBfind_dest\fI destination\fR"
+/*     Look up cached destination properties, cached endpoint properties,
+/*     and a cached file descriptor for the specified logical destination.
+/* SECURITY
+/* .ad
+/* .fi
+/*     The session cache server is not security-sensitive. It does not
+/*     talk to the network, and it does not talk to local users.
+/*     The scache server can run chrooted at fixed low privilege.
+/*
+/*     The session cache server is not a trusted process. It must
+/*     not be used to store information that is security sensitive.
+/* DIAGNOSTICS
+/*     Problems and transactions are logged to \fBsyslogd\fR(8).
+/* BUGS
+/*     Sessions cannot be cached across multiple machines.
+/*
+/*      When a session expires from the cache it is closed without
+/*     protocol specific handshake.
+/* CONFIGURATION PARAMETERS
+/* .ad
+/* .fi
+/*     Changes to \fBmain.cf\fR are picked up automatically as scache(8)
+/*     processes run for only a limited amount of time. Use the command
+/*     "\fBpostfix reload\fR" to speed up a change.
+/*
+/*     The text below provides only a parameter summary. See
+/*     postconf(5) for more details including examples.
+/* RESOURCE CONTROLS
+/* .ad
+/* .fi
+/* .IP "\fBsession_cache_ttl_limit (2s)\fR"
+/*     The maximal time-to-live value that the session cache server
+/*     allows.
+/* MISCELLANEOUS CONTROLS
+/* .ad
+/* .fi
+/* .IP "\fBconfig_directory (see 'postconf -d' output)\fR"
+/*     The default location of the Postfix main.cf and master.cf
+/*     configuration files.
+/* .IP "\fBdaemon_timeout (18000s)\fR"
+/*     How much time a Postfix daemon process may take to handle a
+/*     request before it is terminated by a built-in watchdog timer.
+/* .IP "\fBipc_timeout (3600s)\fR"
+/*     The time limit for sending or receiving information over an internal
+/*     communication channel.
+/* .IP "\fBmax_idle (100s)\fR"
+/*     The maximum amount of time that an idle Postfix daemon process
+/*     waits for the next service request before exiting.
+/* .IP "\fBprocess_id (read-only)\fR"
+/*     The process ID of a Postfix command or daemon process.
+/* .IP "\fBprocess_name (read-only)\fR"
+/*     The process name of a Postfix command or daemon process.
+/* .IP "\fBsyslog_facility (mail)\fR"
+/*     The syslog facility of Postfix logging.
+/* .IP "\fBsyslog_name (postfix)\fR"
+/*     The mail system name that is prepended to the process name in syslog
+/*     records, so that "smtpd" becomes, for example, "postfix/smtpd".
+/* SEE ALSO
+/*     smtp(8), SMTP client
+/*     postconf(5), configuration parameters
+/*     master(8), process manager
+/*     syslogd(8), system logging
+/* LICENSE
+/* .ad
+/* .fi
+/*     The Secure Mailer license must be distributed with this software.
+/* HISTORY
+/*     This service was introduced with Postfix version 2.2.
+/* AUTHOR(S)
+/*     Wietse Venema
+/*     IBM T.J. Watson Research
+/*     P.O. Box 704
+/*     Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <iostuff.h>
+#include <htable.h>
+#include <ring.h>
+
+/* Global library. */
+
+#include <mail_params.h>
+#include <mail_proto.h>
+#include <scache.h>
+
+/* Single server skeleton. */
+
+#include <mail_server.h>
+#include <mail_conf.h>
+
+/* Application-specific. */
+
+ /*
+  * Tunable parameters.
+  */
+int     var_scache_ttl_lim;
+
+ /*
+  * Request parameters.
+  */
+static VSTRING *scache_request;
+static VSTRING *scache_dest_label;
+static VSTRING *scache_dest_prop;
+static VSTRING *scache_endp_label;
+static VSTRING *scache_endp_prop;
+
+ /*
+  * Session cache instance.
+  */
+static SCACHE *scache;
+
+ /*
+  * Silly little macros.
+  */
+#define STR(x)                 vstring_str(x)
+#define VSTREQ(x,y)            (strcmp(STR(x),y) == 0)
+
+/* scache_save_endp_service - protocol to save endpoint->stream binding */
+
+static void scache_save_endp_service(VSTREAM *client_stream)
+{
+    const char *myname = "scache_save_endp_service";
+    int     ttl;
+    int     fd;
+
+    if (attr_scan(client_stream,
+                 ATTR_FLAG_STRICT,
+                 ATTR_TYPE_NUM, MAIL_ATTR_TTL, &ttl,
+                 ATTR_TYPE_STR, MAIL_ATTR_LABEL, scache_endp_label,
+                 ATTR_TYPE_STR, MAIL_ATTR_PROP, scache_endp_prop,
+                 ATTR_TYPE_END) != 3
+       || ttl <= 0) {
+       msg_warn("%s: bad or missing request parameter", myname);
+       attr_print(client_stream, ATTR_FLAG_NONE,
+                  ATTR_TYPE_NUM, MAIL_ATTR_STATUS, SCACHE_STAT_BAD,
+                  ATTR_TYPE_END);
+       return;
+    } else if ((fd = LOCAL_RECV_FD(vstream_fileno(client_stream))) < 0) {
+       msg_warn("%s: unable to receive file descriptor", myname);
+       attr_print(client_stream, ATTR_FLAG_NONE,
+                  ATTR_TYPE_NUM, MAIL_ATTR_STATUS, SCACHE_STAT_FAIL,
+                  ATTR_TYPE_END);
+       return;
+    } else {
+       scache_save_endp(scache,
+                        ttl > var_scache_ttl_lim ? var_scache_ttl_lim : ttl,
+                        STR(scache_endp_label), STR(scache_endp_prop), fd);
+       attr_print(client_stream, ATTR_FLAG_NONE,
+                  ATTR_TYPE_NUM, MAIL_ATTR_STATUS, SCACHE_STAT_OK,
+                  ATTR_TYPE_END);
+       return;
+    }
+}
+
+/* scache_find_endp_service - protocol to find session for endpoint */
+
+static void scache_find_endp_service(VSTREAM *client_stream)
+{
+    const char *myname = "scache_find_endp_service";
+    int     fd;
+
+    if (attr_scan(client_stream,
+                 ATTR_FLAG_STRICT,
+                 ATTR_TYPE_STR, MAIL_ATTR_LABEL, scache_endp_label,
+                 ATTR_TYPE_END) != 1) {
+       msg_warn("%s: bad or missing request parameter", myname);
+       attr_print(client_stream, ATTR_FLAG_NONE,
+                  ATTR_TYPE_NUM, MAIL_ATTR_STATUS, SCACHE_STAT_BAD,
+                  ATTR_TYPE_STR, MAIL_ATTR_PROP, "",
+                  ATTR_TYPE_END);
+       return;
+    } else if ((fd = scache_find_endp(scache, STR(scache_endp_label),
+                                     scache_endp_prop)) < 0) {
+       attr_print(client_stream, ATTR_FLAG_NONE,
+                  ATTR_TYPE_NUM, MAIL_ATTR_STATUS, SCACHE_STAT_FAIL,
+                  ATTR_TYPE_STR, MAIL_ATTR_PROP, "",
+                  ATTR_TYPE_END);
+       return;
+    } else {
+       attr_print(client_stream, ATTR_FLAG_NONE,
+                  ATTR_TYPE_NUM, MAIL_ATTR_STATUS, SCACHE_STAT_OK,
+                  ATTR_TYPE_STR, MAIL_ATTR_PROP, STR(scache_endp_prop),
+                  ATTR_TYPE_END);
+       if (vstream_fflush(client_stream) != 0
+           || LOCAL_SEND_FD(vstream_fileno(client_stream), fd) < 0)
+           msg_warn("%s: cannot send file descriptor: %m", myname);
+       if (close(fd) < 0)
+           msg_warn("close(%d): %m", fd);
+       return;
+    }
+}
+
+/* scache_save_dest_service - protocol to save destiation->endpoint binding */
+
+static void scache_save_dest_service(VSTREAM *client_stream)
+{
+    const char *myname = "scache_save_dest_service";
+    int     ttl;
+
+    if (attr_scan(client_stream,
+                 ATTR_FLAG_STRICT,
+                 ATTR_TYPE_NUM, MAIL_ATTR_TTL, &ttl,
+                 ATTR_TYPE_STR, MAIL_ATTR_LABEL, scache_dest_label,
+                 ATTR_TYPE_STR, MAIL_ATTR_PROP, scache_dest_prop,
+                 ATTR_TYPE_STR, MAIL_ATTR_LABEL, scache_endp_label,
+                 ATTR_TYPE_END) != 4
+       || ttl <= 0) {
+       msg_warn("%s: bad or missing request parameter", myname);
+       attr_print(client_stream, ATTR_FLAG_NONE,
+                  ATTR_TYPE_NUM, MAIL_ATTR_STATUS, SCACHE_STAT_BAD,
+                  ATTR_TYPE_END);
+       return;
+    } else {
+       scache_save_dest(scache,
+                        ttl > var_scache_ttl_lim ? var_scache_ttl_lim : ttl,
+                        STR(scache_dest_label), STR(scache_dest_prop),
+                        STR(scache_endp_label));
+       attr_print(client_stream, ATTR_FLAG_NONE,
+                  ATTR_TYPE_NUM, MAIL_ATTR_STATUS, SCACHE_STAT_OK,
+                  ATTR_TYPE_END);
+       return;
+    }
+}
+
+/* scache_find_dest_service - protocol to find session for destination */
+
+static void scache_find_dest_service(VSTREAM *client_stream)
+{
+    const char *myname = "scache_find_dest_service";
+    int     fd;
+
+    if (attr_scan(client_stream,
+                 ATTR_FLAG_STRICT,
+                 ATTR_TYPE_STR, MAIL_ATTR_LABEL, scache_dest_label,
+                 ATTR_TYPE_END) != 1) {
+       msg_warn("%s: bad or missing request parameter", myname);
+       attr_print(client_stream, ATTR_FLAG_NONE,
+                  ATTR_TYPE_NUM, MAIL_ATTR_STATUS, SCACHE_STAT_BAD,
+                  ATTR_TYPE_STR, MAIL_ATTR_PROP, "",
+                  ATTR_TYPE_STR, MAIL_ATTR_PROP, "",
+                  ATTR_TYPE_END);
+       return;
+    } else if ((fd = scache_find_dest(scache, STR(scache_dest_label),
+                                     scache_dest_prop,
+                                     scache_endp_prop)) < 0) {
+       attr_print(client_stream, ATTR_FLAG_NONE,
+                  ATTR_TYPE_NUM, MAIL_ATTR_STATUS, SCACHE_STAT_FAIL,
+                  ATTR_TYPE_STR, MAIL_ATTR_PROP, "",
+                  ATTR_TYPE_STR, MAIL_ATTR_PROP, "",
+                  ATTR_TYPE_END);
+       return;
+    } else {
+       attr_print(client_stream, ATTR_FLAG_NONE,
+                  ATTR_TYPE_NUM, MAIL_ATTR_STATUS, SCACHE_STAT_OK,
+                  ATTR_TYPE_STR, MAIL_ATTR_PROP, STR(scache_dest_prop),
+                  ATTR_TYPE_STR, MAIL_ATTR_PROP, STR(scache_endp_prop),
+                  ATTR_TYPE_END);
+       if (vstream_fflush(client_stream) != 0
+           || LOCAL_SEND_FD(vstream_fileno(client_stream), fd) < 0)
+           msg_warn("%s: cannot send file descriptor: %m", myname);
+       if (close(fd) < 0)
+           msg_warn("close(%d): %m", fd);
+       return;
+    }
+}
+
+/* scache_service - perform service for client */
+
+static void scache_service(VSTREAM *client_stream, char *unused_service,
+                                  char **argv)
+{
+
+    /*
+     * Sanity check. This service takes no command-line arguments.
+     */
+    if (argv[0])
+       msg_fatal("unexpected command-line argument: %s", argv[0]);
+
+    /*
+     * This routine runs whenever a client connects to the UNIX-domain socket
+     * dedicated to the scache service. All connection-management stuff is
+     * handled by the common code in multi_server.c.
+     */
+    if (attr_scan(client_stream,
+                 ATTR_FLAG_MORE | ATTR_FLAG_STRICT,
+                 ATTR_TYPE_STR, MAIL_ATTR_REQ, scache_request,
+                 ATTR_TYPE_END) == 1) {
+       if (VSTREQ(scache_request, SCACHE_REQ_SAVE_DEST)) {
+           scache_save_dest_service(client_stream);
+       } else if (VSTREQ(scache_request, SCACHE_REQ_FIND_DEST)) {
+           scache_find_dest_service(client_stream);
+       } else if (VSTREQ(scache_request, SCACHE_REQ_SAVE_ENDP)) {
+           scache_save_endp_service(client_stream);
+       } else if (VSTREQ(scache_request, SCACHE_REQ_FIND_ENDP)) {
+           scache_find_endp_service(client_stream);
+       } else {
+           msg_warn("unrecognized request: \"%s\", ignored",
+                    STR(scache_request));
+           attr_print(client_stream, ATTR_FLAG_NONE,
+                      ATTR_TYPE_NUM, MAIL_ATTR_STATUS, SCACHE_STAT_BAD,
+                      ATTR_TYPE_END);
+       }
+    }
+    vstream_fflush(client_stream);
+}
+
+/* post_jail_init - initialization after privilege drop */
+
+static void post_jail_init(char *unused_name, char **unused_argv)
+{
+
+    /*
+     * Pre-allocate the cache instance.
+     */
+    scache = scache_multi_create();
+
+    /*
+     * Pre-allocate buffers.
+     */
+    scache_request = vstring_alloc(10);
+    scache_dest_label = vstring_alloc(10);
+    scache_dest_prop = vstring_alloc(10);
+    scache_endp_label = vstring_alloc(10);
+    scache_endp_prop = vstring_alloc(10);
+
+    /*
+     * Disable the max_use limit. We still terminate when no client is
+     * connected for $idle_limit time units.
+     */
+    var_use_limit = 0;
+}
+
+/* main - pass control to the multi-threaded skeleton */
+
+int     main(int argc, char **argv)
+{
+    static CONFIG_TIME_TABLE time_table[] = {
+       VAR_SCACHE_TTL_LIM, DEF_SCACHE_TTL_LIM, &var_scache_ttl_lim, 1, 0,
+       0,
+    };
+
+    multi_server_main(argc, argv, scache_service,
+                     MAIL_SERVER_TIME_TABLE, time_table,
+                     MAIL_SERVER_POST_INIT, post_jail_init,
+                     MAIL_SERVER_SOLITARY,
+                     0);
+}
index fbbb60600ce8392c661de44895de8271c4ccde45..0da0de758fe9ee5a7ace79f3a506303a55d0972f 100644 (file)
@@ -46,7 +46,7 @@ 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 *"\([^"]*\)".*/{' \
+           $(CC) -E $(DEFS) $(INCL) $$i | grep -v '[<>]' | 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
index a8cba0c1f156d6783fb1bb9152559bb86213a89a..ae0e476cf936e620a2327166e0aea3307262bfed 100644 (file)
@@ -46,7 +46,7 @@ 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 *"\([^"]*\)".*/{' \
+           $(CC) -E $(DEFS) $(INCL) $$i | grep -v '[<>]' | 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
index 609123a10c3329fcf0835acf53d39f9533f5beb9..8fa75f53d5f830166797156b41d8aaff19b906e6 100644 (file)
@@ -1,11 +1,11 @@
 SHELL  = /bin/sh
 SRCS   = smtp.c smtp_connect.c smtp_proto.c smtp_chat.c smtp_session.c \
        smtp_addr.c smtp_trouble.c smtp_state.c smtp_rcpt.c \
-       smtp_sasl_proto.c smtp_sasl_glue.c
+       smtp_sasl_proto.c smtp_sasl_glue.c smtp_reuse.c
 OBJS   = smtp.o smtp_connect.o smtp_proto.o smtp_chat.o smtp_session.o \
        smtp_addr.o smtp_trouble.o smtp_state.o smtp_rcpt.o \
-       smtp_sasl_proto.o smtp_sasl_glue.o
-HDRS   = smtp.h smtp_sasl.h
+       smtp_sasl_proto.o smtp_sasl_glue.o smtp_reuse.o
+HDRS   = smtp.h smtp_sasl.h smtp_addr.h smtp_reuse.h
 TESTSRC        = 
 DEFS   = -I. -I$(INC_DIR) -D$(SYSTYPE)
 CFLAGS = $(DEBUG) $(OPT) $(DEFS)
@@ -54,7 +54,7 @@ smtp_unalias: smtp_unalias.c $(LIBS)
 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 *"\([^"]*\)".*/{' \
+           $(CC) -E $(DEFS) $(INCL) $$i | grep -v '[<>]' | 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
@@ -76,8 +76,13 @@ smtp.o: ../../include/mail_params.h
 smtp.o: ../../include/mail_conf.h
 smtp.o: ../../include/debug_peer.h
 smtp.o: ../../include/flush_clnt.h
+smtp.o: ../../include/scache.h
+smtp.o: ../../include/string_list.h
+smtp.o: ../../include/match_list.h
+smtp.o: ../../include/match_ops.h
 smtp.o: ../../include/mail_server.h
 smtp.o: smtp.h
+smtp.o: ../../include/htable.h
 smtp.o: smtp_sasl.h
 smtp_addr.o: smtp_addr.c
 smtp_addr.o: ../../include/sys_defs.h
@@ -94,8 +99,13 @@ smtp_addr.o: ../../include/dns.h
 smtp_addr.o: smtp.h
 smtp_addr.o: ../../include/vstream.h
 smtp_addr.o: ../../include/argv.h
+smtp_addr.o: ../../include/htable.h
 smtp_addr.o: ../../include/deliver_request.h
 smtp_addr.o: ../../include/recipient_list.h
+smtp_addr.o: ../../include/scache.h
+smtp_addr.o: ../../include/string_list.h
+smtp_addr.o: ../../include/match_list.h
+smtp_addr.o: ../../include/match_ops.h
 smtp_addr.o: smtp_addr.h
 smtp_chat.o: smtp_chat.c
 smtp_chat.o: ../../include/sys_defs.h
@@ -117,6 +127,11 @@ smtp_chat.o: ../../include/cleanup_user.h
 smtp_chat.o: ../../include/mail_error.h
 smtp_chat.o: ../../include/name_mask.h
 smtp_chat.o: smtp.h
+smtp_chat.o: ../../include/htable.h
+smtp_chat.o: ../../include/scache.h
+smtp_chat.o: ../../include/string_list.h
+smtp_chat.o: ../../include/match_list.h
+smtp_chat.o: ../../include/match_ops.h
 smtp_connect.o: smtp_connect.c
 smtp_connect.o: ../../include/sys_defs.h
 smtp_connect.o: ../../include/msg.h
@@ -133,7 +148,6 @@ smtp_connect.o: ../../include/host_port.h
 smtp_connect.o: ../../include/sane_connect.h
 smtp_connect.o: ../../include/mail_params.h
 smtp_connect.o: ../../include/own_inet_addr.h
-smtp_connect.o: ../../include/debug_peer.h
 smtp_connect.o: ../../include/deliver_pass.h
 smtp_connect.o: ../../include/deliver_request.h
 smtp_connect.o: ../../include/recipient_list.h
@@ -144,7 +158,13 @@ smtp_connect.o: ../../include/name_mask.h
 smtp_connect.o: ../../include/dns.h
 smtp_connect.o: smtp.h
 smtp_connect.o: ../../include/argv.h
+smtp_connect.o: ../../include/htable.h
+smtp_connect.o: ../../include/scache.h
+smtp_connect.o: ../../include/string_list.h
+smtp_connect.o: ../../include/match_list.h
+smtp_connect.o: ../../include/match_ops.h
 smtp_connect.o: smtp_addr.h
+smtp_connect.o: smtp_reuse.h
 smtp_proto.o: smtp_proto.c
 smtp_proto.o: ../../include/sys_defs.h
 smtp_proto.o: ../../include/msg.h
@@ -176,6 +196,11 @@ smtp_proto.o: ../../include/mime_state.h
 smtp_proto.o: ../../include/header_opts.h
 smtp_proto.o: smtp.h
 smtp_proto.o: ../../include/argv.h
+smtp_proto.o: ../../include/htable.h
+smtp_proto.o: ../../include/scache.h
+smtp_proto.o: ../../include/string_list.h
+smtp_proto.o: ../../include/match_list.h
+smtp_proto.o: ../../include/match_ops.h
 smtp_proto.o: smtp_sasl.h
 smtp_rcpt.o: smtp_rcpt.c
 smtp_rcpt.o: ../../include/sys_defs.h
@@ -189,6 +214,31 @@ smtp_rcpt.o: ../../include/deliver_completed.h
 smtp_rcpt.o: ../../include/sent.h
 smtp_rcpt.o: smtp.h
 smtp_rcpt.o: ../../include/argv.h
+smtp_rcpt.o: ../../include/htable.h
+smtp_rcpt.o: ../../include/scache.h
+smtp_rcpt.o: ../../include/string_list.h
+smtp_rcpt.o: ../../include/match_list.h
+smtp_rcpt.o: ../../include/match_ops.h
+smtp_reuse.o: smtp_reuse.c
+smtp_reuse.o: ../../include/sys_defs.h
+smtp_reuse.o: ../../include/msg.h
+smtp_reuse.o: ../../include/mymalloc.h
+smtp_reuse.o: ../../include/vstream.h
+smtp_reuse.o: ../../include/vbuf.h
+smtp_reuse.o: ../../include/vstring.h
+smtp_reuse.o: ../../include/htable.h
+smtp_reuse.o: ../../include/stringops.h
+smtp_reuse.o: ../../include/scache.h
+smtp_reuse.o: ../../include/mail_params.h
+smtp_reuse.o: smtp.h
+smtp_reuse.o: ../../include/argv.h
+smtp_reuse.o: ../../include/deliver_request.h
+smtp_reuse.o: ../../include/recipient_list.h
+smtp_reuse.o: ../../include/string_list.h
+smtp_reuse.o: ../../include/match_list.h
+smtp_reuse.o: ../../include/match_ops.h
+smtp_reuse.o: smtp_reuse.h
+smtp_reuse.o: ../../include/dns.h
 smtp_sasl_glue.o: smtp_sasl_glue.c
 smtp_sasl_glue.o: ../../include/sys_defs.h
 smtp_sasl_glue.o: ../../include/msg.h
@@ -207,8 +257,10 @@ smtp_sasl_glue.o: ../../include/dict.h
 smtp_sasl_glue.o: ../../include/vstream.h
 smtp_sasl_glue.o: ../../include/argv.h
 smtp_sasl_glue.o: smtp.h
+smtp_sasl_glue.o: ../../include/htable.h
 smtp_sasl_glue.o: ../../include/deliver_request.h
 smtp_sasl_glue.o: ../../include/recipient_list.h
+smtp_sasl_glue.o: ../../include/scache.h
 smtp_sasl_glue.o: smtp_sasl.h
 smtp_sasl_proto.o: smtp_sasl_proto.c
 smtp_sasl_proto.o: ../../include/sys_defs.h
@@ -220,33 +272,51 @@ smtp_sasl_proto.o: ../../include/vstream.h
 smtp_sasl_proto.o: ../../include/vbuf.h
 smtp_sasl_proto.o: ../../include/vstring.h
 smtp_sasl_proto.o: ../../include/argv.h
+smtp_sasl_proto.o: ../../include/htable.h
 smtp_sasl_proto.o: ../../include/deliver_request.h
 smtp_sasl_proto.o: ../../include/recipient_list.h
+smtp_sasl_proto.o: ../../include/scache.h
+smtp_sasl_proto.o: ../../include/string_list.h
+smtp_sasl_proto.o: ../../include/match_list.h
+smtp_sasl_proto.o: ../../include/match_ops.h
 smtp_sasl_proto.o: smtp_sasl.h
 smtp_session.o: smtp_session.c
 smtp_session.o: ../../include/sys_defs.h
+smtp_session.o: ../../include/msg.h
 smtp_session.o: ../../include/mymalloc.h
 smtp_session.o: ../../include/vstream.h
 smtp_session.o: ../../include/vbuf.h
 smtp_session.o: ../../include/stringops.h
 smtp_session.o: ../../include/vstring.h
+smtp_session.o: ../../include/mime_state.h
+smtp_session.o: ../../include/header_opts.h
+smtp_session.o: ../../include/debug_peer.h
+smtp_session.o: ../../include/mail_params.h
 smtp_session.o: smtp.h
 smtp_session.o: ../../include/argv.h
+smtp_session.o: ../../include/htable.h
 smtp_session.o: ../../include/deliver_request.h
 smtp_session.o: ../../include/recipient_list.h
+smtp_session.o: ../../include/scache.h
+smtp_session.o: ../../include/string_list.h
+smtp_session.o: ../../include/match_list.h
+smtp_session.o: ../../include/match_ops.h
 smtp_state.o: smtp_state.c
 smtp_state.o: ../../include/sys_defs.h
 smtp_state.o: ../../include/mymalloc.h
 smtp_state.o: ../../include/vstring.h
 smtp_state.o: ../../include/vbuf.h
-smtp_state.o: ../../include/vstream.h
-smtp_state.o: ../../include/mail_conf.h
-smtp_state.o: ../../include/mime_state.h
-smtp_state.o: ../../include/header_opts.h
+smtp_state.o: ../../include/mail_params.h
 smtp_state.o: smtp.h
+smtp_state.o: ../../include/vstream.h
 smtp_state.o: ../../include/argv.h
+smtp_state.o: ../../include/htable.h
 smtp_state.o: ../../include/deliver_request.h
 smtp_state.o: ../../include/recipient_list.h
+smtp_state.o: ../../include/scache.h
+smtp_state.o: ../../include/string_list.h
+smtp_state.o: ../../include/match_list.h
+smtp_state.o: ../../include/match_ops.h
 smtp_state.o: smtp_sasl.h
 smtp_trouble.o: smtp_trouble.c
 smtp_trouble.o: ../../include/sys_defs.h
@@ -266,6 +336,11 @@ smtp_trouble.o: ../../include/mail_error.h
 smtp_trouble.o: ../../include/name_mask.h
 smtp_trouble.o: smtp.h
 smtp_trouble.o: ../../include/argv.h
+smtp_trouble.o: ../../include/htable.h
+smtp_trouble.o: ../../include/scache.h
+smtp_trouble.o: ../../include/string_list.h
+smtp_trouble.o: ../../include/match_list.h
+smtp_trouble.o: ../../include/match_ops.h
 smtp_unalias.o: smtp_unalias.c
 smtp_unalias.o: ../../include/sys_defs.h
 smtp_unalias.o: ../../include/htable.h
@@ -278,3 +353,7 @@ smtp_unalias.o: ../../include/vstream.h
 smtp_unalias.o: ../../include/argv.h
 smtp_unalias.o: ../../include/deliver_request.h
 smtp_unalias.o: ../../include/recipient_list.h
+smtp_unalias.o: ../../include/scache.h
+smtp_unalias.o: ../../include/string_list.h
+smtp_unalias.o: ../../include/match_list.h
+smtp_unalias.o: ../../include/match_ops.h
index dbcb0c1d2f00feb49d8dbbdc51f58df04504ef3f..d51f011f38f20e6556ec7323775e20624d2bfcc3 100644 (file)
 /*     When a server is not reachable, or when mail delivery fails due
 /*     to a recoverable error condition, the SMTP client will try to
 /*     deliver the mail to an alternate host.
+/*
+/*     After a successful mail transaction, a session may be saved
+/*     to the \fBscache(8)\fR session cache server, so that it
+/*     may be used by any SMTP client for a subsequent transaction.
+/*     Session caching is disabled by default.
 /* SECURITY
 /* .ad
 /* .fi
 /*     Depending on the setting of the \fBnotify_classes\fR parameter,
 /*     the postmaster is notified of bounces, protocol problems, and of
 /*     other trouble.
+/* BUGS
+/*      SMTP session caching does not work with TLS. The necessary
+/*      support for object passivation and re-activation does not
+/*     exist.
+/*
+/*     SMTP session caching assumes that SASL credentials are valid for
+/*     all destinations that map onto the same IP address and TCP port.
 /* CONFIGURATION PARAMETERS
 /* .ad
 /* .fi
 /* .IP "\fBmime_boundary_length_limit (2048)\fR"
 /*     The maximal length of MIME multipart boundary strings.
 /* .IP "\fBmime_nesting_limit (100)\fR"
-/*     The maximal nesting level of multipart mail that the MIME processor
-/*     will handle.
+/*     The maximal recursion level that the MIME processor will handle.
 /* EXTERNAL CONTENT INSPECTION CONTROLS
 /* .ad
 /* .fi
 /*     The maximal number of SMTP sessions per delivery request before
 /*     giving up or delivering to a fall-back relay host, or zero (no
 /*     limit).
-/* .IP "\fBsmtp_rset_timeout (120s)\fR"
+/* .IP "\fBsmtp_rset_timeout (20s)\fR"
 /*     The SMTP client time limit for sending the RSET command, and
 /*     for receiving the server response.
+/* .PP
+/*     Available in Postfix version 2.2 and later:
+/* .IP "\fBsmtp_connection_cache_domains (empty)\fR"
+/*     The SMTP destinations for which SMTP connection caching is
+/*     enabled.
+/* .IP "\fBsmtp_connection_cache_reuse_limit (10)\fR"
+/*     When SMTP session caching is enabled, the number of times that
+/*     an SMTP session is reused before it is closed.
+/* .IP "\fBsmtp_connection_cache_time_limit (2s)\fR"
+/*     When SMTP session caching is enabled, the amount of time that
+/*     an unused SMTP client socket is kept open before it is closed.
 /* TROUBLE SHOOTING CONTROLS
 /* .ad
 /* .fi
 /* SEE ALSO
 /*     qmgr(8), queue manager
 /*     bounce(8), delivery status reports
+/*     scache(8), session cache server
 /*     postconf(5), configuration parameters
 /*     master(8), process manager
 /*     syslogd(8), system logging
 /*     IBM T.J. Watson Research
 /*     P.O. Box 704
 /*     Yorktown Heights, NY 10598, USA
+/*
+/*     Command pipelining in cooperation with:
+/*     Jon Ribbens
+/*     Oaktree Internet Solutions Ltd.,
+/*     Internet House,
+/*     Canal Basin,
+/*     Coventry,
+/*     CV1 4LY, United Kingdom.
+/*
+/*     Connection caching in cooperation with:
+/*     Victor Duchovni
+/*     Morgan Stanley
 /*--*/
 
 /* System library. */
 #include <mail_conf.h>
 #include <debug_peer.h>
 #include <flush_clnt.h>
+#include <scache.h>
+#include <string_list.h>
 
 /* Single server skeleton. */
 
@@ -333,6 +370,10 @@ bool    var_smtp_defer_mxaddr;
 bool    var_smtp_send_xforward;
 int     var_smtp_mxaddr_limit;
 int     var_smtp_mxsess_limit;
+int     var_smtp_cache_conn;
+int     var_smtp_reuse_limit;
+char   *var_smtp_cache_dest;
+char   *var_scache_service;
 
  /*
   * Global variables. smtp_errno is set by the address lookup routines and by
@@ -340,10 +381,12 @@ int     var_smtp_mxsess_limit;
   */
 int     smtp_errno;
 int     smtp_host_lookup_mask;
+STRING_LIST *smtp_cache_dest;
+SCACHE *smtp_scache;
 
 /* deliver_message - deliver message with extreme prejudice */
 
-static int deliver_message(DELIVER_REQUEST *request)
+static int deliver_message(const char *service, DELIVER_REQUEST *request)
 {
     SMTP_STATE *state;
     int     result;
@@ -369,6 +412,7 @@ static int deliver_message(DELIVER_REQUEST *request)
     state = smtp_state_alloc();
     state->request = request;
     state->src = request->fp;
+    state->service = service;
     SMTP_RCPT_INIT(state);
 
     /*
@@ -389,7 +433,7 @@ static int deliver_message(DELIVER_REQUEST *request)
 
 /* smtp_service - perform service for client */
 
-static void smtp_service(VSTREAM *client_stream, char *unused_service, char **argv)
+static void smtp_service(VSTREAM *client_stream, char *service, char **argv)
 {
     DELIVER_REQUEST *request;
     int     status;
@@ -409,14 +453,14 @@ static void smtp_service(VSTREAM *client_stream, char *unused_service, char **ar
      * the common code in single_server.c.
      */
     if ((request = deliver_request_read(client_stream)) != 0) {
-       status = deliver_message(request);
+       status = deliver_message(service, request);
        deliver_request_done(client_stream, request, status);
     }
 }
 
-/* pre_init - pre-jail initialization */
+/* post_init - post-jail initialization */
 
-static void pre_init(char *unused_name, char **unused_argv)
+static void post_init(char *unused_name, char **unused_argv)
 {
     static NAME_MASK lookup_masks[] = {
        SMTP_HOST_LOOKUP_DNS, SMTP_HOST_FLAG_DNS,
@@ -442,6 +486,24 @@ static void pre_init(char *unused_name, char **unused_argv)
                 str_name_mask(VAR_SMTP_HOST_LOOKUP, lookup_masks,
                               smtp_host_lookup_mask));
 
+    /*
+     * Session cache instance.
+     */
+    if (*var_smtp_cache_dest)
+#if 0
+       smtp_scache = scache_multi_create();
+#else
+       smtp_scache = scache_clnt_create(var_scache_service,
+                                        var_ipc_idle_limit,
+                                        var_ipc_ttl_limit);
+#endif
+}
+
+/* pre_init - pre-jail initialization */
+
+static void pre_init(char *unused_name, char **unused_argv)
+{
+
     /*
      * SASL initialization.
      */
@@ -457,6 +519,12 @@ static void pre_init(char *unused_name, char **unused_argv)
      * Flush client.
      */
     flush_init();
+
+    /*
+     * Session cache domain list.
+     */
+    if (*var_smtp_cache_dest)
+       smtp_cache_dest = string_list_init(MATCH_FLAG_NONE, var_smtp_cache_dest);
 }
 
 /* pre_accept - see if tables have changed */
@@ -495,6 +563,8 @@ int     main(int argc, char **argv)
        VAR_SMTP_BIND_ADDR, DEF_SMTP_BIND_ADDR, &var_smtp_bind_addr, 0, 0,
        VAR_SMTP_HELO_NAME, DEF_SMTP_HELO_NAME, &var_smtp_helo_name, 1, 0,
        VAR_SMTP_HOST_LOOKUP, DEF_SMTP_HOST_LOOKUP, &var_smtp_host_lookup, 1, 0,
+       VAR_SMTP_CACHE_DEST, DEF_SMTP_CACHE_DEST, &var_smtp_cache_dest, 0, 0,
+       VAR_SCACHE_SERVICE, DEF_SCACHE_SERVICE, &var_scache_service, 1, 0,
        0,
     };
     static CONFIG_TIME_TABLE time_table[] = {
@@ -510,12 +580,14 @@ int     main(int argc, char **argv)
        VAR_SMTP_QUIT_TMOUT, DEF_SMTP_QUIT_TMOUT, &var_smtp_quit_tmout, 1, 0,
        VAR_SMTP_PIX_THRESH, DEF_SMTP_PIX_THRESH, &var_smtp_pix_thresh, 0, 0,
        VAR_SMTP_PIX_DELAY, DEF_SMTP_PIX_DELAY, &var_smtp_pix_delay, 1, 0,
+       VAR_SMTP_CACHE_CONN, DEF_SMTP_CACHE_CONN, &var_smtp_cache_conn, 1, 0,
        0,
     };
     static CONFIG_INT_TABLE int_table[] = {
        VAR_SMTP_LINE_LIMIT, DEF_SMTP_LINE_LIMIT, &var_smtp_line_limit, 0, 0,
        VAR_SMTP_MXADDR_LIMIT, DEF_SMTP_MXADDR_LIMIT, &var_smtp_mxaddr_limit, 0, 0,
        VAR_SMTP_MXSESS_LIMIT, DEF_SMTP_MXSESS_LIMIT, &var_smtp_mxsess_limit, 0, 0,
+       VAR_SMTP_REUSE_LIMIT, DEF_SMTP_REUSE_LIMIT, &var_smtp_reuse_limit, 1, 0,
        0,
     };
     static CONFIG_BOOL_TABLE bool_table[] = {
@@ -538,6 +610,7 @@ int     main(int argc, char **argv)
                       MAIL_SERVER_STR_TABLE, str_table,
                       MAIL_SERVER_BOOL_TABLE, bool_table,
                       MAIL_SERVER_PRE_INIT, pre_init,
+                      MAIL_SERVER_POST_INIT, post_init,
                       MAIL_SERVER_PRE_ACCEPT, pre_accept,
                       MAIL_SERVER_EXIT, pre_exit,
                       0);
index c965c25e7d288e0908fa75da5b95c3d4777ffd47..bb061a966a1ff8d9ecec90feb319af4629a70925 100644 (file)
 #include <vstream.h>
 #include <vstring.h>
 #include <argv.h>
+#include <htable.h>
 
  /*
   * Global library.
   */
 #include <deliver_request.h>
+#include <scache.h>
+#include <string_list.h>
 
  /*
   * State information associated with each SMTP delivery. We're bundling the
   */
 typedef struct SMTP_STATE {
     VSTREAM *src;                      /* queue file stream */
+    const char *service;               /* transport name */
     DELIVER_REQUEST *request;          /* envelope info, offsets */
     struct SMTP_SESSION *session;      /* network connection */
     int     status;                    /* delivery status */
     int     space_left;                        /* output length control */
 
+    /*
+     * Session cache support. The (nexthop_lookup_mx, nexthop_domain,
+     * nexthop_port) triple is a parsed next-hop specification, and should be
+     * a data type by itself. The (service, nexthop_mumble) members specify
+     * the name under which the first good session should be cached. The
+     * nexthop_mumble members are initialized by the connection management
+     * module. nexthop_domain is reset to null after one session is saved
+     * under the (service, nexthop_mumble) label, or upon exit from the
+     * connection management module.
+     */
+    HTABLE *cache_used;                        /* cached addresses that were used */
+    VSTRING *dest_label;               /* cached logical/physical binding */
+    VSTRING *dest_prop;                        /* binding properties, passivated */
+    VSTRING *endp_label;               /* cached session physical endpoint */
+    VSTRING *endp_prop;                        /* endpoint properties, passivated */
+    int     nexthop_lookup_mx;         /* do/don't MX expand nexthop_domain */
+    char   *nexthop_domain;            /* next-hop name or bare address */
+    unsigned nexthop_port;             /* next-hop TCP port, network order */
+
     /*
      * Flags and counters to control the handling of mail delivery errors.
      * There is some redundancy for sanity checking. At the end of an SMTP
@@ -50,6 +73,20 @@ typedef struct SMTP_STATE {
     int     rcpt_keep;                 /* recipients marked as keep */
 } SMTP_STATE;
 
+#define SET_NEXTHOP_STATE(state, lookup_mx, domain, port) { \
+       (state)->nexthop_lookup_mx = lookup_mx; \
+       (state)->nexthop_domain = mystrdup(domain); \
+       (state)->nexthop_port = port; \
+    }
+
+#define FREE_NEXTHOP_STATE(state) { \
+       myfree((state)->nexthop_domain); \
+       (state)->nexthop_domain = 0; \
+    }
+
+#define HAVE_NEXTHOP_STATE(state) ((state)->nexthop_domain != 0)
+
+
  /*
   * Server features.
   */
@@ -64,7 +101,22 @@ typedef struct SMTP_STATE {
 #define SMTP_FEATURE_XFORWARD_ADDR     (1<<8)
 #define SMTP_FEATURE_XFORWARD_PROTO    (1<<9)
 #define SMTP_FEATURE_XFORWARD_HELO     (1<<10)
-#define SMTP_FEATURE_CACHE_SESSION     (1<<11)
+
+#define SMTP_FEATURE_BEST_MX           (1<<12) /* for next-hop or fall-back */
+#define SMTP_FEATURE_RSET_REJECTED     (1<<13) /* RSET probe rejected */
+#define SMTP_FEATURE_FROM_CACHE                (1<<14) /* cached session */
+
+ /*
+  * Features that passivate under the endpoint.
+  */
+#define SMTP_FEATURE_ENDPOINT_MASK \
+       (~(SMTP_FEATURE_BEST_MX | SMTP_FEATURE_RSET_REJECTED \
+       | SMTP_FEATURE_FROM_CACHE))
+
+ /*
+  * Features that passivate under the logical destination.
+  */
+#define SMTP_FEATURE_DESTINATION_MASK (SMTP_FEATURE_BEST_MX)
 
  /*
   * Misc flags.
@@ -88,16 +140,19 @@ extern int smtp_host_lookup_mask;  /* host lookup methods to use */
 #define SMTP_HOST_FLAG_DNS     (1<<0)
 #define SMTP_HOST_FLAG_NATIVE  (1<<1)
 
+extern SCACHE *smtp_scache;            /* cache instance */
+extern STRING_LIST *smtp_cache_dest;   /* cached destinations */
+
  /*
   * smtp_session.c
   */
 typedef struct SMTP_SESSION {
     VSTREAM *stream;                   /* network connection */
-    char   *dest;                      /* nexthop[:port] or fallback relay */
+    char   *dest;                      /* nexthop or fallback */
     char   *host;                      /* mail exchanger */
     char   *addr;                      /* mail exchanger */
     char   *namaddr;                   /* mail exchanger */
-    int     best;                      /* most preferred host */
+    unsigned port;                     /* network byte order */
 
     VSTRING *buffer;                   /* I/O buffer */
     VSTRING *scratch;                  /* scratch buffer */
@@ -110,6 +165,11 @@ typedef struct SMTP_SESSION {
     int     error_mask;                        /* error classes */
     struct MIME_STATE *mime_state;     /* mime state machine */
 
+    int     sndbufsize;                        /* PIPELINING buffer size */
+    int     send_proto_helo;           /* XFORWARD support */
+
+    int     reuse_count;               /* how many uses left */
+
 #ifdef USE_SASL_AUTH
     char   *sasl_mechanism_list;       /* server mechanism list */
     char   *sasl_username;             /* client username */
@@ -122,9 +182,14 @@ typedef struct SMTP_SESSION {
 
 } SMTP_SESSION;
 
-extern SMTP_SESSION *smtp_session_alloc(VSTREAM *, char *, char *, char *);
-extern void smtp_session_reuse(SMTP_SESSION *);
+extern SMTP_SESSION *smtp_session_alloc(VSTREAM *, const char *,
+                                     const char *, const char *, unsigned, int);
 extern void smtp_session_free(SMTP_SESSION *);
+extern int smtp_session_passivate(SMTP_SESSION *, VSTRING *, VSTRING *);
+extern SMTP_SESSION *smtp_session_activate(int, VSTRING *, VSTRING *);
+
+#define SMTP_SESS_FLAG_NONE    0       /* no options */
+#define SMTP_SESS_FLAG_CACHE   (1<<0)  /* enable session caching */
 
  /*
   * smtp_connect.c
@@ -136,6 +201,8 @@ extern int smtp_connect(SMTP_STATE *);
   */
 extern int smtp_helo(SMTP_STATE *, int);
 extern int smtp_xfer(SMTP_STATE *);
+extern int smtp_rset(SMTP_STATE *);
+extern int smtp_quit(SMTP_STATE *);
 
  /*
   * smtp_chat.c
index bd295aacf923d6402ec7a356a0bdb8358f457921..b7cf54ecf004ff9d031ae9485da225fc5c51febc 100644 (file)
@@ -262,40 +262,41 @@ static DNS_RR *smtp_find_self(DNS_RR *addr_list)
 {
     char   *myname = "smtp_find_self";
     INET_ADDR_LIST *self;
+    INET_ADDR_LIST *proxy;
     DNS_RR *addr;
     int     i;
 
-    /*
-     * Find the first address that lists any address that this mail system is
-     * supposed to be listening on.
-     */
 #define INADDRP(x) ((struct in_addr *) (x))
 
     self = own_inet_addr_list();
+    proxy = proxy_inet_addr_list();
+
     for (addr = addr_list; addr; addr = addr->next) {
+
+       /*
+        * Find out if this mail system is listening on this address.
+        */
        for (i = 0; i < self->used; i++)
            if (INADDRP(addr->data)->s_addr == self->addrs[i].s_addr) {
                if (msg_verbose)
-                   msg_info("%s: found at pref %d", myname, addr->pref);
+                   msg_info("%s: found self at pref %d", myname, addr->pref);
                return (addr);
            }
-    }
 
-    /*
-     * Find out if this mail system has a proxy listening on this address.
-     */
-    self = proxy_inet_addr_list();
-    for (addr = addr_list; addr; addr = addr->next) {
-       for (i = 0; i < self->used; i++)
-           if (INADDRP(addr->data)->s_addr == self->addrs[i].s_addr) {
+       /*
+        * Find out if this mail system has a proxy listening on this
+        * address.
+        */
+       for (i = 0; i < proxy->used; i++)
+           if (INADDRP(addr->data)->s_addr == proxy->addrs[i].s_addr) {
                if (msg_verbose)
-                   msg_info("%s: found at pref %d", myname, addr->pref);
+                   msg_info("%s: found proxy at pref %d", myname, addr->pref);
                return (addr);
            }
     }
 
     /*
-     * Didn't find myself.
+     * Didn't find myself, or my proxy.
      */
     if (msg_verbose)
        msg_info("%s: not found", myname);
index c81120adfa73eba16065e636e5f236b725fcc62f..bbc0ccacb3659bc61a0dd9fe6a3e3fb50ed3f4db 100644 (file)
@@ -19,6 +19,9 @@
 /*     that fail to complete the SMTP handshake and tries to find
 /*     an alternate server when an SMTP session fails to deliver.
 /*
+/*     This layer also controls what sessions are retrieved from
+/*     the session cache, and what sessions are saved to the cache.
+/*
 /*     The destination is either a host (or domain) name or a numeric
 /*     address. Symbolic or numeric service port information may be
 /*     appended, separated by a colon (":").
 /*     IBM T.J. Watson Research
 /*     P.O. Box 704
 /*     Yorktown Heights, NY 10598, USA
+/*
+/*     Connection caching in cooperation with:
+/*     Victor Duchovni
+/*     Morgan Stanley
 /*--*/
 
 /* System library. */
 #define INADDR_NONE 0xffffffff
 #endif
 
+#ifndef IPPORT_SMTP
+#define IPPORT_SMTP 25
+#endif
+
 /* Utility library. */
 
 #include <msg.h>
@@ -83,7 +94,6 @@
 
 #include <mail_params.h>
 #include <own_inet_addr.h>
-#include <debug_peer.h>
 #include <deliver_pass.h>
 #include <mail_error.h>
 
 
 /* Application-specific. */
 
-#include "smtp.h"
-#include "smtp_addr.h"
+#include <smtp.h>
+#include <smtp_addr.h>
+#include <smtp_reuse.h>
+
+#define STR(x) vstring_str(x)
 
 /* smtp_connect_addr - connect to explicit address */
 
-static SMTP_SESSION *smtp_connect_addr(char *dest, DNS_RR *addr, unsigned port,
-                                              VSTRING *why)
+static SMTP_SESSION *smtp_connect_addr(const char *dest, DNS_RR *addr,
+                                              unsigned port, VSTRING *why,
+                                              int sess_flags)
 {
     char   *myname = "smtp_connect_addr";
     struct sockaddr_in sin;
@@ -213,7 +227,7 @@ static SMTP_SESSION *smtp_connect_addr(char *dest, DNS_RR *addr, unsigned port,
     }
     vstream_ungetc(stream, ch);
     return (smtp_session_alloc(stream, dest, addr->name,
-                              inet_ntoa(sin.sin_addr)));
+                              inet_ntoa(sin.sin_addr), port, sess_flags));
 }
 
 /* smtp_parse_destination - parse destination */
@@ -251,6 +265,168 @@ static char *smtp_parse_destination(char *destination, char *def_service,
     return (buf);
 }
 
+/* smtp_cleanup_session - clean up after using a session */
+
+static void smtp_cleanup_session(SMTP_STATE *state)
+{
+    SMTP_SESSION *session = state->session;
+
+    /*
+     * Inform the postmaster of trouble.
+     */
+    if (session->history != 0
+       && (session->error_mask & name_mask(VAR_NOTIFY_CLASSES,
+                                           mail_error_masks,
+                                           var_notify_classes)) != 0)
+       smtp_chat_notify(session);
+
+    /*
+     * When session caching is enabled, cache the first good session for this
+     * delivery request under the next-hop destination, and cache all good
+     * sessions under their server network address (destroying the session in
+     * the process).
+     * 
+     * Caching under the next-hop destination name (rather than the fall-back
+     * destination) allows us to skip over non-responding primary or backup
+     * hosts. In fact, this is the only benefit of caching logical to
+     * physical bindings; caching a session under its own hostname provides
+     * no performance benefit, given the way smtp_connect() works.
+     * 
+     * XXX Should not cache TLS sessions unless we are using a single-session,
+     * in-process, cache. And if we did, we should passivate VSTREAM objects
+     * in addition to passivating SMTP_SESSION objects.
+     */
+    if (session->reuse_count > 0) {
+       smtp_save_session(state);
+       if (HAVE_NEXTHOP_STATE(state))
+           FREE_NEXTHOP_STATE(state);
+    } else {
+       smtp_session_free(session);
+    }
+    state->session = 0;
+
+    /*
+     * Clean up the lists with todo and dropped recipients.
+     */
+    smtp_rcpt_cleanup(state);
+}
+
+/* smtp_scrub_address_list - delete all cached addresses from list */
+
+static void smtp_scrub_addr_list(HTABLE *cached_addr, DNS_RR **addr_list)
+{
+    DNS_RR *addr;
+    DNS_RR *next;
+
+#define INADDRP(x) ((struct in_addr *) (x))
+
+    for (addr = *addr_list; addr; addr = next) {
+       next = addr->next;
+       if (addr->type == T_A) {
+           if (addr->data_len > sizeof(struct in_addr))
+               continue;
+           if (htable_locate(cached_addr, inet_ntoa(*INADDRP(addr->data))))
+               *addr_list = dns_rr_remove(*addr_list, addr);
+       }
+    }
+}
+
+/* smtp_update_addr_list - common address list update */
+
+static void smtp_update_addr_list(DNS_RR **addr_list, const char *server_addr,
+                                         int session_count)
+{
+    struct in_addr server_in_addr;
+    DNS_RR *addr;
+    DNS_RR *next;
+
+    if (*addr_list == 0)
+       return;
+
+    /*
+     * Truncate the address list if we are not going to use it anyway.
+     */
+    if (session_count == var_smtp_mxsess_limit
+       || session_count == var_smtp_mxaddr_limit) {
+       dns_rr_free(*addr_list);
+       *addr_list = 0;
+       return;
+    }
+
+    /*
+     * Convert server address to internal form, and look it up in the address
+     * list.
+     * 
+     * XXX smtp_reuse_session() breaks if we remove two or more adjacent list
+     * elements but do not truncate the list to zero length.
+     */
+    server_in_addr.s_addr = inet_addr(server_addr);
+    for (addr = *addr_list; addr; addr = next) {
+       next = addr->next;
+       if (addr->type == T_A) {                /* NOT: switch */
+           if (addr->data_len > sizeof(server_in_addr))
+               continue;
+           if (INADDRP(addr->data)->s_addr == server_in_addr.s_addr) {
+               *addr_list = dns_rr_remove(*addr_list, addr);
+               break;
+           }
+       }
+    }
+}
+
+/* smtp_reuse_session - try to use existing connection, return session count */
+
+static int smtp_reuse_session(SMTP_STATE *state, int lookup_mx,
+                                     const char *domain, unsigned port,
+                                  DNS_RR **addr_list, int domain_best_pref)
+{
+    int     session_count = 0;
+    DNS_RR *addr;
+    DNS_RR *next;
+    int     saved_final_server = state->final_server;
+    SMTP_SESSION *session;
+
+    /*
+     * First, search the cache by logical destination. We truncate the server
+     * address list when all the sessions for this destination are used up,
+     * to reduce the number of variables that need to be checked later.
+     * 
+     * Note: lookup by logical destination restores the "best MX" bit.
+     */
+    if (*addr_list && SMTP_RCPT_LEFT(state) > 0
+    && (session = smtp_reuse_domain(state, lookup_mx, domain, port)) != 0) {
+       session_count = 1;
+       smtp_update_addr_list(addr_list, session->addr, session_count);
+       state->final_server = (saved_final_server && *addr_list == 0);
+       smtp_xfer(state);
+       smtp_cleanup_session(state);
+    }
+
+    /*
+     * Second, search the cache by primary MX address. Again, we use address
+     * list truncation so that we have to check fewer variables later.
+     * 
+     * XXX This loop is safe because smtp_update_addr_list() either truncates
+     * the list to zero length, or removes at most one list element.
+     */
+    for (addr = *addr_list; SMTP_RCPT_LEFT(state) > 0 && addr; addr = next) {
+       if (addr->pref != domain_best_pref)
+           break;
+       next = addr->next;
+       if ((session = smtp_reuse_addr(state, addr, port)) != 0) {
+           session->features |= SMTP_FEATURE_BEST_MX;
+           session_count += 1;
+           smtp_update_addr_list(addr_list, session->addr, session_count);
+           if (*addr_list == 0)
+               next = 0;
+           state->final_server = (saved_final_server && next == 0);
+           smtp_xfer(state);
+           smtp_cleanup_session(state);
+       }
+    }
+    return (session_count);
+}
+
 /* smtp_connect - establish SMTP connection */
 
 int     smtp_connect(SMTP_STATE *state)
@@ -258,9 +434,9 @@ int     smtp_connect(SMTP_STATE *state)
     DELIVER_REQUEST *request = state->request;
     VSTRING *why = vstring_alloc(10);
     char   *dest_buf;
-    char   *host;
+    char   *domain;
     unsigned port;
-    char   *def_service = "smtp";      /* XXX configurable? */
+    char   *def_service = "smtp";      /* XXX ##IPPORT_SMTP? */
     ARGV   *sites;
     char   *dest;
     char  **cpp;
@@ -271,6 +447,9 @@ int     smtp_connect(SMTP_STATE *state)
     int     sess_count;
     int     misc_flags = SMTP_MISC_FLAG_DEFAULT;
     SMTP_SESSION *session;
+    int     lookup_mx;
+    unsigned domain_best_pref;
+    int     sess_flags;
 
     /*
      * First try to deliver to the indicated destination, then try to deliver
@@ -285,6 +464,16 @@ int     smtp_connect(SMTP_STATE *state)
        msg_panic("null destination: \"%s\"", request->nexthop);
     argv_split_append(sites, var_fallback_relay, ", \t\r\n");
 
+    /*
+     * Enable session caching by next-hop destination.
+     */
+    if (sites->argv[0]
+       && smtp_cache_dest
+       && string_list_match(smtp_cache_dest, sites->argv[0]))
+       sess_flags = SMTP_SESS_FLAG_CACHE;
+    else
+       sess_flags = SMTP_SESS_FLAG_NONE;
+
     /*
      * Don't give up after a hard host lookup error until we have tried the
      * fallback relay servers.
@@ -309,31 +498,71 @@ int     smtp_connect(SMTP_STATE *state)
         * the address instead of the mail exchanger when a quoted host is
         * specified, or when DNS lookups are disabled.
         */
-       dest_buf = smtp_parse_destination(dest, def_service, &host, &port);
+       dest_buf = smtp_parse_destination(dest, def_service, &domain, &port);
 
        /*
         * Resolve an SMTP server. Skip mail exchanger lookups when a quoted
         * host is specified, or when DNS lookups are disabled.
         */
        if (msg_verbose)
-           msg_info("connecting to %s port %d", host, ntohs(port));
-       if (ntohs(port) != 25)
+           msg_info("connecting to %s port %d", domain, ntohs(port));
+       if (ntohs(port) != IPPORT_SMTP)
            misc_flags &= ~SMTP_MISC_FLAG_LOOP_DETECT;
        else
            misc_flags |= SMTP_MISC_FLAG_LOOP_DETECT;
-       if (var_disable_dns || *dest == '[') {
-           addr_list = smtp_host_addr(host, misc_flags, why);
+       lookup_mx = (var_disable_dns == 0 && *dest != '[');
+       if (!lookup_mx) {
+           addr_list = smtp_host_addr(domain, misc_flags, why);
        } else {
-           addr_list = smtp_domain_addr(host, misc_flags, why);
+           addr_list = smtp_domain_addr(domain, misc_flags, why);
        }
-       myfree(dest_buf);
+
+       /*
+        * When session caching is enabled, store the first good session for
+        * this delivery request under the next-hop destination name. All
+        * good sessions will be stored under their specific server IP
+        * address.
+        * 
+        * XXX Replace sites->argv by (lookup_mx, domain, port) triples so we
+        * don't have to make clumsy ad-hoc copies and keep track of who
+        * free()s the memory.
+        */
+       if (cpp == sites->argv && (sess_flags & SMTP_SESS_FLAG_CACHE) != 0)
+           SET_NEXTHOP_STATE(state, lookup_mx, domain, port);
 
        /*
         * Don't try any backup host if mail loops to myself. That would just
         * make the problem worse.
         */
-       if (addr_list == 0 && smtp_errno == SMTP_ERR_LOOP)
+       if (addr_list == 0 && smtp_errno == SMTP_ERR_LOOP) {
+           myfree(dest_buf);
            break;
+       }
+
+       /*
+        * No early loop exit or we have a memory leak with dest_buf.
+        */
+       if (addr_list)
+           domain_best_pref = addr_list->pref;
+
+       /*
+        * Delete visited cached hosts from the address list.
+        * 
+        * Optionally search the connection cache by domain name or by primary
+        * MX address.
+        * 
+        * Enforce the MX session and MX address counts per next-hop or
+        * fall-back destination. smtp_reuse_session() will truncate the
+        * address list when either limit is reached.
+        */
+       if (addr_list && (sess_flags & SMTP_SESS_FLAG_CACHE) != 0) {
+           if (state->cache_used->used > 0)
+               smtp_scrub_addr_list(state->cache_used, &addr_list);
+           sess_count = addr_count =
+               smtp_reuse_session(state, lookup_mx, domain, port,
+                                  &addr_list, domain_best_pref);
+       } else
+           sess_count = addr_count = 0;
 
        /*
         * Connect to an SMTP server.
@@ -344,36 +573,33 @@ int     smtp_connect(SMTP_STATE *state)
         * the end of an SMTP session, weed out the recipient list. Unmark
         * any left-over recipients and try to deliver them to a backup mail
         * server.
+        * 
+        * Cache the first good session under the next-hop destination name.
+        * Cache all good sessions under their physical endpoint.
         */
-       sess_count = addr_count = 0;
        for (addr = addr_list; SMTP_RCPT_LEFT(state) > 0 && addr; addr = next) {
            next = addr->next;
            if (++addr_count == var_smtp_mxaddr_limit)
                next = 0;
-           session = state->session = smtp_connect_addr(dest, addr, port, why);
-           if (session != 0) {
+           if ((sess_flags & SMTP_SESS_FLAG_CACHE) == 0
+               || (session = smtp_reuse_addr(state, addr, port)) == 0)
+               session = smtp_connect_addr(dest, addr, port, why, sess_flags);
+           if ((state->session = session) != 0) {
                if (++sess_count == var_smtp_mxsess_limit)
                    next = 0;
                state->final_server = (cpp[1] == 0 && next == 0);
-               session->best = (addr->pref == addr_list->pref);
-               debug_peer_check(session->host, session->addr);
-               if (smtp_helo(state, misc_flags) == 0)
+               if (addr->pref == domain_best_pref)
+                   session->features |= SMTP_FEATURE_BEST_MX;
+               if ((session->features & SMTP_FEATURE_FROM_CACHE) != 0
+                   || smtp_helo(state, misc_flags) == 0)
                    smtp_xfer(state);
-               if (session->history != 0) {
-                   if (session->error_mask & name_mask(VAR_NOTIFY_CLASSES,
-                                     mail_error_masks, var_notify_classes))
-                       smtp_chat_notify(session);
-               }
-               /* XXX smtp_xfer() may abort in the middle of DATA. */
-               smtp_session_free(session);
-               state->session = 0;
-               debug_peer_restore();
-               smtp_rcpt_cleanup(state);
+               smtp_cleanup_session(state);
            } else {
                msg_info("%s (port %d)", vstring_str(why), ntohs(port));
            }
        }
        dns_rr_free(addr_list);
+       myfree(dest_buf);
     }
 
     /*
@@ -445,6 +671,8 @@ int     smtp_connect(SMTP_STATE *state)
     /*
      * Cleanup.
      */
+    if (HAVE_NEXTHOP_STATE(state))
+       FREE_NEXTHOP_STATE(state);
     argv_free(sites);
     vstring_free(why);
     return (state->status);
index 18fcb3ea1fa11fef9d51b2fd317b49a0e7c25b5c..82b7debe3c8cd61a0d0c5287da69bdbbb7a13059 100644 (file)
 /*
 /*     int     smtp_xfer(state)
 /*     SMTP_STATE *state;
+/*
+/*     int     smtp_rset(state)
+/*     SMTP_STATE *state;
+/*
+/*     int     smtp_quit(state)
+/*     SMTP_STATE *state;
 /* DESCRIPTION
 /*     This module implements the client side of the SMTP protocol.
 /*
 /*     Recipients are marked as "done" in the mail queue file when
 /*     bounced or delivered. The message delivery status is updated
 /*     accordingly.
+/*
+/*     smtp_rset() sends a single RSET command and waits for the
+/*     response. In case of no response, or negative response, it
+/*     turns off caching for the current session.
+/*
+/*     smtp_quit() sends a single QUIT command and waits for the
+/*     response if configured to do so. It always turns off caching
+/*     for the current session.
 /* DIAGNOSTICS
-/*     smtp_helo() and smtp_xfer() return 0 in case of success, -1 in case
-/*     of failure. For smtp_xfer(), success means the ability to perform
-/*     an SMTP conversation, not necessarily the ability to deliver mail.
+/*     smtp_helo(), smtp_xfer(), smtp_rset() and smtp_quit() return
+/*     0 in case of success, -1 in case of failure. For smtp_xfer(),
+/*     smtp_rset() and smtp_quit(), success means the ability to
+/*     perform an SMTP conversation, not necessarily the ability
+/*     to deliver mail, or the achievement of server happiness.
 /*
 /*     Warnings: corrupt message file. A corrupt message is marked
 /*     as "corrupt" by changing its queue file permissions.
@@ -57,6 +73,9 @@
 /*     Coventry,
 /*     CV1 4LY, United Kingdom.
 /*
+/*     Connection caching in cooperation with:
+/*     Victor Duchovni
+/*     Morgan Stanley
 /*--*/
 
 /* System library. */
   * Normal sessions go from MAIL->RCPT->DATA->DOT->QUIT->LAST. The states
   * MAIL, RCPT, and DATA may also be followed by ABORT->QUIT->LAST.
   * 
+  * When connection caching is enabled, the QUIT state is suppressed. Normal
+  * sessions proceed as MAIL->RCPT->DATA->DOT->LAST, while aborted sessions
+  * end with ABORT->LAST. The connection is left open for a limited time. An
+  * RSET probe should be sent before attempting to reuse an open connection
+  * for a new transaction.
+  * 
+  * The code to send an RSET probe is a special case with its own initial state
+  * and with its own dedicated state transitions. The session proceeds as
+  * RSET->LAST. This code is kept inside the main protocol engine for
+  * consistent error handling and error reporting. It is not to be confused
+  * with the code that sends RSET to abort a mail transaction in progress.
+  * 
+  * The code to send QUIT without message delivery transaction jumps into the
+  * main state machine. If this introduces complications, then we should
+  * introduce a second QUIT state with its own dedicated state transitions,
+  * just like we did for RSET probes.
+  * 
   * By default, the receiver skips the QUIT response. Some SMTP servers
   * disconnect after responding to ".", and some SMTP servers wait before
   * responding to QUIT.
 #define SMTP_STATE_DATA                4
 #define SMTP_STATE_DOT         5
 #define SMTP_STATE_ABORT       6
-#define SMTP_STATE_QUIT                7
-#define SMTP_STATE_LAST                8
+#define SMTP_STATE_RSET                7
+#define SMTP_STATE_QUIT                8
+#define SMTP_STATE_LAST                9
 
 int    *xfer_timeouts[SMTP_STATE_LAST] = {
     &var_smtp_xfwd_tmout,              /* name/addr */
@@ -140,6 +177,7 @@ int    *xfer_timeouts[SMTP_STATE_LAST] = {
     &var_smtp_data0_tmout,
     &var_smtp_data2_tmout,
     &var_smtp_rset_tmout,
+    &var_smtp_rset_tmout,
     &var_smtp_quit_tmout,
 };
 
@@ -151,6 +189,7 @@ char   *xfer_states[SMTP_STATE_LAST] = {
     "sending DATA command",
     "sending end of data -- message may be sent more than once",
     "sending final RSET",
+    "sending RSET probe",
     "sending QUIT",
 };
 
@@ -162,6 +201,7 @@ char   *xfer_request[SMTP_STATE_LAST] = {
     "DATA command",
     "end of DATA command",
     "final RSET command",
+    "RSET probe",
     "QUIT command",
 };
 
@@ -169,6 +209,7 @@ char   *xfer_request[SMTP_STATE_LAST] = {
 
 int     smtp_helo(SMTP_STATE *state, int misc_flags)
 {
+    char   *myname = "smtp_helo";
     SMTP_SESSION *session = state->session;
     DELIVER_REQUEST *request = state->request;
     SMTP_RESP *resp;
@@ -184,6 +225,7 @@ int     smtp_helo(SMTP_STATE *state, int misc_flags)
        XFORWARD_HELO, SMTP_FEATURE_XFORWARD_HELO,
        0, 0,
     };
+    SOCKOPT_SIZE optlen;
 
     /*
      * Prepare for disaster.
@@ -234,10 +276,10 @@ int     smtp_helo(SMTP_STATE *state, int misc_flags)
            session->features |= SMTP_FEATURE_ESMTP;
     }
     if (var_smtp_always_ehlo
-        && (session->features & SMTP_FEATURE_MAYBEPIX) == 0)
+       && (session->features & SMTP_FEATURE_MAYBEPIX) == 0)
        session->features |= SMTP_FEATURE_ESMTP;
     if (var_smtp_never_ehlo
-       || (session->features & SMTP_FEATURE_MAYBEPIX) != 0)
+       || (session->features & SMTP_FEATURE_MAYBEPIX) != 0)
        session->features &= ~SMTP_FEATURE_ESMTP;
 
     /*
@@ -279,7 +321,7 @@ int     smtp_helo(SMTP_STATE *state, int misc_flags)
            else if (strcasecmp(word, "XFORWARD") == 0)
                while ((word = mystrtok(&words, " \t")) != 0)
                    session->features |= name_code(xforward_features,
-                                                  NAME_CODE_FLAG_NONE, word);
+                                                NAME_CODE_FLAG_NONE, word);
            else if (strcasecmp(word, "SIZE") == 0) {
                session->features |= SMTP_FEATURE_SIZE;
                if ((word = mystrtok(&words, " \t")) != 0) {
@@ -298,7 +340,8 @@ int     smtp_helo(SMTP_STATE *state, int misc_flags)
                if (misc_flags & SMTP_MISC_FLAG_LOOP_DETECT) {
                    msg_warn("host %s replied to HELO/EHLO with my own hostname %s",
                             session->namaddr, var_myhostname);
-                   return (smtp_site_fail(state, session->best ? 550 : 450,
+                   return (smtp_site_fail(state,
+                    (session->features & SMTP_FEATURE_BEST_MX) ? 550 : 450,
                                         "mail for %s loops back to myself",
                                           request->nexthop));
                }
@@ -309,6 +352,39 @@ int     smtp_helo(SMTP_STATE *state, int misc_flags)
        msg_info("server features: 0x%x size %.0f",
                 session->features, (double) session->size_limit);
 
+    /*
+     * We use SMTP command pipelining if the server said it supported it.
+     * Since we use blocking I/O, RFC 2197 says that we should inspect the
+     * TCP window size and not send more than this amount of information.
+     * Unfortunately this information is not available using the sockets
+     * interface. However, we *can* get the TCP send buffer size on the local
+     * TCP/IP stack. We should be able to fill this buffer without being
+     * blocked, and then the kernel will effectively do non-blocking I/O for
+     * us by automatically writing out the contents of its send buffer while
+     * we are reading in the responses. In addition to TCP buffering we have
+     * to be aware of application-level buffering by the vstream module,
+     * which is limited to a couple kbytes.
+     */
+    if (session->features & SMTP_FEATURE_PIPELINING) {
+       optlen = sizeof(session->sndbufsize);
+       if (getsockopt(vstream_fileno(session->stream), SOL_SOCKET,
+                    SO_SNDBUF, (char *) &session->sndbufsize, &optlen) < 0)
+           msg_fatal("%s: getsockopt: %m", myname);
+       if (session->sndbufsize > VSTREAM_BUFSIZE)
+           session->sndbufsize = VSTREAM_BUFSIZE;
+       if (session->sndbufsize == 0) {
+           session->sndbufsize = VSTREAM_BUFSIZE;
+           if (setsockopt(vstream_fileno(session->stream), SOL_SOCKET,
+                     SO_SNDBUF, (char *) &session->sndbufsize, optlen) < 0)
+               msg_fatal("%s: setsockopt: %m", myname);
+       }
+       if (msg_verbose)
+           msg_info("Using ESMTP PIPELINING, TCP send buffer size is %d",
+                    session->sndbufsize);
+    } else {
+       session->sndbufsize = 0;
+    }
+
 #ifdef USE_SASL_AUTH
     if (var_smtp_sasl_enable && (session->features & SMTP_FEATURE_AUTH))
        return (smtp_sasl_helo_login(state));
@@ -380,11 +456,12 @@ static void smtp_header_out(void *context, int unused_header_class,
     }
 }
 
-/* smtp_xfer - send a batch of envelope information and the message data */
+/* smtp_loop - exercise the SMTP protocol engine */
 
-int     smtp_xfer(SMTP_STATE *state)
+static int smtp_loop(SMTP_STATE *state, NOCLOBBER int send_state,
+                            NOCLOBBER int recv_state)
 {
-    char   *myname = "smtp_xfer";
+    char   *myname = "smtp_loop";
     DELIVER_REQUEST *request = state->request;
     SMTP_SESSION *session = state->session;
     SMTP_RESP *resp;
@@ -392,22 +469,16 @@ int     smtp_xfer(SMTP_STATE *state)
     VSTRING *next_command = vstring_alloc(100);
     NOCLOBBER int next_state;
     NOCLOBBER int next_rcpt;
-    NOCLOBBER int send_state;
-    NOCLOBBER int recv_state;
     NOCLOBBER int send_rcpt;
     NOCLOBBER int recv_rcpt;
     NOCLOBBER int nrcpt;
     int     except;
     int     rec_type;
     NOCLOBBER int prev_type = 0;
-    int     sndbufsize = 0;
     NOCLOBBER int sndbuffree;
-    SOCKOPT_SIZE optlen = sizeof(sndbufsize);
     NOCLOBBER int mail_from_rejected;
     NOCLOBBER int downgrading;
     int     mime_errs;
-    int     send_name_addr;
-    NOCLOBBER int send_proto_helo;
 
     /*
      * Macros for readability.
@@ -445,61 +516,20 @@ int     smtp_xfer(SMTP_STATE *state)
 #define SENDING_MAIL \
        (recv_state <= SMTP_STATE_DOT)
 
-    /*
-     * Sanity check. Recipients should be unmarked at this point.
-     */
-    if (SMTP_RCPT_LEFT(state) <= 0)
-       msg_panic("smtp_xfer: bad recipient count: %d",
-                 SMTP_RCPT_LEFT(state));
-    if (SMTP_RCPT_ISMARKED(request->rcpt_list.info))
-       msg_panic("smtp_xfer: bad recipient status: %d",
-                 request->rcpt_list.info->status);
+#define THIS_SESSION_IS_CACHED \
+       (session->reuse_count > 0)
 
-    /*
-     * See if we should even try to send this message at all. This code sits
-     * here rather than in the EHLO processing code, because of future SMTP
-     * connection caching.
-     */
-    if (session->size_limit > 0 && session->size_limit < request->data_size) {
-       smtp_mesg_fail(state, 552,
-                   "message size %lu exceeds size limit %.0f of server %s",
-                      request->data_size, (double) session->size_limit,
-                      session->namaddr);
-       RETURN(0);
-    }
+#define DONT_CACHE_THIS_SESSION \
+       (session->reuse_count = 0)
+
+#define CANT_RSET_THIS_SESSION \
+       (session->features |= SMTP_FEATURE_RSET_REJECTED)
 
     /*
-     * We use SMTP command pipelining if the server said it supported it.
-     * Since we use blocking I/O, RFC 2197 says that we should inspect the
-     * TCP window size and not send more than this amount of information.
-     * Unfortunately this information is not available using the sockets
-     * interface. However, we *can* get the TCP send buffer size on the local
-     * TCP/IP stack. We should be able to fill this buffer without being
-     * blocked, and then the kernel will effectively do non-blocking I/O for
-     * us by automatically writing out the contents of its send buffer while
-     * we are reading in the responses. In addition to TCP buffering we have
-     * to be aware of application-level buffering by the vstream module,
-     * which is limited to a couple kbytes.
+     * Miscellaneous initialization. Some of this might be done in
+     * smtp_xfer() but that just complicates interfaces and data structures.
      */
-    if (session->features & SMTP_FEATURE_PIPELINING) {
-       if (getsockopt(vstream_fileno(session->stream), SOL_SOCKET,
-                      SO_SNDBUF, (char *) &sndbufsize, &optlen) < 0)
-           msg_fatal("%s: getsockopt: %m", myname);
-       if (sndbufsize > VSTREAM_BUFSIZE)
-           sndbufsize = VSTREAM_BUFSIZE;
-       if (sndbufsize == 0) {
-           sndbufsize = VSTREAM_BUFSIZE;
-           if (setsockopt(vstream_fileno(session->stream), SOL_SOCKET,
-                          SO_SNDBUF, (char *) &sndbufsize, optlen) < 0)
-               msg_fatal("%s: setsockopt: %m", myname);
-       }
-       if (msg_verbose)
-           msg_info("Using ESMTP PIPELINING, TCP send buffer size is %d",
-                    sndbufsize);
-    } else {
-       sndbufsize = 0;
-    }
-    sndbuffree = sndbufsize;
+    sndbuffree = session->sndbufsize;
 
     /*
      * Pipelining support requires two loops: one loop for sending and one
@@ -516,33 +546,12 @@ int     smtp_xfer(SMTP_STATE *state)
      * receiver detects a serious problem (MAIL FROM rejected, all RCPT TO
      * commands rejected, DATA rejected) it forces the sender to abort the
      * SMTP dialog with RSET and QUIT.
-     * 
-     * Use the XFORWARD command to forward client attributes only when a minimal
-     * amount of information is available.
      */
     nrcpt = 0;
-    send_name_addr =
-       var_smtp_send_xforward
-       && (((session->features & SMTP_FEATURE_XFORWARD_NAME)
-            && DEL_REQ_ATTR_AVAIL(request->client_name))
-           || ((session->features & SMTP_FEATURE_XFORWARD_ADDR)
-               && DEL_REQ_ATTR_AVAIL(request->client_addr)));
-    send_proto_helo =
-       var_smtp_send_xforward
-       && (((session->features & SMTP_FEATURE_XFORWARD_PROTO)
-            && DEL_REQ_ATTR_AVAIL(request->client_proto))
-           || ((session->features & SMTP_FEATURE_XFORWARD_HELO)
-               && DEL_REQ_ATTR_AVAIL(request->client_helo)));
-    if (send_name_addr)
-       recv_state = send_state = SMTP_STATE_XFORWARD_NAME_ADDR;
-    else if (send_proto_helo)
-       recv_state = send_state = SMTP_STATE_XFORWARD_PROTO_HELO;
-    else
-       recv_state = send_state = SMTP_STATE_MAIL;
     next_rcpt = send_rcpt = recv_rcpt = 0;
     mail_from_rejected = 0;
 
-    while (recv_state != SMTP_STATE_LAST) {
+    do {
 
        /*
         * Build the next command.
@@ -570,7 +579,7 @@ int     smtp_xfer(SMTP_STATE *state)
                vstring_sprintf_append(next_command, " %s=%s",
                   XFORWARD_ADDR, DEL_REQ_ATTR_AVAIL(request->client_addr) ?
                               request->client_addr : XFORWARD_UNAVAILABLE);
-           if (send_proto_helo)
+           if (session->send_proto_helo)
                next_state = SMTP_STATE_XFORWARD_PROTO_HELO;
            else
                next_state = SMTP_STATE_MAIL;
@@ -648,27 +657,42 @@ int     smtp_xfer(SMTP_STATE *state)
             */
        case SMTP_STATE_DOT:
            vstring_strcpy(next_command, ".");
-           next_state = SMTP_STATE_QUIT;
+           next_state = THIS_SESSION_IS_CACHED ?
+               SMTP_STATE_LAST : SMTP_STATE_QUIT;
            break;
 
            /*
-            * The SMTP_STATE_ABORT sender state is entered by sender when it
-            * has verified all recipients; or it is entered the receiver
-            * when all recipients are rejected and is then left before the
-            * bottom of the main loop.
+            * The SMTP_STATE_ABORT sender state is entered by the sender
+            * when it has verified all recipients; or it is entered by the
+            * receiver when all recipients are verified or rejected, and is
+            * then left before the bottom of the main loop.
             */
        case SMTP_STATE_ABORT:
            vstring_strcpy(next_command, "RSET");
-           next_state = SMTP_STATE_QUIT;
+           next_state = THIS_SESSION_IS_CACHED ?
+               SMTP_STATE_LAST : SMTP_STATE_QUIT;
+           break;
+
+           /*
+            * Build the RSET command. This is entered as initial state from
+            * smtp_rset() and has its own dedicated state transitions. It is
+            * used to find out the status of a cached session before
+            * attempting mail delivery.
+            */
+       case SMTP_STATE_RSET:
+           vstring_strcpy(next_command, "RSET");
+           next_state = SMTP_STATE_LAST;
            break;
 
            /*
             * Build the QUIT command before we have seen the "." or RSET
-            * response.
+            * response. This is entered as initial state from smtp_quit(),
+            * or is reached near the end of any non-cached session.
             */
        case SMTP_STATE_QUIT:
            vstring_strcpy(next_command, "QUIT");
            next_state = SMTP_STATE_LAST;
+           DONT_CACHE_THIS_SESSION;
            break;
 
            /*
@@ -727,7 +751,7 @@ int     smtp_xfer(SMTP_STATE *state)
                                 session->namaddr,
                                 translit(resp->str, "\n", " "),
                               xfer_request[SMTP_STATE_XFORWARD_NAME_ADDR]);
-                   if (send_proto_helo)
+                   if (session->send_proto_helo)
                        recv_state = SMTP_STATE_XFORWARD_PROTO_HELO;
                    else
                        recv_state = SMTP_STATE_MAIL;
@@ -795,6 +819,7 @@ int     smtp_xfer(SMTP_STATE *state)
                    if (++recv_rcpt == SMTP_RCPT_LEFT(state))
                        recv_state = DEL_REQ_TRACE_ONLY(request->flags) ?
                            SMTP_STATE_ABORT : SMTP_STATE_DATA;
+                   /* XXX Also: record if non-delivering session. */
                    break;
 
                    /*
@@ -819,8 +844,8 @@ int     smtp_xfer(SMTP_STATE *state)
                     * Process the end of message response. Ignore the
                     * response when no recipient was accepted: all
                     * recipients are dead already, and the next receiver
-                    * state is SMTP_STATE_QUIT regardless. Otherwise, if the
-                    * message transfer fails, bounce all remaining
+                    * state is SMTP_STATE_LAST/QUIT regardless. Otherwise,
+                    * if the message transfer fails, bounce all remaining
                     * recipients, else cross off the recipients that were
                     * delivered.
                     */
@@ -840,20 +865,34 @@ int     smtp_xfer(SMTP_STATE *state)
                            }
                        }
                    }
-                   recv_state = (var_skip_quit_resp ?
+                   recv_state = (var_skip_quit_resp || THIS_SESSION_IS_CACHED ?
                                  SMTP_STATE_LAST : SMTP_STATE_QUIT);
                    break;
 
                    /*
-                    * Ignore the RSET response.
+                    * Receive the RSET response, and disable session caching
+                    * in case of failure.
                     */
                case SMTP_STATE_ABORT:
-                   recv_state = (var_skip_quit_resp ?
+                   if (resp->code / 100 != 2)
+                       DONT_CACHE_THIS_SESSION;
+                   recv_state = (var_skip_quit_resp || THIS_SESSION_IS_CACHED ?
                                  SMTP_STATE_LAST : SMTP_STATE_QUIT);
                    break;
 
                    /*
-                    * Ignore the QUIT response.
+                    * This is the initial receiver state from smtp_rset().
+                    * It is used to find out the status of a cached session
+                    * before attempting mail delivery.
+                    */
+               case SMTP_STATE_RSET:
+                   if (resp->code / 100 != 2)
+                       CANT_RSET_THIS_SESSION;
+                   recv_state = SMTP_STATE_LAST;
+                   break;
+
+                   /*
+                    * Receive, but otherwise ignore, the QUIT response.
                     */
                case SMTP_STATE_QUIT:
                    recv_state = SMTP_STATE_LAST;
@@ -865,7 +904,7 @@ int     smtp_xfer(SMTP_STATE *state)
             * At this point, the sender and receiver are fully synchronized,
             * so that the entire TCP send buffer becomes available again.
             */
-           sndbuffree = sndbufsize;
+           sndbuffree = session->sndbufsize;
 
            /*
             * We know the server response to every command that was sent.
@@ -880,7 +919,9 @@ int     smtp_xfer(SMTP_STATE *state)
                send_state = recv_state = SMTP_STATE_ABORT;
                send_rcpt = recv_rcpt = 0;
                vstring_strcpy(next_command, "RSET");
-               next_state = SMTP_STATE_QUIT;
+               next_state = THIS_SESSION_IS_CACHED ?
+                   SMTP_STATE_LAST : SMTP_STATE_QUIT;
+               /* XXX Also: record if non-delivering session. */
                next_rcpt = 0;
            }
        }
@@ -908,12 +949,12 @@ int     smtp_xfer(SMTP_STATE *state)
            if (downgrading)
                session->mime_state = mime_state_alloc(MIME_OPT_DOWNGRADE
                                                  | MIME_OPT_REPORT_NESTING,
-                                                    smtp_header_out,
+                                                      smtp_header_out,
                                                     (MIME_STATE_ANY_END) 0,
-                                                    smtp_text_out,
+                                                      smtp_text_out,
                                                     (MIME_STATE_ANY_END) 0,
                                                   (MIME_STATE_ERR_PRINT) 0,
-                                                    (void *) state);
+                                                      (void *) state);
            state->space_left = var_smtp_line_limit;
            smtp_timeout_setup(session->stream,
                               var_smtp_data1_tmout);
@@ -989,6 +1030,90 @@ int     smtp_xfer(SMTP_STATE *state)
        smtp_chat_cmd(session, "%s", vstring_str(next_command));
        send_state = next_state;
        send_rcpt = next_rcpt;
-    }
+    } while (recv_state != SMTP_STATE_LAST);
     RETURN(0);
 }
+
+/* smtp_xfer - send a batch of envelope information and the message data */
+
+int     smtp_xfer(SMTP_STATE *state)
+{
+    DELIVER_REQUEST *request = state->request;
+    SMTP_SESSION *session = state->session;
+    int     send_state;
+    int     recv_state;
+    int     send_name_addr;
+
+    /*
+     * Sanity check. Recipients should be unmarked at this point.
+     */
+    if (SMTP_RCPT_LEFT(state) <= 0)
+       msg_panic("smtp_xfer: bad recipient count: %d",
+                 SMTP_RCPT_LEFT(state));
+    if (SMTP_RCPT_ISMARKED(request->rcpt_list.info))
+       msg_panic("smtp_xfer: bad recipient status: %d",
+                 request->rcpt_list.info->status);
+
+    /*
+     * See if we should even try to send this message at all. This code sits
+     * here rather than in the EHLO processing code, because of SMTP
+     * connection caching.
+     */
+    if (session->size_limit > 0 && session->size_limit < request->data_size) {
+       smtp_mesg_fail(state, 552,
+                   "message size %lu exceeds size limit %.0f of server %s",
+                      request->data_size, (double) session->size_limit,
+                      session->namaddr);
+       return (0);
+    }
+
+    /*
+     * Use the XFORWARD command to forward client attributes only when a
+     * minimal amount of information is available.
+     */
+    send_name_addr =
+       var_smtp_send_xforward
+       && (((session->features & SMTP_FEATURE_XFORWARD_NAME)
+            && DEL_REQ_ATTR_AVAIL(request->client_name))
+           || ((session->features & SMTP_FEATURE_XFORWARD_ADDR)
+               && DEL_REQ_ATTR_AVAIL(request->client_addr)));
+    session->send_proto_helo =
+       var_smtp_send_xforward
+       && (((session->features & SMTP_FEATURE_XFORWARD_PROTO)
+            && DEL_REQ_ATTR_AVAIL(request->client_proto))
+           || ((session->features & SMTP_FEATURE_XFORWARD_HELO)
+               && DEL_REQ_ATTR_AVAIL(request->client_helo)));
+    if (send_name_addr)
+       recv_state = send_state = SMTP_STATE_XFORWARD_NAME_ADDR;
+    else if (session->send_proto_helo)
+       recv_state = send_state = SMTP_STATE_XFORWARD_PROTO_HELO;
+    else
+       recv_state = send_state = SMTP_STATE_MAIL;
+
+    return (smtp_loop(state, send_state, recv_state));
+}
+
+/* smtp_rset - send a lone RSET command */
+
+int     smtp_rset(SMTP_STATE *state)
+{
+
+    /*
+     * This works because SMTP_STATE_RSET is a dedicated sender/recipient
+     * entry state, with SMTP_STATE_LAST as next sender/recipient state.
+     */
+    return (smtp_loop(state, SMTP_STATE_RSET, SMTP_STATE_RSET));
+}
+
+/* smtp_quit - send a lone QUIT command */
+
+int     smtp_quit(SMTP_STATE *state)
+{
+
+    /*
+     * This works because SMTP_STATE_QUIT is the last state with a sender
+     * action, with SMTP_STATE_LAST as the next sender/recipient state.
+     */
+    return (smtp_loop(state, SMTP_STATE_QUIT, var_skip_quit_resp ?
+                     SMTP_STATE_LAST : SMTP_STATE_QUIT));
+}
diff --git a/postfix/src/smtp/smtp_reuse.c b/postfix/src/smtp/smtp_reuse.c
new file mode 100644 (file)
index 0000000..705e782
--- /dev/null
@@ -0,0 +1,266 @@
+/*++
+/* NAME
+/*     smtp_reuse 3
+/* SUMMARY
+/*     SMTP session cache glue
+/* SYNOPSIS
+/*     #include <smtp.h>
+/*     #include <smtp_reuse.h>
+/*
+/*     void    smtp_save_session(state)
+/*     SMTP_STATE *state;
+/*
+/*     SMTP_SESSION *smtp_reuse_domain(state, lookup_mx, domain, port)
+/*     SMTP_STATE *state;
+/*     int     lookup_mx;
+/*     char    *domain;
+/*     unsigned port;
+/*
+/*     SMTP_SESSION *smtp_reuse_addr(state, addr, port)
+/*     SMTP_STATE *state;
+/*     DNS_RR  *addr;
+/*     unsigned port;
+/* DESCRIPTION
+/*     This module implements the SMTP client specific interface to
+/*     the generic session cache infrastructure.
+/*
+/*     smtp_save_session() stores the current session under the
+/*     next-hop logical destination (if available) and under the
+/*     remote server address.  The SMTP_SESSION object is destroyed.
+/*
+/*     smtp_reuse_domain() looks up a cached session by its logical
+/*     destination, and verifies that the session is still alive.
+/*     The restored session information includes the "best MX" bit.
+/*     The result is null in case of failure.
+/*
+/*     smtp_reuse_addr() looks up a cached session by its server
+/*     address, and verifies that the session is still alive.
+/*     The result is null in case of failure.
+/*
+/*     Arguments:
+/* .IP state
+/*     SMTP client state, including the current session, the original
+/*     next-hop domain, etc.
+/* .IP lookup_mx
+/*     Whether or not the domain is subject to MX lookup.
+/* .IP domain
+/*     Domain name or bare numerical address.
+/* .IP addr
+/*     The remote server name and address.
+/* .IP port
+/*     The remote server port, network byte order.
+/* LICENSE
+/* .ad
+/* .fi
+/*     The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/*     Wietse Venema
+/*     IBM T.J. Watson Research
+/*     P.O. Box 704
+/*     Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <string.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <vstream.h>
+#include <vstring.h>
+#include <htable.h>
+#include <stringops.h>
+
+/* Global library. */
+
+#include <scache.h>
+#include <mail_params.h>
+
+/* Application-specific. */
+
+#include <smtp.h>
+#include <smtp_reuse.h>
+
+ /*
+  * We encode the MX lookup/A lookup method into the name under which SMTP
+  * session information is cached. The following macros serve to make the
+  * remainder of the code less obscure.
+  */
+#define NO_MX_LOOKUP   0
+
+#define SMTP_SCACHE_LABEL(mx_lookup_flag) \
+       ((mx_lookup_flag) ? "%s:%s:%u" : "%s:[%s]:%u")
+
+#define STR(x) vstring_str(x)
+
+/* smtp_save_session - save session under next-hop name and server address */
+
+void    smtp_save_session(SMTP_STATE *state)
+{
+    SMTP_SESSION *session = state->session;
+    int     fd;
+
+    /*
+     * Encode the next-hop logical destination, if available. Reuse storage
+     * that is also used for cache lookup queries.
+     * 
+     * Note: if the label needs to be made more specific (with e.g., SASL login
+     * information), just append the text with vstring_sprintf_append().
+     */
+    if (HAVE_NEXTHOP_STATE(state))
+       vstring_sprintf(state->dest_label,
+                       SMTP_SCACHE_LABEL(state->nexthop_lookup_mx),
+                       state->service, state->nexthop_domain,
+                       ntohs(state->nexthop_port));
+
+    /*
+     * Encode the physical endpoint name. Reuse storage that is also used for
+     * cache lookup queries.
+     * 
+     * Note: if the label needs to be made more specific (with e.g., SASL login
+     * information), just append the text with vstring_sprintf_append().
+     */
+    vstring_sprintf(state->endp_label,
+                   SMTP_SCACHE_LABEL(NO_MX_LOOKUP),
+                   state->service, session->addr, ntohs(session->port));
+
+    /*
+     * Passivate the SMTP_SESSION object, destroying the object in the
+     * process. Reuse storage that is also used for cache lookup results.
+     */
+    fd = smtp_session_passivate(session, state->dest_prop, state->endp_prop);
+    state->session = 0;
+
+    /*
+     * Save the session under the next-hop name, if available.
+     * 
+     * XXX The logical to physical binding can be kept for as long as the DNS
+     * allows us to (but that could result in the caching of lots of unused
+     * bindings). The session should be idle for no more than 30 seconds or
+     * so.
+     */
+    if (HAVE_NEXTHOP_STATE(state))
+       scache_save_dest(smtp_scache, var_smtp_cache_conn, STR(state->dest_label),
+                        STR(state->dest_prop), STR(state->endp_label));
+
+    /*
+     * Save every good sessions under its physical endpoint address.
+     */
+    scache_save_endp(smtp_scache, var_smtp_cache_conn, STR(state->endp_label),
+                    STR(state->endp_prop), fd);
+}
+
+/* smtp_reuse_common - common session reuse code */
+
+static SMTP_SESSION *smtp_reuse_common(SMTP_STATE *state, int fd,
+                                              const char *label)
+{
+    const char *myname = "smtp_reuse_common";
+    SMTP_SESSION *session;
+
+    /*
+     * Re-activate the SMTP_SESSION object.
+     */
+    session = smtp_session_activate(fd, state->dest_prop, state->endp_prop);
+    if (session == 0) {
+       msg_warn("%s: bad cached session attribute for %s", myname, label);
+       (void) close(fd);
+       return (0);
+    }
+    state->session = session;
+
+    /*
+     * Send an RSET probe to verify that the session is still good.
+     */
+    if (smtp_rset(state) < 0
+       || (session->features & SMTP_FEATURE_RSET_REJECTED) != 0) {
+       smtp_session_free(session);
+       return (state->session = 0);
+    }
+
+    /*
+     * Update the list of used cached addresses.
+     */
+    htable_enter(state->cache_used, session->addr, (char *) 0);
+
+    return (session);
+}
+
+/* smtp_reuse_domain - reuse session cached under domain name */
+
+SMTP_SESSION *smtp_reuse_domain(SMTP_STATE *state, int lookup_mx,
+                                       const char *domain, unsigned port)
+{
+    SMTP_SESSION *session;
+    int     fd;
+
+    /*
+     * Look up the session by its logical name.
+     * 
+     * Note: if the label needs to be made more specific (with e.g., SASL login
+     * information), just append the text with vstring_sprintf_append().
+     */
+    vstring_sprintf(state->dest_label, SMTP_SCACHE_LABEL(lookup_mx),
+                   state->service, domain, ntohs(port));
+    if ((fd = scache_find_dest(smtp_scache, STR(state->dest_label),
+                              state->dest_prop, state->endp_prop)) < 0)
+       return (0);
+
+    /*
+     * Re-activate the SMTP_SESSION object, and verify that the session is
+     * still good.
+     */
+    session = smtp_reuse_common(state, fd, STR(state->dest_label));
+    return (session);
+}
+
+/* smtp_reuse_addr - reuse session cached under numerical address */
+
+SMTP_SESSION *smtp_reuse_addr(SMTP_STATE *state, DNS_RR *addr, unsigned port)
+{
+    SMTP_SESSION *session;
+    int     fd;
+
+#define INADDRP(x) ((struct in_addr *) (x))
+
+    /*
+     * Look up the session by its IP address. This means that we have no
+     * destination-to-address binding properties.
+     * 
+     * Note: if the label needs to be made more specific (with e.g., SASL login
+     * information), just append the text with vstring_sprintf_append().
+     */
+    if (addr->data_len > sizeof(struct in_addr))
+       return (0);
+    vstring_sprintf(state->endp_label, SMTP_SCACHE_LABEL(NO_MX_LOOKUP),
+                   state->service, inet_ntoa(*INADDRP(addr->data)),
+                   ntohs(port));
+    if ((fd = scache_find_endp(smtp_scache, STR(state->endp_label),
+                              state->endp_prop)) < 0)
+       return (0);
+    VSTRING_RESET(state->dest_prop);
+    VSTRING_TERMINATE(state->dest_prop);
+
+    /*
+     * Re-activate the SMTP_SESSION object, and verify that the session is
+     * still good.
+     */
+    session = smtp_reuse_common(state, fd, STR(state->endp_label));
+
+    /*
+     * XXX What if hostnames don't match (addr->name versus session->name),
+     * or if the SASL login name for this host does not match the SASL login
+     * name that was used when opening this session? If something depends
+     * critically on such information being identical, then that information
+     * should be included in the logical and physical labels under which a
+     * session is cached.
+     */
+    return (session);
+}
diff --git a/postfix/src/smtp/smtp_reuse.h b/postfix/src/smtp/smtp_reuse.h
new file mode 100644 (file)
index 0000000..9018c9f
--- /dev/null
@@ -0,0 +1,32 @@
+/*++
+/* NAME
+/*     smtp_reuse 3h
+/* SUMMARY
+/*     SMTP session cache glue
+/* SYNOPSIS
+/*     #include <smtp_reuse.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+  * DNS library.
+  */
+#include <dns.h>
+
+ /*
+  * Internal interfaces.
+  */
+extern void smtp_save_session(SMTP_STATE *);
+extern SMTP_SESSION *smtp_reuse_domain(SMTP_STATE *, int, const char *, unsigned);
+extern SMTP_SESSION *smtp_reuse_addr(SMTP_STATE *, DNS_RR *, unsigned);
+
+/* 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
+/*--*/
index c7550aae207b2a10057d740df8a37965918d78fa..cf8c6d59c1669472bacbc5c2f8c6df008c864298 100644 (file)
@@ -21,6 +21,9 @@ extern void smtp_sasl_cleanup(SMTP_SESSION *);
 extern void smtp_sasl_helo_auth(SMTP_SESSION *, const char *);
 extern int smtp_sasl_helo_login(SMTP_STATE *);
 
+extern void smtp_sasl_passivate(SMTP_SESSION *, VSTRING *);
+extern int smtp_sasl_activate(SMTP_SESSION *, char *);
+
 /* LICENSE
 /* .ad
 /* .fi
index 30251c8d37c744c0f32c56587c39ae2ad28651f5..2c0d88a36832d0b89af429b86ea7e9ce8ea04350 100644 (file)
 /*
 /*     void    smtp_sasl_cleanup(session)
 /*     SMTP_SESSION *session;
+/*
+/*     void    smtp_sasl_passivate(session, buf)
+/*     SMTP_SESSION *session;
+/*     VSTRING *buf;
+/*
+/*     int     smtp_sasl_activate(session, buf)
+/*     SMTP_SESSION *session;
+/*     char    *buf;
 /* DESCRIPTION
 /*     smtp_sasl_initialize() initializes the SASL library. This
 /*     routine must be called once at process startup, before any
 /*     end of every SMTP session that uses SASL authentication.
 /*     This routine is a noop for non-SASL sessions.
 /*
+/*     smtp_sasl_passivate() appends flattened SASL attributes to the
+/*     specified buffer. The SASL attributes are not destroyed.
+/*
+/*     smtp_sasl_activate() restores SASL attributes from the
+/*     specified buffer. The buffer is modified. A result < 0
+/*     means there was an error.
+/*
 /*     Arguments:
 /* .IP session
 /*     Session context.
@@ -272,9 +287,15 @@ int     smtp_sasl_passwd_lookup(SMTP_SESSION *session)
      * 
      * XXX Instead of using nexthop (the intended destination) we use dest
      * (either the intended destination, or a fall-back destination).
+     * 
+     * XXX SASL authentication currently depends on the host/domain but not on
+     * the TCP port. If the port is not :25, we should append it to the table
+     * lookup key. Code for this was briefly introduced into 2.2 snapshots,
+     * but didn't canonicalize the TCP port, and did not append the port to
+     * the MX hostname.
      */
     if ((value = maps_find(smtp_sasl_passwd_map, session->host, 0)) != 0
-      || (value = maps_find(smtp_sasl_passwd_map, session->dest, 0)) != 0) {
+    || (value = maps_find(smtp_sasl_passwd_map, session->dest, 0)) != 0) {
        session->sasl_username = mystrdup(value);
        passwd = split_at(session->sasl_username, ':');
        session->sasl_passwd = mystrdup(passwd ? passwd : "");
@@ -575,4 +596,17 @@ void    smtp_sasl_cleanup(SMTP_SESSION *session)
     }
 }
 
+/* smtp_sasl_passivate - append serialized SASL attributes */
+
+void    smtp_sasl_passivate(SMTP_SESSION *session, VSTRING *buf)
+{
+}
+
+/* smtp_sasl_activate - de-serialize SASL attributes */
+
+int     smtp_sasl_activate(SMTP_SESSION *session, char *buf)
+{
+    return (0);
+}
+
 #endif
index 8ac687a2df755b8777e044f86ec00cbf4c3a64f8..9eab20e549c6f8d812e3b88c47b6a06594d713c2 100644 (file)
@@ -6,23 +6,69 @@
 /* SYNOPSIS
 /*     #include "smtp.h"
 /*
-/*     SMTP_SESSION *smtp_session_alloc(stream, dest, host, addr)
+/*     SMTP_SESSION *smtp_session_alloc(stream, dest, host, addr, port, flags)
 /*     VSTREAM *stream;
 /*     char    *dest;
 /*     char    *host;
 /*     char    *addr;
+/*     unsigned port;
+/*     int     flags;
 /*
 /*     void    smtp_session_free(session)
 /*     SMTP_SESSION *session;
+/*
+/*     int     smtp_session_passivate(session, dest_prop, endp_prop)
+/*     SMTP_SESSION *session;
+/*     VSTRING *dest_prop;
+/*     VSTRING *endp_prop;
+/*
+/*     SMTP_SESSION *smtp_session_activate(fd, dest_prop, endp_prop)
+/*     int     fd;
+/*     VSTRING *dest_prop;
+/*     VSTRING *endp_prop;
 /* DESCRIPTION
 /*     smtp_session_alloc() allocates memory for an SMTP_SESSION structure
 /*     and initializes it with the given stream and destination, host name
 /*     and address information.  The host name and address strings are
-/*     copied. The code assumes that the stream is connected to the "best"
-/*     alternative.
+/*     copied. The port is in network byte order.
 /*
 /*     smtp_session_free() destroys an SMTP_SESSION structure and its
-/*     members, making memory available for reuse.
+/*     members, making memory available for reuse. It will handle the
+/*     case of a null stream and will assume it was given a different
+/*     purpose.
+/*
+/*     smtp_session_passivate() flattens an SMTP session so that
+/*     it can be cached. The SMTP_SESSION structure is destroyed.
+/*
+/*     smtp_session_activate() inflates a flattened SMTP session
+/*     so that it can be used. The input is modified.
+/*
+/*     Arguments:
+/* .IP stream
+/*     A full-duplex stream.
+/* .IP dest
+/*     The unmodified next-hop or fall-back destination including
+/*     the optional [] and including the optional port or service.
+/* .IP host
+/*     The name of the host that we are connected to.
+/* .IP addr
+/*     The address of the host that we are connected to.
+/* .IP port
+/*     The remote port, network byte order.
+/* .IP flags
+/*     Zero or more of the following:
+/* .RS
+/* .IP SMTP_SESS_FLAG_CACHE
+/*     Enable session caching.
+/* .RE
+/* .IP
+/*     The manifest constant SMTP_SESS_FLAG_NONE requests no options.
+/* .IP dest_prop
+/*     Destination specific session properties: the server is the
+/*     best MX host for the current logical destination.
+/* .IP endp_prop
+/*     Endpoint specific session properties: all the features
+/*     advertised by the remote server.
 /* LICENSE
 /* .ad
 /* .fi
 /* System library. */
 
 #include <sys_defs.h>
+#include <stdlib.h>
 
 /* Utility library. */
 
+#include <msg.h>
 #include <mymalloc.h>
 #include <vstream.h>
 #include <stringops.h>
 /* Global library. */
 
 #include <mime_state.h>
+#include <debug_peer.h>
+#include <mail_params.h>
 
 /* Application-specific. */
 
 #include "smtp.h"
 
+#define STR(x) vstring_str(x)
+
+/*#define msg_verbose 1*/
+
 /* smtp_session_alloc - allocate and initialize SMTP_SESSION structure */
 
-SMTP_SESSION *smtp_session_alloc(VSTREAM *stream, char *dest, char *host,
-                                        char *addr)
+SMTP_SESSION *smtp_session_alloc(VSTREAM *stream, const char *dest,
+                                        const char *host, const char *addr,
+                                        unsigned port, int flags)
 {
     SMTP_SESSION *session;
 
@@ -65,7 +120,8 @@ SMTP_SESSION *smtp_session_alloc(VSTREAM *stream, char *dest, char *host,
     session->host = mystrdup(host);
     session->addr = mystrdup(addr);
     session->namaddr = concatenate(host, "[", addr, "]", (char *) 0);
-    session->best = 1;
+    session->port = port;
+    session->features = 0;
 
     session->size_limit = 0;
     session->error_mask = 0;
@@ -73,13 +129,22 @@ SMTP_SESSION *smtp_session_alloc(VSTREAM *stream, char *dest, char *host,
     session->scratch = vstring_alloc(100);
     session->scratch2 = vstring_alloc(100);
     smtp_chat_init(session);
-    session->features = SMTP_FEATURE_CACHE_SESSION;
+    session->features = 0;
     session->mime_state = 0;
 
+    session->sndbufsize = 0;
+    session->send_proto_helo = 0;
+
+    if (flags & SMTP_SESS_FLAG_CACHE)
+       session->reuse_count = var_smtp_reuse_limit;
+    else
+       session->reuse_count = 0;
+
 #ifdef USE_SASL_AUTH
     smtp_sasl_connect(session);
 #endif
 
+    debug_peer_check(host, addr);
     return (session);
 }
 
@@ -107,5 +172,166 @@ void    smtp_session_free(SMTP_SESSION *session)
     smtp_sasl_cleanup(session);
 #endif
 
+    debug_peer_restore();
     myfree((char *) session);
 }
+
+/* smtp_session_passivate - passivate an SMTP_SESSION object */
+
+int     smtp_session_passivate(SMTP_SESSION *session, VSTRING *dest_prop,
+                                      VSTRING *endp_prop)
+{
+    int     fd;
+
+    /*
+     * Encode the local-to-physical binding properties: whether or not this
+     * server is best MX host for the next-hop or fall-back logical
+     * destination (this information is needed for loop handling in
+     * smtp_proto()).
+     * 
+     * XXX It would be nice to have a VSTRING to VSTREAM adapter so that we can
+     * serialize the properties with attr_print() instead of using ad-hoc,
+     * non-reusable, code and hard-coded format strings.
+     */
+    vstring_sprintf(dest_prop, "%u",
+                   session->features & SMTP_FEATURE_DESTINATION_MASK);
+
+    /*
+     * Encode the physical endpoint properties: all the session properties
+     * except for "session from cache", "best MX", or "RSET failure".
+     * 
+     * XXX It would be nice to have a VSTRING to VSTREAM adapter so that we can
+     * serialize the properties with attr_print() instead of using obscure
+     * hard-coded format strings.
+     * 
+     * XXX Should also record an absolute time when a session must be closed,
+     * how many non-delivering mail transactions there were during this
+     * session, and perhaps other statistics, so that we don't reuse a
+     * session too much.
+     */
+    vstring_sprintf(endp_prop, "%s\n%s\n%s\n%u\n%u\n%u\n%u",
+                   session->dest, session->host,
+                   session->addr, session->port,
+                   session->features & SMTP_FEATURE_ENDPOINT_MASK,
+                   session->reuse_count,
+                   session->sndbufsize);
+
+    /*
+     * Append the passivated SASL attributes.
+     */
+#ifdef USE_SASL
+    if (smtp_sasl_enable)
+       smtp_sasl_passivate(endp_prop, session);
+#endif
+
+    /*
+     * Salvage the underlying file descriptor, and destroy the session
+     * object.
+     */
+    fd = vstream_fileno(session->stream);
+    vstream_fdclose(session->stream);
+    session->stream = 0;
+    smtp_session_free(session);
+
+    return (fd);
+}
+
+/* smtp_session_activate - re-activate a passivated SMTP_SESSION object */
+
+SMTP_SESSION *smtp_session_activate(int fd, VSTRING *dest_prop,
+                                           VSTRING *endp_prop)
+{
+    const char *myname = "smtp_session_activate";
+    SMTP_SESSION *session;
+    char   *dest_props;
+    char   *endp_props;
+    const char *prop;
+    const char *dest;
+    const char *host;
+    const char *addr;
+    unsigned port;
+    unsigned features;                 /* server features */
+    unsigned reuse_count;              /* how reuses left */
+    unsigned sndbufsize;               /* PIPELINING buffer size */
+
+    /*
+     * XXX it would be nice to have a VSTRING to VSTREAM adapter so that we
+     * can de-serialize the properties with attr_scan(), instead of using
+     * ad-hoc, non-reusable code.
+     * 
+     * XXX As a preliminary solution we use mystrtok(), but that function is not
+     * suitable for zero-length fields.
+     */
+    endp_props = STR(endp_prop);
+    if ((dest = mystrtok(&endp_props, "\n")) == 0) {
+       msg_warn("%s: missing cached session destination property", myname);
+       return (0);
+    }
+    if ((host = mystrtok(&endp_props, "\n")) == 0) {
+       msg_warn("%s: missing cached session hostname property", myname);
+       return (0);
+    }
+    if ((addr = mystrtok(&endp_props, "\n")) == 0) {
+       msg_warn("%s: missing cached session address property", myname);
+       return (0);
+    }
+    if ((prop = mystrtok(&endp_props, "\n")) == 0 || !alldig(prop)) {
+       msg_warn("%s: bad cached session port property", myname);
+       return (0);
+    }
+    port = atoi(prop);
+
+    if ((prop = mystrtok(&endp_props, "\n")) == 0 || !alldig(prop)) {
+       msg_warn("%s: bad cached session features property", myname);
+       return (0);
+    }
+    features = atoi(prop);
+
+    if ((prop = mystrtok(&endp_props, "\n")) == 0 || !alldig(prop)) {
+       msg_warn("%s: bad cached session reuse_count property", myname);
+       return (0);
+    }
+    reuse_count = atoi(prop);
+
+    if ((prop = mystrtok(&endp_props, "\n")) == 0 || !alldig(prop)) {
+       msg_warn("%s: bad cached session sndbufsize property", myname);
+       return (0);
+    }
+    sndbufsize = atoi(prop);
+
+    if (dest_prop && VSTRING_LEN(dest_prop)) {
+       dest_props = STR(dest_prop);
+       if ((prop = mystrtok(&dest_props, "\n")) == 0 || !alldig(prop)) {
+           msg_warn("%s: bad cached destination features property", myname);
+           return (0);
+       }
+       features |= atoi(prop);
+    }
+
+    /*
+     * Allright, bundle up what we have sofar.
+     */
+    session = smtp_session_alloc(vstream_fdopen(fd, O_RDWR),
+                                dest, host, addr, port, SMTP_SESS_FLAG_NONE);
+    session->features = (features | SMTP_FEATURE_FROM_CACHE);
+    session->reuse_count = reuse_count - 1;
+    session->sndbufsize = sndbufsize;
+
+    if (msg_verbose)
+       msg_info("%s: dest=%s host=%s addr=%s port=%u features=0x%x, reuse=%u, sndbuf=%u",
+                myname, dest, host, addr, ntohs(port), features, reuse_count, sndbufsize);
+
+    /*
+     * Re-activate the SASL attributes.
+     */
+#ifdef USE_SASL
+    if (smtp_sasl_enable && smtp_sasl_activate(session, endp_props) < 0) {
+       vstream_fdclose(session->stream);
+       session->stream = 0;
+       smtp_session_free(session);
+       return (0);
+    }
+#endif
+
+    return (session);
+}
index f871a391a3111e3b45ad2d3c390470b94e2f4ebf..404279cd9f3f82911f06ea99ce49081c92cf4e0f 100644 (file)
 
 #include <mymalloc.h>
 #include <vstring.h>
-#include <vstream.h>
 
 /* Global library. */
 
-#include <mail_conf.h>
-#include <mime_state.h>
+#include <mail_params.h>
 
 /* Application-specific. */
 
@@ -57,10 +55,25 @@ SMTP_STATE *smtp_state_alloc(void)
     SMTP_STATE *state = (SMTP_STATE *) mymalloc(sizeof(*state));
 
     state->src = 0;
+    state->service = 0;
     state->request = 0;
     state->session = 0;
     state->status = 0;
     state->space_left = 0;
+    state->nexthop_domain = 0;
+    if (var_smtp_cache_conn) {
+       state->dest_label = vstring_alloc(10);
+       state->dest_prop = vstring_alloc(10);
+       state->endp_label = vstring_alloc(10);
+       state->endp_prop = vstring_alloc(10);
+       state->cache_used = htable_create(1);
+    } else {
+       state->dest_label = 0;
+       state->dest_prop = 0;
+       state->endp_label = 0;
+       state->endp_prop = 0;
+       state->cache_used = 0;
+    }
     return (state);
 }
 
@@ -68,5 +81,15 @@ SMTP_STATE *smtp_state_alloc(void)
 
 void    smtp_state_free(SMTP_STATE *state)
 {
+    if (state->dest_label)
+       vstring_free(state->dest_label);
+    if (state->dest_prop)
+       vstring_free(state->dest_prop);
+    if (state->endp_label)
+       vstring_free(state->endp_label);
+    if (state->endp_prop)
+       vstring_free(state->endp_prop);
+    if (state->cache_used)
+       htable_free(state->cache_used, (void (*) (char *)) 0);
     myfree((char *) state);
 }
index f323dfb9296f8e8e571899cb709ab4f0d4461e7c..435a1ae35413fe11b41723363846d7e7267ba65b 100644 (file)
@@ -223,7 +223,7 @@ int     smtp_site_fail(SMTP_STATE *state, int code, char *format,...)
      * Don't cache this session. We can't talk to this server.
      */
     if (session)
-       session->features &= ~SMTP_FEATURE_CACHE_SESSION;
+       session->reuse_count = 0;
 
     /*
      * Cleanup.
@@ -425,7 +425,7 @@ int     smtp_stream_except(SMTP_STATE *state, int code, char *description)
     /*
      * Don't attempt to cache this session.
      */
-    session->features &= ~SMTP_FEATURE_CACHE_SESSION;
+    session->reuse_count = 0;
 
     /*
      * Cleanup.
index 292ac1fca4d8fe8ce98ff577705a28e0dd15d7d8..254f39e2147b65affef82c4dd6f1d2448f0f5ff8 100644 (file)
@@ -61,7 +61,7 @@ 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 *"\([^"]*\)".*/{' \
+           $(CC) -E $(DEFS) $(INCL) $$i | grep -v '[<>]' | 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
index 18951700643094cd85abda8bd83ecbaba49c057e..f3738bcc633caae34f7d2a0e088fd2af237b7906 100644 (file)
@@ -67,7 +67,7 @@ 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 *"\([^"]*\)".*/{' \
+           $(CC) -E $(DEFS) $(INCL) $$i | grep -v '[<>]' | 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
index 3a1cdb12e92784bfc829cc1b04eb3f9a09bad38e..be7554fa8d04aabafb6d0da1033245da8dfb8882 100644 (file)
@@ -46,7 +46,7 @@ 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 *"\([^"]*\)".*/{' \
+           $(CC) -E $(DEFS) $(INCL) $$i | grep -v '[<>]' | 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
index 94f6d89aadccd6b237a819f442c093f1c70eaff2..0b734b3481465ab93e379199e24ff62ce2d43db3 100644 (file)
@@ -52,7 +52,7 @@ 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 *"\([^"]*\)".*/{' \
+           $(CC) -E $(DEFS) $(INCL) $$i | grep -v '[<>]' | 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
index 422390ca35efd14b97a32a3f762d2542a2162740..91ed1ba722b7ad9fe54cf8e616d85185bee2c57a 100644 (file)
@@ -28,7 +28,7 @@ SRCS  = alldig.c argv.c argv_split.c attr_print0.c attr_print64.c \
        vstream_popen.c vstring.c vstring_vstream.c watchdog.c writable.c \
        write_buf.c write_wait.c auto_clnt.c attr_clnt.c attr_scan_plain.c \
        attr_print_plain.c sane_connect.c neuter.c name_code.c \
-       uppercase.c
+       uppercase.c unix_recv_fd.c stream_recv_fd.c unix_send_fd.c stream_send_fd.c
 OBJS   = alldig.o argv.o argv_split.o attr_print0.o attr_print64.o \
        attr_scan0.o attr_scan64.o base64_code.o basename.o binhash.o \
        chroot_uid.o clean_env.o close_on_exec.o concatenate.o ctable.o \
@@ -58,7 +58,7 @@ OBJS  = alldig.o argv.o argv_split.o attr_print0.o attr_print64.o \
        vstream_popen.o vstring.o vstring_vstream.o watchdog.o writable.o \
        write_buf.o write_wait.o auto_clnt.o attr_clnt.o attr_scan_plain.o \
        attr_print_plain.o sane_connect.o $(STRCASE) neuter.o name_code.o \
-       uppercase.o
+       uppercase.o unix_recv_fd.o stream_recv_fd.o unix_send_fd.o stream_send_fd.o
 HDRS   = argv.h attr.h base64_code.h binhash.h chroot_uid.h clean_env.h \
        connect.h ctable.h dict.h dict_db.h dict_dbm.h dict_env.h \
        dict_cidr.h dict_ht.h dict_ni.h dict_nis.h \
@@ -92,7 +92,8 @@ TESTPROG= dict_open dup2_pass_on_exec events exec_command fifo_open \
        vstring vstring_vstream doze select_bug stream_test mac_expand \
        watchdog unescape hex_quote name_mask rand_sleep sane_time ctable \
        inet_addr_list attr_print64 attr_scan64 base64_code attr_print0 \
-       attr_scan0 host_port attr_scan_plain attr_print_plain htable
+       attr_scan0 host_port attr_scan_plain attr_print_plain htable \
+       unix_recv_fd unix_send_fd stream_recv_fd stream_send_fd
 
 LIB_DIR        = ../../lib
 INC_DIR        = ../../include
@@ -341,10 +342,30 @@ htable: $(LIB)
        $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS)
        mv junk $@.o
 
+unix_recv_fd:  $(LIB)
+       mv $@.o junk
+       $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS)
+       mv junk $@.o
+
+unix_send_fd:  $(LIB)
+       mv $@.o junk
+       $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS)
+       mv junk $@.o
+
+stream_recv_fd:  $(LIB)
+       mv $@.o junk
+       $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS)
+       mv junk $@.o
+
+stream_send_fd:  $(LIB)
+       mv $@.o junk
+       $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS)
+       mv junk $@.o
+
 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 *"\([^"]*\)".*/{' \
+           $(CC) -E $(DEFS) $(INCL) $$i | grep -v '[<>]' | 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
@@ -1211,6 +1232,14 @@ stream_listen.o: sys_defs.h
 stream_listen.o: msg.h
 stream_listen.o: listen.h
 stream_listen.o: iostuff.h
+stream_recv_fd.o: stream_recv_fd.c
+stream_recv_fd.o: sys_defs.h
+stream_recv_fd.o: msg.h
+stream_recv_fd.o: iostuff.h
+stream_send_fd.o: stream_send_fd.c
+stream_send_fd.o: sys_defs.h
+stream_send_fd.o: msg.h
+stream_send_fd.o: iostuff.h
 stream_test.o: stream_test.c
 stream_test.o: sys_defs.h
 stream_test.o: stream_test.c
@@ -1278,6 +1307,14 @@ unix_listen.o: msg.h
 unix_listen.o: iostuff.h
 unix_listen.o: listen.h
 unix_listen.o: sane_accept.h
+unix_recv_fd.o: unix_recv_fd.c
+unix_recv_fd.o: sys_defs.h
+unix_recv_fd.o: msg.h
+unix_recv_fd.o: iostuff.h
+unix_send_fd.o: unix_send_fd.c
+unix_send_fd.o: sys_defs.h
+unix_send_fd.o: msg.h
+unix_send_fd.o: iostuff.h
 unix_trigger.o: unix_trigger.c
 unix_trigger.o: sys_defs.h
 unix_trigger.o: msg.h
index 25af73f47a6cb1ef92edcf49706b53baf5ce9e54..436693075788a66c20edd73f0c7f1fd2d5a2b4df 100644 (file)
@@ -261,9 +261,10 @@ void    event_drain(int time_limit)
        return;
 
     FD_ZERO(&zero_mask);
+    (void) time(&event_present);
     max_time = event_present + time_limit;
     while (event_present < max_time
-          && (event_timer_head.pred != event_timer_head.succ
+          && (event_timer_head.pred != &event_timer_head
               || memcmp(&zero_mask, &event_xmask, sizeof(zero_mask)) != 0))
        event_loop(1);
 }
@@ -493,6 +494,20 @@ void    event_loop(int delay)
     if (EVENT_INIT_NEEDED())
        event_init();
 
+    /*
+     * XXX Also print the select() masks?
+     */
+    if (msg_verbose > 2) {
+       RING   *ring;
+
+       FOREACH_QUEUE_ENTRY(ring, &event_timer_head) {
+           timer = RING_TO_TIMER(ring);
+           msg_info("%s: time left %3d for 0x%lx 0x%lx", myname,
+                    (int) (timer->when - event_present),
+                    (long) timer->callback, (long) timer->context);
+       }
+    }
+
     /*
      * Find out when the next timer would go off. Timer requests are sorted.
      * If any timer is scheduled, adjust the delay appropriately.
@@ -598,23 +613,19 @@ void    event_loop(int delay)
 #ifdef TEST
 
  /*
-  * Proof-of-concept test program for the event manager. Print "dingdong"
-  * every 5 seconds, while echoing any lines read from stdin. The "dingdong
-  * changes case each time it is printed.
+  * Proof-of-concept test program for the event manager. Schedule a series
+of events at one-second intervals and let them happen,
+  while echoing any lines read from stdin. 
   */
 #include <stdio.h>
 #include <ctype.h>
 
-#define DELAY 5
+/* timer_event - display event */
 
-/* dingdong - print text every DELAY seconds */
-
-static void dingdong(char *context)
+static void timer_event(int unused_event, char *context)
 {
-    printf("%c", *context);
+    printf("%ld: %s\n", (long) event_present, context);
     fflush(stdout);
-    *context = (ISUPPER(*context) ? TOLOWER(*context) : TOUPPER(*context));
-    event_request_timer(dingdong, context, (char *) DELAY);
 }
 
 /* echo - echo text received on stdin */
@@ -628,13 +639,14 @@ static void echo(int unused_event, char *unused_context)
     printf("Result: %s", buf);
 }
 
-main(void)
+int main(void)
 {
-    static char text[] = "\rdingdong ";
-    char   *cp;
-
-    for (cp = text; *cp; cp++)
-       event_request_timer(dingdong, cp, (char *) 0);
+    event_request_timer(timer_event, "3 first", 3);
+    event_request_timer(timer_event, "3 second", 3);
+    event_request_timer(timer_event, "4 first", 4);
+    event_request_timer(timer_event, "4 second", 4);
+    event_request_timer(timer_event, "2 first", 2);
+    event_request_timer(timer_event, "2 second", 2);
     event_enable_read(fileno(stdin), echo, (char *) 0);
     for (;;)
        event_loop(-1);
index 7da0f297db022e0f913aff5deeaa75ad43fe8e52..05c57d1cb36280f0445ae4d3870ae2d93f9eb201 100644 (file)
@@ -29,6 +29,10 @@ extern int timed_write(int, void *, unsigned, int, void *);
 extern void doze(unsigned);
 extern void rand_sleep(unsigned, unsigned);
 extern int duplex_pipe(int *);
+extern int stream_recv_fd(int);
+extern int stream_send_fd(int, int);
+extern int unix_recv_fd(int);
+extern int unix_send_fd(int, int);
 
 #define BLOCKING       0
 #define NON_BLOCKING   1
index d0219cf804cc27b0784661db3e32280ae0b72b04..47749e276af743867a2e5057b30939e275bf78c8 100644 (file)
@@ -29,6 +29,9 @@ extern void ring_detach(RING *);
 #define ring_succ(c) ((c)->succ)
 #define ring_pred(c) ((c)->pred)
 
+#define RING_FOREACH(entry, head) \
+    for (entry = ring_succ(head); entry != (head); entry = ring_succ(entry))
+
  /*
   * Typically, an application will embed a RING structure into a larger
   * structure that also contains application-specific members. This approach
diff --git a/postfix/src/util/stream_recv_fd.c b/postfix/src/util/stream_recv_fd.c
new file mode 100644 (file)
index 0000000..9698d7a
--- /dev/null
@@ -0,0 +1,118 @@
+/*++
+/* NAME
+/*     stream_recv_fd 3
+/* SUMMARY
+/*     receive file descriptor
+/* SYNOPSIS
+/*     #include <iostuff.h>
+/*
+/*     int     stream_recv_fd(fd)
+/*     int     fd;
+/* DESCRIPTION
+/*     stream_recv_fd() receives a file descriptor via the specified
+/*     stream. The result value is the received descriptor.
+/*
+/*     Arguments:
+/* .IP fd
+/*     File descriptor.
+/* DIAGNOSTICS
+/*     stream_recv_fd() returns -1 upon 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
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>                  /* includes <sys/types.h> */
+
+#ifdef STREAM_CONNECTIONS
+
+#include <sys/stat.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stropts.h>
+#include <fcntl.h>
+
+#endif
+
+/* Utility library. */
+
+#include <msg.h>
+#include <iostuff.h>
+
+/* stream_recv_fd - receive file descriptor */
+
+int     stream_recv_fd(int fd)
+{
+#ifdef STREAM_CONNECTIONS
+    struct strrecvfd fdinfo;
+
+    /*
+     * This will return EAGAIN on a non-blocking stream when someone else
+     * snatched the connection from us.
+     */
+    if (ioctl(fd, I_RECVFD, &fdinfo) < 0)
+       return (-1);
+    return (fdinfo.fd);
+#else
+    msg_fatal("stream connections are not implemented");
+#endif
+}
+
+#ifdef TEST
+
+ /*
+  * Proof-of-concept program. Receive a descriptor (presumably from the
+  * stream_send_fd test program) and copy its content until EOF.
+  */
+#include <unistd.h>
+#include <split_at.h>
+#include <listen.h>
+
+int     main(int argc, char **argv)
+{
+    char   *transport;
+    char   *endpoint;
+    int     listen_sock;
+    int     client_sock;
+    int     client_fd;
+    ssize_t read_count;
+    char    buf[1024];
+
+    if (argc != 2
+       || (endpoint = split_at(transport = argv[1], ':')) == 0
+       || *endpoint == 0 || *transport == 0)
+       msg_fatal("usage: %s transport:endpoint", argv[0]);
+
+    if (strcmp(transport, "stream") == 0) {
+       listen_sock = stream_listen(endpoint, BLOCKING, 0);
+    } else {
+       msg_fatal("invalid transport name: %s", transport);
+    }
+    if (listen_sock < 0)
+       msg_fatal("listen %s:%s: %m", transport, endpoint);
+
+    client_sock = stream_accept(listen_sock);
+    if (client_sock < 0)
+       msg_fatal("stream_accept: %m");
+
+    while ((client_fd = stream_recv_fd(client_sock)) >= 0) {
+       msg_info("client_fd = %d", client_fd);
+       while ((read_count = read(client_fd, buf, sizeof(buf))) > 0)
+           write(1, buf, read_count);
+       if (read_count < 0)
+           msg_fatal("read: %m");
+       if (close(client_fd) != 0)
+           msg_fatal("close(%d): %m", client_fd);
+    }
+    exit(0);
+}
+
+#endif
diff --git a/postfix/src/util/stream_send_fd.c b/postfix/src/util/stream_send_fd.c
new file mode 100644 (file)
index 0000000..4107837
--- /dev/null
@@ -0,0 +1,112 @@
+/*++
+/* NAME
+/*     stream_send_fd 3
+/* SUMMARY
+/*     send file descriptor
+/* SYNOPSIS
+/*     #include <iostuff.h>
+/*
+/*     int     stream_send_fd(fd, sendfd)
+/*     int     fd;
+/*     int     sendfd;
+/* DESCRIPTION
+/*     stream_send_fd() sends a file descriptor over the specified
+/*     stream.
+/*
+/*     Arguments:
+/* .IP fd
+/*     File descriptor.
+/* .IP sendfd
+/*     Another file descriptor.
+/* DIAGNOSTICS
+/*     stream_send_fd() returns -1 upon 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
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>                  /* includes <sys/types.h> */
+
+#ifdef STREAM_CONNECTIONS
+
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <stropts.h>
+
+#endif
+
+/* Utility library. */
+
+#include <msg.h>
+#include <iostuff.h>
+
+/* stream_send_fd - send file descriptor */
+
+int     stream_send_fd(int fd, int sendfd)
+{
+    char   *myname = "stream_send_fd";
+
+#ifdef STREAM_CONNECTIONS
+    if (ioctl(fd, I_SENDFD, sendfd) < 0)
+       msg_fatal("%s: send file descriptor: %m", myname);
+#else
+    msg_fatal("stream connections are not implemented");
+#endif
+}
+
+#ifdef TEST
+
+ /*
+  * Proof-of-concept program. Open a file and send the descriptor, presumably
+  * to the stream_recv_fd test program.
+  */
+#include <unistd.h>
+#include <fcntl.h>
+#include <split_at.h>
+#include <connect.h>
+
+int     main(int argc, char **argv)
+{
+    char   *transport;
+    char   *endpoint;
+    char   *path;
+    int     server_sock;
+    int     client_fd;
+
+    if (argc < 3
+       || (endpoint = split_at(transport = argv[1], ':')) == 0
+       || *endpoint == 0 || *transport == 0)
+       msg_fatal("usage: %s transport:endpoint file...", argv[0]);
+
+    if (strcmp(transport, "stream") == 0) {
+       server_sock = stream_connect(endpoint, BLOCKING, 0);
+    } else {
+       msg_fatal("invalid transport name: %s", transport);
+    }
+    if (server_sock < 0)
+       msg_fatal("connect %s:%s: %m", transport, endpoint);
+
+    argv += 2;
+    while ((path = *argv++) != 0) {
+       if ((client_fd = open(path, O_RDONLY, 0)) < 0)
+           msg_fatal("open %s: %m", path);
+       msg_info("path=%s client_fd=%d", path, client_fd);
+       if (stream_send_fd(server_sock, client_fd) < 0)
+           msg_fatal("send file descriptor: %m");
+       if (close(client_fd) != 0)
+           msg_fatal("close(%d): %m");
+    }
+    exit(0);
+}
+
+#endif
index 264467cfb15fc6733b89b86c2f32275a56c164d2..8841e2202a772079f1f4e858980fef50c422d5cd 100644 (file)
@@ -298,6 +298,8 @@ extern int opterr;
 #define LOCAL_ACCEPT   stream_accept
 #define LOCAL_CONNECT  stream_connect
 #define LOCAL_TRIGGER  stream_trigger
+#define LOCAL_SEND_FD  stream_send_fd
+#define LOCAL_RECV_FD  stream_recv_fd
 #define HAS_VOLATILE_LOCKS
 #define BROKEN_READ_SELECT_ON_TCP_SOCKET
 
@@ -993,6 +995,8 @@ extern int dup2_pass_on_exec(int oldd, int newd);
 #define LOCAL_ACCEPT   unix_accept
 #define LOCAL_CONNECT  unix_connect
 #define LOCAL_TRIGGER  unix_trigger
+#define LOCAL_SEND_FD  unix_send_fd
+#define LOCAL_RECV_FD  unix_recv_fd
 #endif
 
 #if !defined (HAVE_SYS_NDIR_H) && !defined (HAVE_SYS_DIR_H) \
diff --git a/postfix/src/util/unix_recv_fd.c b/postfix/src/util/unix_recv_fd.c
new file mode 100644 (file)
index 0000000..43ddfe6
--- /dev/null
@@ -0,0 +1,156 @@
+/*++
+/* NAME
+/*     unix_recv_fd 3
+/* SUMMARY
+/*     receive file descriptor
+/* SYNOPSIS
+/*     #include <iostuff.h>
+/*
+/*     int     unix_recv_fd(fd)
+/*     int     fd;
+/* DESCRIPTION
+/*     unix_recv_fd() receives a file descriptor via the specified
+/*     UNIX-domain socket. The result value is the received descriptor.
+/*
+/*     Arguments:
+/* .IP fd
+/*     File descriptor.
+/* DIAGNOSTICS
+/*     unix_recv_fd() returns -1 upon 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
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>                  /* includes <sys/types.h> */
+#include <sys/socket.h>
+#include <sys/uio.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <iostuff.h>
+
+/* unix_recv_fd - receive file descriptor */
+
+int     unix_recv_fd(int fd)
+{
+    char   *myname = "unix_recv_fd";
+    struct msghdr msg;
+    int     newfd;
+    struct iovec iov[1];
+    char    buf[1];
+
+    /*
+     * Adapted from: W. Richard Stevens, UNIX Network Programming, Volume 1,
+     * Second edition.
+     */
+#if defined(CMSG_SPACE) && !defined(NO_MSGHDR_MSG_CONTROL)
+    union {
+       struct msghdr just_for_alignment;
+       char    control[CMSG_SPACE(sizeof(newfd))];
+    }       control_un;
+    struct cmsghdr *cmptr;
+
+    msg.msg_control = control_un.control;
+    msg.msg_controllen = sizeof(control_un.control);
+#else
+    msg.msg_accrights = (char *) &newfd;
+    msg.msg_accrightslen = sizeof(newfd);
+#endif
+
+    msg.msg_name = 0;
+    msg.msg_namelen = 0;
+
+    /*
+     * XXX We don't want to pass any data, just a file descriptor. However,
+     * setting msg.msg_iov = 0 and msg.msg_iovlen = 0 causes trouble: we need
+     * to read_wait() before we can receive the descriptor, and the code
+     * fails after the first descriptor when we attempt to receive a sequence
+     * of descriptors.
+     */
+    iov->iov_base = buf;
+    iov->iov_len = sizeof(buf);
+    msg.msg_iov = iov;
+    msg.msg_iovlen = 1;
+
+    if (recvmsg(fd, &msg, 0) < 0)
+       return (-1);
+
+#if defined(CMSG_SPACE) && !defined(NO_MSGHDR_MSG_CONTROL)
+    if ((cmptr = CMSG_FIRSTHDR(&msg)) != 0
+       && cmptr->cmsg_len == CMSG_LEN(sizeof(newfd))) {
+       if (cmptr->cmsg_level != SOL_SOCKET)
+           msg_fatal("%s: control level %d != SOL_SOCKET",
+                     myname, cmptr->cmsg_level);
+       if (cmptr->cmsg_type != SCM_RIGHTS)
+           msg_fatal("%s: control type %d != SCM_RIGHTS",
+                     myname, cmptr->cmsg_type);
+       return (*(int *) CMSG_DATA(cmptr));
+    } else
+       return (-1);
+#else
+    if (msg.msg_accrightslen == sizeof(newfd))
+       return (newfd);
+    else
+       return (-1);
+#endif
+}
+
+#ifdef TEST
+
+ /*
+  * Proof-of-concept program. Receive a descriptor (presumably from the
+  * unix_send_fd test program) and copy its content until EOF.
+  */
+#include <unistd.h>
+#include <split_at.h>
+#include <listen.h>
+
+int     main(int argc, char **argv)
+{
+    char   *transport;
+    char   *endpoint;
+    int     listen_sock;
+    int     client_sock;
+    int     client_fd;
+    ssize_t read_count;
+    char    buf[1024];
+
+    if (argc != 2
+       || (endpoint = split_at(transport = argv[1], ':')) == 0
+       || *endpoint == 0 || *transport == 0)
+       msg_fatal("usage: %s transport:endpoint", argv[0]);
+
+    if (strcmp(transport, "unix") == 0) {
+       listen_sock = unix_listen(endpoint, 10, BLOCKING);
+    } else {
+       msg_fatal("invalid transport name: %s", transport);
+    }
+    if (listen_sock < 0)
+       msg_fatal("listen %s:%s: %m", transport, endpoint);
+
+    client_sock = accept(listen_sock, (struct sockaddr *) 0, (SOCKADDR_SIZE) 0);
+    if (client_sock < 0)
+       msg_fatal("accept: %m");
+
+    while ((client_fd = unix_recv_fd(client_sock)) >= 0) {
+       msg_info("client_fd = %d", client_fd);
+       while ((read_count = read(client_fd, buf, sizeof(buf))) > 0)
+           write(1, buf, read_count);
+       if (read_count < 0)
+           msg_fatal("read: %m");
+       close(client_fd);
+    }
+    exit(0);
+}
+
+#endif
diff --git a/postfix/src/util/unix_send_fd.c b/postfix/src/util/unix_send_fd.c
new file mode 100644 (file)
index 0000000..6fcc511
--- /dev/null
@@ -0,0 +1,137 @@
+/*++
+/* NAME
+/*     unix_send_fd 3
+/* SUMMARY
+/*     send file descriptor
+/* SYNOPSIS
+/*     #include <iostuff.h>
+/*
+/*     int     unix_send_fd(fd, sendfd)
+/*     int     fd;
+/*     int     sendfd;
+/* DESCRIPTION
+/*     unix_send_fd() sends a file descriptor over the specified
+/*     UNIX-domain socket.
+/*
+/*     Arguments:
+/* .IP fd
+/*     File descriptor.
+/* .IP sendfd
+/*     Another file descriptor.
+/* DIAGNOSTICS
+/*     unix_send_fd() returns -1 upon 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
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>                  /* includes <sys/types.h> */
+#include <sys/socket.h>
+#include <sys/uio.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <iostuff.h>
+
+/* unix_send_fd - send file descriptor */
+
+int     unix_send_fd(int fd, int sendfd)
+{
+    struct msghdr msg;
+    struct iovec iov[1];
+
+    /*
+     * Adapted from: W. Richard Stevens, UNIX Network Programming, Volume 1,
+     * Second edition.
+     */
+#if defined(CMSG_SPACE) && !defined(NO_MSGHDR_MSG_CONTROL)
+    union {
+       struct msghdr just_for_alignment;
+       char    control[CMSG_SPACE(sizeof(sendfd))];
+    }       control_un;
+    struct cmsghdr *cmptr;
+
+    msg.msg_control = control_un.control;
+    msg.msg_controllen = sizeof(control_un.control);
+
+    cmptr = CMSG_FIRSTHDR(&msg);
+    cmptr->cmsg_len = CMSG_LEN(sizeof(sendfd));
+    cmptr->cmsg_level = SOL_SOCKET;
+    cmptr->cmsg_type = SCM_RIGHTS;
+    *(int *) CMSG_DATA(cmptr) = sendfd;
+#else
+    msg.msg_accrights = (char *) &sendfd;
+    msg.msg_accrightslen = sizeof(sendfd);
+#endif
+
+    msg.msg_name = 0;
+    msg.msg_namelen = 0;
+
+    /*
+     * XXX We don't want to pass any data, just a file descriptor. However,
+     * setting msg.msg_iov = 0 and msg.msg_iovlen = 0 causes trouble. See the
+     * comments in the unix_recv_fd() routine.
+     */
+    iov->iov_base = "";
+    iov->iov_len = 1;
+    msg.msg_iov = iov;
+    msg.msg_iovlen = 1;
+
+    return (sendmsg(fd, &msg, 0));
+}
+
+#ifdef TEST
+
+ /*
+  * Proof-of-concept program. Open a file and send the descriptor, presumably
+  * to the unix_recv_fd test program.
+  */
+#include <unistd.h>
+#include <fcntl.h>
+#include <split_at.h>
+#include <connect.h>
+
+int     main(int argc, char **argv)
+{
+    char   *transport;
+    char   *endpoint;
+    char   *path;
+    int     server_sock;
+    int     client_fd;
+
+    if (argc < 3
+       || (endpoint = split_at(transport = argv[1], ':')) == 0
+       || *endpoint == 0 || *transport == 0)
+       msg_fatal("usage: %s transport:endpoint file...", argv[0]);
+
+    if (strcmp(transport, "unix") == 0) {
+       server_sock = unix_connect(endpoint, BLOCKING, 0);
+    } else {
+       msg_fatal("invalid transport name: %s", transport);
+    }
+    if (server_sock < 0)
+       msg_fatal("connect %s:%s: %m", transport, endpoint);
+
+    argv += 2;
+    while ((path = *argv++) != 0) {
+       if ((client_fd = open(path, O_RDONLY, 0)) < 0)
+           msg_fatal("open %s: %m", path);
+       msg_info("path=%s fd=%d", path, client_fd);
+       if (unix_send_fd(server_sock, client_fd) < 0)
+           msg_fatal("send file descriptor: %m");
+       if (close(client_fd) != 0)
+           msg_fatal("close(%d): %m", client_fd);
+    }
+    exit(0);
+}
+
+#endif
index fc502d18c465736bed5e778171ec332a5a1a3949..b67553bf67cd314a4189d0b73a6158f3a75ddb81 100644 (file)
@@ -18,6 +18,9 @@
 /*     int     vstream_fclose(stream)
 /*     VSTREAM *stream;
 /*
+/*     int     vstream_fdclose(stream)
+/*     VSTREAM *stream;
+/*
 /*     VSTREAM *vstream_printf(format, ...)
 /*     char    *format;
 /*
 /*     vstream_fclose() closes the named buffered stream. The result
 /*     is 0 in case of success, VSTREAM_EOF in case of problems.
 /*
+/*     vstream_fdclose() leaves the file(s) open but is otherwise
+/*     identical to vstream_fclose().
+/*
 /*     vstream_fprintf() formats its arguments according to the
 /*     \fIformat\fR argument and writes the result to the named stream.
 /*     The result is the stream argument. It understands the s, c, d, u,
@@ -642,7 +648,7 @@ static int vstream_buf_get_ready(VBUF *bp)
      */
     if (bp->data == 0) {
        vstream_buf_alloc(bp, VSTREAM_BUFSIZE);
-       if (bp->flags & VSTREAM_FLAG_DOUBLE) 
+       if (bp->flags & VSTREAM_FLAG_DOUBLE)
            VSTREAM_SAVE_STATE(stream, read_buf, read_fd);
     }
 
@@ -727,7 +733,7 @@ static int vstream_buf_put_ready(VBUF *bp)
      */
     if (bp->data == 0) {
        vstream_buf_alloc(bp, VSTREAM_BUFSIZE);
-       if (bp->flags & VSTREAM_FLAG_DOUBLE) 
+       if (bp->flags & VSTREAM_FLAG_DOUBLE)
            VSTREAM_SAVE_STATE(stream, write_buf, write_fd);
     } else if (bp->cnt <= 0) {
        if (VSTREAM_FFLUSH_SOME(stream))
@@ -970,20 +976,29 @@ int     vstream_fclose(VSTREAM *stream)
 {
     int     err;
 
+    /*
+     * NOTE: Negative file descriptors are not part of the external
+     * interface. They are for internal use only, in order to support
+     * vstream_fdclose() without a lot of code duplication. Applications that
+     * rely on negative VSTREAM file descriptors will break without warning.
+     */
     if (stream->pid != 0)
        msg_panic("vstream_fclose: stream has process");
     if ((stream->buf.flags & VSTREAM_FLAG_WRITE_DOUBLE) != 0)
        vstream_fflush(stream);
     err = vstream_ferror(stream);
     if (stream->buf.flags & VSTREAM_FLAG_DOUBLE) {
-       err |= close(stream->read_fd);
+       if (stream->read_fd >= 0)
+           err |= close(stream->read_fd);
        if (stream->write_fd != stream->read_fd)
-           err |= close(stream->write_fd);
+           if (stream->write_fd >= 0)
+               err |= close(stream->write_fd);
        vstream_buf_wipe(&stream->read_buf);
        vstream_buf_wipe(&stream->write_buf);
        stream->buf = stream->read_buf;
     } else {
-       err |= close(stream->fd);
+       if (stream->fd >= 0)
+           err |= close(stream->fd);
        vstream_buf_wipe(&stream->buf);
     }
     if (stream->path)
@@ -995,6 +1010,18 @@ int     vstream_fclose(VSTREAM *stream)
     return (err ? VSTREAM_EOF : 0);
 }
 
+/* vstream_fdclose - close stream, leave file(s) open */
+
+int     vstream_fdclose(VSTREAM *stream)
+{
+    if (stream->buf.flags & VSTREAM_FLAG_DOUBLE) {
+       stream->read_fd = stream->write_fd = -1;
+    } else {
+       stream->fd = -1;
+    }
+    return (vstream_fclose(stream));
+}
+
 /* vstream_printf - formatted print to stdout */
 
 VSTREAM *vstream_printf(const char *fmt,...)
index 13f66e83624e1c8fa3270b96c1602dd51c8540d9..2e3824a2aef91f4f0631e043fd3b890958362ca4 100644 (file)
@@ -78,6 +78,7 @@ extern off_t vstream_ftell(VSTREAM *);
 extern int vstream_fflush(VSTREAM *);
 extern int vstream_fputs(const char *, VSTREAM *);
 extern VSTREAM *vstream_fdopen(int, int);
+extern int vstream_fdclose(VSTREAM *);
 
 #define vstream_fread(v, b, n) vbuf_read(&(v)->buf, (b), (n))
 #define vstream_fwrite(v, b, n)        vbuf_write(&(v)->buf, (b), (n))
index 2daf6659e657d14c2b6aa1f2236bcef417288abd..1748ec7086c051581d73834209b8f0dc64bc9c51 100644 (file)
@@ -46,7 +46,7 @@ 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 *"\([^"]*\)".*/{' \
+           $(CC) -E $(DEFS) $(INCL) $$i | grep -v '[<>]' | 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
index fda53bd6d37687702e5207247ef0444cb32dbbe2..3389ce566ae5fde70047506fc96b3e9b926bfcbd 100644 (file)
@@ -47,7 +47,7 @@ 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 *"\([^"]*\)".*/{' \
+           $(CC) -E $(DEFS) $(INCL) $$i | grep -v '[<>]' | 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