From: Wietse Venema Date: Tue, 20 Jul 2004 05:00:00 +0000 (-0500) Subject: postfix-2.2-20040720 X-Git-Tag: v2.2.0-RC1~50 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=a360b3aff137e8cd9b65591cdc960ddad2b26b91;p=thirdparty%2Fpostfix.git postfix-2.2-20040720 --- diff --git a/postfix/.indent.pro b/postfix/.indent.pro index fbd8874f6..6c604ed34 100644 --- a/postfix/.indent.pro +++ b/postfix/.indent.pro @@ -1,3 +1,4 @@ +-TSCACHE_HEAD_NODE -TABOUNCE -TALIAS_TOKEN -TANVIL_LOCAL @@ -142,6 +143,15 @@ -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 diff --git a/postfix/HISTORY b/postfix/HISTORY index 5d1e4644d..6517f04d5 100644 --- a/postfix/HISTORY +++ b/postfix/HISTORY @@ -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. diff --git a/postfix/Makefile.in b/postfix/Makefile.in index 53aa735bc..52faccc7f 100644 --- a/postfix/Makefile.in +++ b/postfix/Makefile.in @@ -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 diff --git a/postfix/README_FILES/OVERVIEW b/postfix/README_FILES/OVERVIEW index 62c1d804a..8ea7eb0f7 100644 --- a/postfix/README_FILES/OVERVIEW +++ b/postfix/README_FILES/OVERVIEW @@ -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. diff --git a/postfix/RELEASE_NOTES b/postfix/RELEASE_NOTES index 6105973ea..aa0ad76c7 100644 --- a/postfix/RELEASE_NOTES +++ b/postfix/RELEASE_NOTES @@ -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. diff --git a/postfix/conf/master.cf b/postfix/conf/master.cf index 0c2475ee0..00ec103e1 100644 --- a/postfix/conf/master.cf +++ b/postfix/conf/master.cf @@ -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. diff --git a/postfix/conf/post-install b/postfix/conf/post-install index c19add3e3..f23d9b5c1 100644 --- a/postfix/conf/post-install +++ b/postfix/conf/post-install @@ -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 <$@ +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 >$@ diff --git a/postfix/html/OVERVIEW.html b/postfix/html/OVERVIEW.html index 02ab2e223..f3e1c747b 100644 --- a/postfix/html/OVERVIEW.html +++ b/postfix/html/OVERVIEW.html @@ -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.

+
  • 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.

    diff --git a/postfix/html/cleanup.8.html b/postfix/html/cleanup.8.html index cfc6ce6e7..c9d3beb2f 100644 --- a/postfix/html/cleanup.8.html +++ b/postfix/html/cleanup.8.html @@ -131,8 +131,8 @@ CLEANUP(8) CLEANUP(8) strings. mime_nesting_limit (100) - The maximal nesting level of multipart mail that - the MIME processor will handle. + The maximal recursion level that the MIME processor + will handle. strict_8bitmime (no) Enable both strict_7bit_headers and strict_8bit- @@ -258,8 +258,8 @@ CLEANUP(8) CLEANUP(8) strings. mime_nesting_limit (100) - The maximal nesting level of multipart mail that - the MIME processor will handle. + The maximal recursion level that the MIME processor + will handle. queue_file_attribute_count_limit (100) The maximal number of (name=value) attributes that diff --git a/postfix/html/ldap_table.5.html b/postfix/html/ldap_table.5.html index fe7c8b75a..d8d363cac 100644 --- a/postfix/html/ldap_table.5.html +++ b/postfix/html/ldap_table.5.html @@ -138,131 +138,131 @@ LDAP_TABLE(5) LDAP_TABLE(5) %u When the input key is an address of the form user@domain, %u is replaced by the (RFC - 2254) quoted local part of the address. If - no domain is specified, %u is replaced by - the entire search string. + 2254) quoted local part of the address. Oth- + erwise, %u is replaced by the entire search + string. %d When the input key is an address of the form user@domain, %d is replaced by the (RFC 2254) quoted domain part of the address. - When the input key has no domain qualifier, - %d is replaced by the entire search string. + Otherwise, %d 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. result_filter (default: %s) - 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: - %s This is replaced by the value of the result + %s This is replaced by the value of the result attribute. - %u When the result attribute is an address of - the form user@domain, %u is replaced local - part of the address, if the result attribute - is unqualified, %u is replaced by the entire + %u When the result attribute value is an + address of the form user@domain, %u is + replaced by the local part of the address. + Otherwise, %u is replaced by the entire attribute value. - %d When a result attribute is an address of the - form user@domain, %d is replaced by the - domain part of the attribute value. If an - attribute value is unqualified %d is - replaced by the entire attribute value. + %d When a result attribute value is an address + of the form user@domain, %d is replaced by + the domain part of the attribute value. + Otherwise, %d is replaced by the entire + attribute value. For example, using "result_filter = smtp:[%s]" allows one to use a mailHost attribute as the basis - of a transport(5) 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 transport(5) 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 %s specifies that each attribute + The default value %s specifies that each attribute value should be used as is. NOTE: DO NOT put quotes around the result filter! domain (default: no domain list) - 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 local(8) + NOTE: DO NOT define this parameter for local(8) aliases. result_attribute (default: maildrop) - 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 special_result_attribute (No default) 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. scope (default: sub) - The LDAP search scope: sub, base, or one. These + The LDAP search scope: sub, base, or one. These translate into LDAP_SCOPE_SUBTREE, LDAP_SCOPE_BASE, and LDAP_SCOPE_ONELEVEL. bind (default: yes) - 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. bind_dn (default: empty) - 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 bind_pw (default: empty) - 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 ldap:ldapsource syn- + user. When using the obsolete ldap: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) cache_expiry (IGNORED with a warning) cache_size (IGNORED with a warning) - 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. recursion_limit (default: 1000) - 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. expansion_limit (default: 0) - 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. size_limit (default: $expansion_limit) - 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". dereference (default: 0) - 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. chase_referrals (default: 0) - Sets (or clears) LDAP_OPT_REFERRALS (requires LDAP + Sets (or clears) LDAP_OPT_REFERRALS (requires LDAP version 3 support). version (default: 2) Specifies the LDAP protocol version to use. debuglevel (default: 0) - What level to set for debugging in the OpenLDAP + What level to set for debugging in the OpenLDAP libraries. LDAP SSL AND STARTTLS PARAMETERS - 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: start_tls (default: no) 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). - tls_ca_cert_dir (No default; set either this or + tls_ca_cert_dir (No default; set either this or tls_ca_cert_file) 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. - tls_ca_cert_file (No default; set either this or + tls_ca_cert_file (No default; set either this or tls_ca_cert_dir) 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. tls_cert (No default; you must set this) - File containing client's X509 certificate to be + File containing client's X509 certificate to be used by the client in SSL/ TLS connections. tls_key (No default; you must set this) - File containing the private key corresponding to + File containing the private key corresponding to the above tls_cert. tls_require_cert (default: no) 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. tls_random_file (No default) - 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. tls_cipher_suite (No default) Cipher suite to use in SSL/TLS negotiations. EXAMPLE - Here's a basic example for using LDAP to look up local(8) + Here's a basic example for using LDAP to look up local(8) aliases. Assume that in main.cf, you have: alias_maps = hash:/etc/aliases, ldap:/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 RFC822 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 RFC822 addresses to which the message will be delivered. SEE ALSO @@ -443,13 +443,13 @@ LDAP_TABLE(5) LDAP_TABLE(5) LDAP_README, Postfix LDAP client guide LICENSE - The Secure Mailer license must be distributed with this + The Secure Mailer license must be distributed with this software. AUTHOR(S) - 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) diff --git a/postfix/html/pipe.8.html b/postfix/html/pipe.8.html index 6bf9ef687..4b9ca85b6 100644 --- a/postfix/html/pipe.8.html +++ b/postfix/html/pipe.8.html @@ -48,11 +48,18 @@ PIPE(8) PIPE(8) file at the end of a service definition. The syntax is as follows: + directory=pathname (optional, default: $queue_directory) + 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. + eol=string (optional, default: \n) The output record delimiter. Typically one would use either \r\n or \n. The usual C-style backslash escape sequences are recognized: \a \b \f \n \r \t - \v \octal and \\. + \v \ddd (up to three octal digits) and \\. flags=BDFORhqu.> (optional) Optional message processing flags. By default, a @@ -111,10 +118,10 @@ PIPE(8) PIPE(8) most @ character) to lower case. This is recommended for delivery via UUCP. - . Prepend . to lines starting with ".". This + . Prepend "." to lines starting with ".". This is needed by, for example, BSMTP software. - > Prepend > to lines starting with "From ". + > Prepend ">" to lines starting with "From ". This is expected by, for example, UUCP soft- ware. @@ -216,13 +223,6 @@ PIPE(8) PIPE(8) $(name) are also recognized. Specify $$ where a single $ is wanted. - Available in Postfix 2.2 and later: - - directory=pathname (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 con- ventions defined in <sysexits.h>. diff --git a/postfix/html/postconf.5.html b/postfix/html/postconf.5.html index 3edff6d40..c0cde5e61 100644 --- a/postfix/html/postconf.5.html +++ b/postfix/html/postconf.5.html @@ -921,10 +921,10 @@ This feature is available in Postfix 2.0 and later.
    bounce_size_limit (default: 50000)
    -

    -The maximal amount of original message text that is sent in a -non-delivery notification. Specify a byte count. -

    +

    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.

    @@ -1184,8 +1184,8 @@ address patterns that cause the verbose logging level to increase by the amount specified in $debug_peer_level.

    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.

    Pattern matching of domain names is controlled by the parent_domain_matches_subdomains parameter.

    @@ -2725,13 +2725,12 @@ The default time unit is s (seconds).
    lmtp_rset_timeout -(default: 120s)
    +(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 find out if a cached connection is still alive. -

    +

    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.

    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)

    -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.

    @@ -5030,6 +5029,27 @@ The default time unit is s (seconds).

    +
    + +
    session_cache_service +(default: scache)
    + +

    The name of the scache(8) session cache service. This service +maintains a limited pool of cached sessions.

    + + +
    + +
    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.

    + +
    setgid_group @@ -5134,6 +5154,62 @@ The default time unit is s (seconds).

    + + +
    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.

    + +

    Specify a comma or white space separated list of destinations +or pseudo-destinations: +

    + +
      + +
    • 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. + +
    + +

    + + +
    + +
    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. +

    + + +
    + +
    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. +

    + +
    smtp_data_done_timeout @@ -5510,10 +5586,12 @@ The default time unit is s (seconds).
    smtp_rset_timeout -(default: 120s)
    +(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.

    This feature is available in Postfix 2.1 and later.

    diff --git a/postfix/html/scache.8.html b/postfix/html/scache.8.html new file mode 100644 index 000000000..ac46f7ce6 --- /dev/null +++ b/postfix/html/scache.8.html @@ -0,0 +1,156 @@ + + + + Postfix manual - scache(8) +
    +SCACHE(8)                                               SCACHE(8)
    +
    +NAME
    +       scache - Postfix session cache server
    +
    +SYNOPSIS
    +       scache [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  scache  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 scache 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 max_idle time units.
    +
    +       This server implements the following requests:
    +
    +       save_endp ttl endpoint endpoint_properties file_descriptor
    +              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.
    +
    +       find_endp endpoint
    +              Look  up  cached  properties  and  a  cached   file
    +              descriptor for the specified endpoint.
    +
    +       save_dest ttl destination destination_properties endpoint
    +              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.
    +
    +       find_dest destination
    +              Look  up cached destination properties, cached end-
    +              point properties, and a cached file descriptor  for
    +              the specified logical destination.
    +
    +SECURITY
    +       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.
    +
    +DIAGNOSTICS
    +       Problems and transactions are logged to syslogd(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
    +       Changes   to   main.cf  are  picked  up  automatically  as
    +       scache(8) processes run for only a limited amount of time.
    +       Use the command "postfix reload" to speed up a change.
    +
    +       The  text  below  provides  only  a parameter summary. See
    +       postconf(5) for more details including examples.
    +
    +RESOURCE CONTROLS
    +       session_cache_ttl_limit (2s)
    +              The maximal time-to-live  value  that  the  session
    +              cache server allows.
    +
    +MISCELLANEOUS CONTROLS
    +       config_directory (see 'postconf -d' output)
    +              The  default  location  of  the Postfix main.cf and
    +              master.cf configuration files.
    +
    +       daemon_timeout (18000s)
    +              How much time a Postfix daemon process may take  to
    +              handle  a  request  before  it  is  terminated by a
    +              built-in watchdog timer.
    +
    +       ipc_timeout (3600s)
    +              The time limit for sending or receiving information
    +              over an internal communication channel.
    +
    +       max_idle (100s)
    +              The  maximum  amount  of  time that an idle Postfix
    +              daemon process waits for the next  service  request
    +              before exiting.
    +
    +       process_id (read-only)
    +              The  process ID of a Postfix command or daemon pro-
    +              cess.
    +
    +       process_name (read-only)
    +              The process name of a  Postfix  command  or  daemon
    +              process.
    +
    +       syslog_facility (mail)
    +              The syslog facility of Postfix logging.
    +
    +       syslog_name (postfix)
    +              The  mail system name that is prepended to the pro-
    +              cess  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
    +       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
    +
    +                                                        SCACHE(8)
    +
    diff --git a/postfix/html/smtp.8.html b/postfix/html/smtp.8.html index e4657fa49..4264f3b54 100644 --- a/postfix/html/smtp.8.html +++ b/postfix/html/smtp.8.html @@ -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 scache(8) session cache server, so that it + may be used by any SMTP client for a subsequent transac- + tion. Session caching is disabled by default. + SECURITY 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. STANDARDS @@ -52,20 +57,29 @@ SMTP(8) SMTP(8) RFC 2920 (SMTP Pipelining) DIAGNOSTICS - Problems and transactions are logged to syslogd(8). Cor- - rupted message files are marked so that the queue manager + Problems and transactions are logged to syslogd(8). Cor- + rupted message files are marked so that the queue manager can move them to the corrupt queue for further inspection. - Depending on the setting of the notify_classes parameter, - the postmaster is notified of bounces, protocol problems, + Depending on the setting of the notify_classes 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 Changes to main.cf are picked up automatically, as smtp(8) - processes run for only a limited amount of time. Use the + processes run for only a limited amount of time. Use the command "postfix reload" to speed up a change. - The text below provides only a parameter summary. See + The text below provides only a parameter summary. See postconf(5) for more details including examples. COMPATIBILITY CONTROLS @@ -79,7 +93,7 @@ SMTP(8) SMTP(8) Never send EHLO at the start of an SMTP session. smtp_defer_if_no_mx_address_found (no) - Defer mail delivery when no MX record resolves to + Defer mail delivery when no MX record resolves to an IP address. smtp_line_length_limit (990) @@ -87,17 +101,17 @@ SMTP(8) SMTP(8) that Postfix will send via SMTP. smtp_pix_workaround_delay_time (10s) - How long the Postfix SMTP client pauses before + How long the Postfix SMTP client pauses before sending ".<CR><LF>" in order to work around the PIX firewall "<CR><LF>.<CR><LF>" bug. smtp_pix_workaround_threshold_time (500s) - How long a message must be queued before the PIX - firewall "<CR><LF>.<CR><LF>" bug workaround is + How long a message must be queued before the PIX + firewall "<CR><LF>.<CR><LF>" bug workaround is turned on. smtp_quote_rfc821_envelope (yes) - Quote addresses in SMTP MAIL FROM and RCPT TO com- + Quote addresses in SMTP MAIL FROM and RCPT TO com- mands as required by RFC 821. smtp_skip_5xx_greeting (yes) @@ -105,7 +119,7 @@ SMTP(8) SMTP(8) (go away, do not try again later). smtp_skip_quit_response (yes) - 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: disable_mime_output_conversion (no) - Disable the conversion of 8BITMIME format to 7BIT + Disable the conversion of 8BITMIME format to 7BIT format. mime_boundary_length_limit (2048) @@ -126,50 +140,50 @@ SMTP(8) SMTP(8) strings. mime_nesting_limit (100) - 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 Available in Postfix version 2.1 and later: smtp_send_xforward_command (no) - 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. SASL AUTHENTICATION CONTROLS smtp_sasl_auth_enable (no) - Enable SASL authentication in the Postfix SMTP + Enable SASL authentication in the Postfix SMTP client. smtp_sasl_password_maps (empty) - 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. smtp_sasl_security_options (noplaintext, noanonymous) - What authentication mechanisms the Postfix SMTP + What authentication mechanisms the Postfix SMTP client is allowed to use. RESOURCE AND RATE CONTROLS smtp_destination_concurrency_limit ($default_destina- tion_concurrency_limit) - 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. smtp_destination_recipient_limit ($default_destina- tion_recipient_limit) - The maximal number of recipients per delivery via + The maximal number of recipients per delivery via the smtp message delivery transport. smtp_connect_timeout (30s) - 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). smtp_helo_timeout (300s) - 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. smtp_xforward_timeout (300s) @@ -177,30 +191,30 @@ SMTP(8) SMTP(8) command, and for receiving the server response. smtp_mail_timeout (300s) - 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. smtp_rcpt_timeout (300s) - 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. smtp_data_init_timeout (120s) - 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. smtp_data_xfer_timeout (180s) - The SMTP client time limit for sending the SMTP + The SMTP client time limit for sending the SMTP message content. smtp_data_done_timeout (600s) - The SMTP client time limit for sending the SMTP + The SMTP client time limit for sending the SMTP ".", and for receiving the server response. smtp_quit_timeout (300s) - 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). smtp_mx_session_limit (2) - 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). - smtp_rset_timeout (120s) - The SMTP client time limit for sending the RSET + smtp_rset_timeout (20s) + The SMTP client time limit for sending the RSET command, and for receiving the server response. + Available in Postfix version 2.2 and later: + + smtp_connection_cache_domains (empty) + The SMTP destinations for which SMTP connection + caching is enabled. + + smtp_connection_cache_reuse_limit (10) + When SMTP session caching is enabled, the number of + times that an SMTP session is reused before it is + closed. + + smtp_connection_cache_time_limit (2s) + 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 debug_peer_level (2) The increment in verbose logging level when a @@ -320,6 +350,7 @@ SMTP(8) SMTP(8) 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 @@ -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) diff --git a/postfix/man/Makefile.in b/postfix/man/Makefile.in index 5010210c8..9f3a3f683 100644 --- a/postfix/man/Makefile.in +++ b/postfix/man/Makefile.in @@ -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 $?) diff --git a/postfix/man/man5/ldap_table.5 b/postfix/man/man5/ldap_table.5 index bc4824eb2..bb7e97f3f 100644 --- a/postfix/man/man5/ldap_table.5 +++ b/postfix/man/man5/ldap_table.5 @@ -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 diff --git a/postfix/man/man5/postconf.5 b/postfix/man/man5/postconf.5 index c473165cd..cac7ae072 100644 --- a/postfix/man/man5/postconf.5 +++ b/postfix/man/man5/postconf.5 @@ -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) diff --git a/postfix/man/man8/cleanup.8 b/postfix/man/man8/cleanup.8 index 22c64cf30..0e91dcb67 100644 --- a/postfix/man/man8/cleanup.8 +++ b/postfix/man/man8/cleanup.8 @@ -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. diff --git a/postfix/man/man8/pipe.8 b/postfix/man/man8/pipe.8 index 8dc1369df..d450e3774 100644 --- a/postfix/man/man8/pipe.8 +++ b/postfix/man/man8/pipe.8 @@ -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 index 000000000..283283c4c --- /dev/null +++ b/postfix/man/man8/scache.8 @@ -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 diff --git a/postfix/man/man8/smtp.8 b/postfix/man/man8/smtp.8 index be03f343d..b7feea688 100644 --- a/postfix/man/man8/smtp.8 +++ b/postfix/man/man8/smtp.8 @@ -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 diff --git a/postfix/mantools/make-relnotes b/postfix/mantools/make-relnotes index 79d5730ad..7e1ade714 100644 --- a/postfix/mantools/make-relnotes +++ b/postfix/mantools/make-relnotes @@ -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 diff --git a/postfix/mantools/postlink b/postfix/mantools/postlink index bcbb1f05b..8b854afa6 100755 --- a/postfix/mantools/postlink +++ b/postfix/mantools/postlink @@ -293,11 +293,20 @@ while (<>) { s;\bsendmail_path\b;$&;g; s;\bservice_throttle_time\b;$&;g; s;\bsetgid_group\b;$&;g; + + s;\bsession_cache_service\b;$&;g; + s;\bsession_cache_ttl_limit\b;$&;g; + s;\bshow_user_unknown_table_name\b;$&;g; s;\bshowq_service_name\b;$&;g; s;\bsmtp_always_send_ehlo\b;$&;g; s;\bsmtp_bind_address\b;$&;g; s;\bsmtp_connect_timeout\b;$&;g; + + s;\bsmtp_connection_cache_reuse_limit\b;$&;g; + s;\bsmtp_connection_cache_time_limit\b;$&;g; + s;\bsmtp_connection_cache_domains\b;$&;g; + s;\bsmtp_data_done_timeout\b;$&;g; s;\bsmtp_data_init_timeout\b;$&;g; s;\bsmtp_data_xfer_timeout\b;$&;g; @@ -478,6 +487,7 @@ while (<>) { s/[]*proxymap[<\/bB>]*\(8\)/$&<\/a>/g; s/[]*reg[-<\/bB>]*\n*[ ]*exp[<\/bBiI>]*_[<\/iIbB>]*table[<\/bB>]*\(5\)/$&<\/a>/g; s/[]*relocated[<\/bB>]*\(5\)/$&<\/a>/g; + s/[]*scache[<\/bB>]*\(8\)/$&<\/a>/g; s/[]*trans[-<\/bB>]*\n*[ ]*port[<\/bB>]*\(5\)/$&<\/a>/g; s/[]*verify[<\/bB>]*\(8\)/$&<\/a>/g; s/[]*virtual[<\/bB>]*\(5\)/$&<\/a>/g; diff --git a/postfix/proto/OVERVIEW.html b/postfix/proto/OVERVIEW.html index 5401aed86..08f8314ea 100644 --- a/postfix/proto/OVERVIEW.html +++ b/postfix/proto/OVERVIEW.html @@ -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.

    +
  • 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.

    diff --git a/postfix/proto/ldap_table b/postfix/proto/ldap_table index 49df5bf17..1aa612797 100644 --- a/postfix/proto/ldap_table +++ b/postfix/proto/ldap_table @@ -138,13 +138,13 @@ # .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 @@ -163,15 +163,15 @@ # .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 diff --git a/postfix/proto/postconf.proto b/postfix/proto/postconf.proto index a71bccf55..9ee27cacc 100644 --- a/postfix/proto/postconf.proto +++ b/postfix/proto/postconf.proto @@ -645,10 +645,10 @@ This feature is available in Postfix 2.1 and later. %PARAM bounce_size_limit 50000 -

    -The maximal amount of original message text that is sent in a -non-delivery notification. Specify a byte count. -

    +

    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.

    %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.

    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.

    Pattern matching of domain names is controlled by the parent_domain_matches_subdomains parameter.

    @@ -1698,13 +1698,12 @@ Time units: s (seconds), m (minutes), h (hours), d (days), w (weeks). The default time unit is s (seconds).

    -%PARAM lmtp_rset_timeout 120s +%PARAM lmtp_rset_timeout 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 find out if a cached connection is still alive. -

    +

    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.

    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 +%PARAM smtp_connection_cache_time_limit 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. +

    + +%PARAM smtp_connection_cache_reuse_limit 10 + +

    When SMTP session caching is enabled, the number of times that +an SMTP session is reused before it is closed. +

    + +%PARAM smtp_connection_cache_domains + +

    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.

    + +

    Specify a comma or white space separated list of destinations +or pseudo-destinations: +

    + +
      + +
    • 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. + +
    + +

    + %PARAM smtp_connect_timeout 30s

    @@ -5953,8 +5996,8 @@ This feature is available in Postfix 2.0 and later. %PARAM mime_nesting_limit 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.

    @@ -6290,10 +6333,12 @@ Randomize the order of equal-preference MX host addresses. This is a performance feature of the Postfix SMTP client.

    -%PARAM smtp_rset_timeout 120s +%PARAM smtp_rset_timeout 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.

    This feature is available in Postfix 2.1 and later.

    @@ -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.

    + +%PARAM session_cache_service scache + +

    The name of the scache(8) session cache service. This service +maintains a limited pool of cached sessions.

    + +%PARAM session_cache_ttl_limit 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.

    diff --git a/postfix/src/anvil/Makefile.in b/postfix/src/anvil/Makefile.in index b88d2b255..23d6d3062 100644 --- a/postfix/src/anvil/Makefile.in +++ b/postfix/src/anvil/Makefile.in @@ -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 diff --git a/postfix/src/bounce/Makefile.in b/postfix/src/bounce/Makefile.in index 6a3f352b3..86030bf3d 100644 --- a/postfix/src/bounce/Makefile.in +++ b/postfix/src/bounce/Makefile.in @@ -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 diff --git a/postfix/src/cleanup/Makefile.in b/postfix/src/cleanup/Makefile.in index 4d4e632e0..b1a884c58 100644 --- a/postfix/src/cleanup/Makefile.in +++ b/postfix/src/cleanup/Makefile.in @@ -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 diff --git a/postfix/src/cleanup/cleanup.c b/postfix/src/cleanup/cleanup.c index 97de4cf66..541418714 100644 --- a/postfix/src/cleanup/cleanup.c +++ b/postfix/src/cleanup/cleanup.c @@ -104,8 +104,7 @@ /* .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" @@ -196,8 +195,7 @@ /* .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. diff --git a/postfix/src/dns/Makefile.in b/postfix/src/dns/Makefile.in index c96f67256..bdbad7385 100644 --- a/postfix/src/dns/Makefile.in +++ b/postfix/src/dns/Makefile.in @@ -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 diff --git a/postfix/src/dns/dns.h b/postfix/src/dns/dns.h index e08583d6e..014536b6e 100644 --- a/postfix/src/dns/dns.h +++ b/postfix/src/dns/dns.h @@ -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 diff --git a/postfix/src/dns/dns_rr.c b/postfix/src/dns/dns_rr.c index 2dc8edd1b..a00a738d0 100644 --- a/postfix/src/dns/dns_rr.c +++ b/postfix/src/dns/dns_rr.c @@ -29,6 +29,10 @@ /* /* 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); +} diff --git a/postfix/src/error/Makefile.in b/postfix/src/error/Makefile.in index 82311bcf0..bbeb61947 100644 --- a/postfix/src/error/Makefile.in +++ b/postfix/src/error/Makefile.in @@ -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 diff --git a/postfix/src/flush/Makefile.in b/postfix/src/flush/Makefile.in index 4708bb12c..194a56738 100644 --- a/postfix/src/flush/Makefile.in +++ b/postfix/src/flush/Makefile.in @@ -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 diff --git a/postfix/src/fsstone/Makefile.in b/postfix/src/fsstone/Makefile.in index d22b4bc55..995637800 100644 --- a/postfix/src/fsstone/Makefile.in +++ b/postfix/src/fsstone/Makefile.in @@ -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 diff --git a/postfix/src/global/Makefile.in b/postfix/src/global/Makefile.in index 8316f6039..240749c1f 100644 --- a/postfix/src/global/Makefile.in +++ b/postfix/src/global/Makefile.in @@ -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.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.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 diff --git a/postfix/src/global/mail_params.h b/postfix/src/global/mail_params.h index b3f91f51d..db99a7c6d 100644 --- a/postfix/src/global/mail_params.h +++ b/postfix/src/global/mail_params.h @@ -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. */ diff --git a/postfix/src/global/mail_proto.h b/postfix/src/global/mail_proto.h index 1fd24d1f8..4d9082a7b 100644 --- a/postfix/src/global/mail_proto.h +++ b/postfix/src/global/mail_proto.h @@ -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. */ diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h index 0ab9ea1e9..13f3b1872 100644 --- a/postfix/src/global/mail_version.h +++ b/postfix/src/global/mail_version.h @@ -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" diff --git a/postfix/src/global/mime_state.c b/postfix/src/global/mime_state.c index e40081245..7beba3aab 100644 --- a/postfix/src/global/mime_state.c +++ b/postfix/src/global/mime_state.c @@ -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 index 000000000..bc30b172d --- /dev/null +++ b/postfix/src/global/scache.c @@ -0,0 +1,391 @@ +/*++ +/* NAME +/* scache 3 +/* SUMMARY +/* generic session cache API +/* SYNOPSIS +/* #include +/* 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 +#include +#include + +/* Utility library. */ + +#include +#include +#include +#include +#include +#include + +/* Global library. */ + +#include + +#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 index 000000000..c00dd6d6b --- /dev/null +++ b/postfix/src/global/scache.h @@ -0,0 +1,147 @@ +#ifndef _SCACHE_H_INCLUDED_ +#define _SCACHE_H_INCLUDED_ + +/*++ +/* NAME +/* scache 3h +/* SUMMARY +/* generic session cache API +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * Utility library. + */ +#include + +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 index 000000000..ff2f62cdd --- /dev/null +++ b/postfix/src/global/scache_clnt.c @@ -0,0 +1,312 @@ +/*++ +/* NAME +/* scache_clnt 3 +/* SUMMARY +/* session cache manager client +/* SYNOPSIS +/* #include +/* 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 +#include + +/* Utility library. */ + +#include +#include + +/*#define msg_verbose 1*/ + +/* Global library. */ + +#include +#include +#include +#include + +/* 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 index 000000000..2e7ca0152 --- /dev/null +++ b/postfix/src/global/scache_multi.c @@ -0,0 +1,477 @@ +/*++ +/* NAME +/* scache_multi 3 +/* SUMMARY +/* multi-session cache +/* SYNOPSIS +/* #include +/* 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 +#include +#include /* offsetof() */ + +/* Utility library. */ + +#include +#include +#include +#include +#include +#include + +/*#define msg_verbose 1*/ + +/* Global library. */ + +#include + +/* 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 index 000000000..1a65650a6 --- /dev/null +++ b/postfix/src/global/scache_multi.in @@ -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 index 000000000..8bf63cb54 --- /dev/null +++ b/postfix/src/global/scache_multi.ref @@ -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 index 000000000..c6ad17e4a --- /dev/null +++ b/postfix/src/global/scache_single.c @@ -0,0 +1,299 @@ +/*++ +/* NAME +/* scache_single 3 +/* SUMMARY +/* single-item session cache +/* SYNOPSIS +/* #include +/* 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 +#include + +/* Utility library. */ + +#include +#include +#include +#include + +/*#define msg_verbose 1*/ + +/* Global library. */ + +#include + +/* 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); +} diff --git a/postfix/src/lmtp/Makefile.in b/postfix/src/lmtp/Makefile.in index 9b3ca14d8..84c822c45 100644 --- a/postfix/src/lmtp/Makefile.in +++ b/postfix/src/lmtp/Makefile.in @@ -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 diff --git a/postfix/src/local/Makefile.in b/postfix/src/local/Makefile.in index 732b788d9..60b4c78dc 100644 --- a/postfix/src/local/Makefile.in +++ b/postfix/src/local/Makefile.in @@ -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 diff --git a/postfix/src/master/Makefile.in b/postfix/src/master/Makefile.in index 7a5a4bab4..c444c9919 100644 --- a/postfix/src/master/Makefile.in +++ b/postfix/src/master/Makefile.in @@ -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 diff --git a/postfix/src/oqmgr/Makefile.in b/postfix/src/oqmgr/Makefile.in index 714a9bc17..63b9590c4 100644 --- a/postfix/src/oqmgr/Makefile.in +++ b/postfix/src/oqmgr/Makefile.in @@ -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 diff --git a/postfix/src/pickup/Makefile.in b/postfix/src/pickup/Makefile.in index 405f1a301..8d832721e 100644 --- a/postfix/src/pickup/Makefile.in +++ b/postfix/src/pickup/Makefile.in @@ -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 diff --git a/postfix/src/pipe/Makefile.in b/postfix/src/pipe/Makefile.in index 0add8c3d0..dd7066f10 100644 --- a/postfix/src/pipe/Makefile.in +++ b/postfix/src/pipe/Makefile.in @@ -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 diff --git a/postfix/src/pipe/pipe.c b/postfix/src/pipe/pipe.c index dcb52da65..1b7217726 100644 --- a/postfix/src/pipe/pipe.c +++ b/postfix/src/pipe/pipe.c @@ -41,11 +41,16 @@ /* .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. @@ -95,10 +100,10 @@ /* 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)" @@ -173,11 +178,6 @@ /* 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>. diff --git a/postfix/src/postalias/Makefile.in b/postfix/src/postalias/Makefile.in index a7b236e91..5049eb1b4 100644 --- a/postfix/src/postalias/Makefile.in +++ b/postfix/src/postalias/Makefile.in @@ -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 diff --git a/postfix/src/postcat/Makefile.in b/postfix/src/postcat/Makefile.in index e1ba3bdc9..bacd2dd16 100644 --- a/postfix/src/postcat/Makefile.in +++ b/postfix/src/postcat/Makefile.in @@ -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 diff --git a/postfix/src/postconf/Makefile.in b/postfix/src/postconf/Makefile.in index 56ec89628..2910a674c 100644 --- a/postfix/src/postconf/Makefile.in +++ b/postfix/src/postconf/Makefile.in @@ -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 diff --git a/postfix/src/postdrop/Makefile.in b/postfix/src/postdrop/Makefile.in index cb914ad96..e6946e11b 100644 --- a/postfix/src/postdrop/Makefile.in +++ b/postfix/src/postdrop/Makefile.in @@ -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 diff --git a/postfix/src/postfix/Makefile.in b/postfix/src/postfix/Makefile.in index ac53549d3..4249375d6 100644 --- a/postfix/src/postfix/Makefile.in +++ b/postfix/src/postfix/Makefile.in @@ -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 diff --git a/postfix/src/postkick/Makefile.in b/postfix/src/postkick/Makefile.in index 38c0fba9a..3e63ead93 100644 --- a/postfix/src/postkick/Makefile.in +++ b/postfix/src/postkick/Makefile.in @@ -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 diff --git a/postfix/src/postlock/Makefile.in b/postfix/src/postlock/Makefile.in index 4e4ea3ad0..04c283f6c 100644 --- a/postfix/src/postlock/Makefile.in +++ b/postfix/src/postlock/Makefile.in @@ -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 diff --git a/postfix/src/postlog/Makefile.in b/postfix/src/postlog/Makefile.in index 0bae7d356..e914094f1 100644 --- a/postfix/src/postlog/Makefile.in +++ b/postfix/src/postlog/Makefile.in @@ -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 diff --git a/postfix/src/postmap/Makefile.in b/postfix/src/postmap/Makefile.in index 61d03f976..00e1c94c7 100644 --- a/postfix/src/postmap/Makefile.in +++ b/postfix/src/postmap/Makefile.in @@ -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 diff --git a/postfix/src/postqueue/Makefile.in b/postfix/src/postqueue/Makefile.in index ba9e66f16..a90fe8d1d 100644 --- a/postfix/src/postqueue/Makefile.in +++ b/postfix/src/postqueue/Makefile.in @@ -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 diff --git a/postfix/src/postsuper/Makefile.in b/postfix/src/postsuper/Makefile.in index 1b9d8079d..98eb5f61a 100644 --- a/postfix/src/postsuper/Makefile.in +++ b/postfix/src/postsuper/Makefile.in @@ -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 diff --git a/postfix/src/proxymap/Makefile.in b/postfix/src/proxymap/Makefile.in index e42f4389c..42c9148fd 100644 --- a/postfix/src/proxymap/Makefile.in +++ b/postfix/src/proxymap/Makefile.in @@ -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 diff --git a/postfix/src/qmgr/Makefile.in b/postfix/src/qmgr/Makefile.in index 9fd75b80f..3239af285 100644 --- a/postfix/src/qmgr/Makefile.in +++ b/postfix/src/qmgr/Makefile.in @@ -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 diff --git a/postfix/src/qmqpd/Makefile.in b/postfix/src/qmqpd/Makefile.in index e3fdf333c..3f24a9351 100644 --- a/postfix/src/qmqpd/Makefile.in +++ b/postfix/src/qmqpd/Makefile.in @@ -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 index 000000000..5c837eca6 --- /dev/null +++ b/postfix/src/scache/.indent.pro @@ -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 index 000000000..092a93b89 --- /dev/null +++ b/postfix/src/scache/Makefile.in @@ -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 index 000000000..70d271da4 --- /dev/null +++ b/postfix/src/scache/scache.c @@ -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 + +/* Utility library. */ + +#include +#include +#include +#include + +/* Global library. */ + +#include +#include +#include + +/* Single server skeleton. */ + +#include +#include + +/* 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); +} diff --git a/postfix/src/sendmail/Makefile.in b/postfix/src/sendmail/Makefile.in index fbbb60600..0da0de758 100644 --- a/postfix/src/sendmail/Makefile.in +++ b/postfix/src/sendmail/Makefile.in @@ -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 diff --git a/postfix/src/showq/Makefile.in b/postfix/src/showq/Makefile.in index a8cba0c1f..ae0e476cf 100644 --- a/postfix/src/showq/Makefile.in +++ b/postfix/src/showq/Makefile.in @@ -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 diff --git a/postfix/src/smtp/Makefile.in b/postfix/src/smtp/Makefile.in index 609123a10..8fa75f53d 100644 --- a/postfix/src/smtp/Makefile.in +++ b/postfix/src/smtp/Makefile.in @@ -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 diff --git a/postfix/src/smtp/smtp.c b/postfix/src/smtp/smtp.c index dbcb0c1d2..d51f011f3 100644 --- a/postfix/src/smtp/smtp.c +++ b/postfix/src/smtp/smtp.c @@ -25,6 +25,11 @@ /* 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 @@ -50,6 +55,13 @@ /* 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 @@ -103,8 +115,7 @@ /* .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 @@ -167,9 +178,20 @@ /* 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 @@ -240,6 +262,7 @@ /* 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 @@ -260,6 +283,18 @@ /* 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. */ @@ -284,6 +319,8 @@ #include #include #include +#include +#include /* 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); diff --git a/postfix/src/smtp/smtp.h b/postfix/src/smtp/smtp.h index c965c25e7..bb061a966 100644 --- a/postfix/src/smtp/smtp.h +++ b/postfix/src/smtp/smtp.h @@ -22,11 +22,14 @@ #include #include #include +#include /* * Global library. */ #include +#include +#include /* * State information associated with each SMTP delivery. We're bundling the @@ -34,11 +37,31 @@ */ 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 diff --git a/postfix/src/smtp/smtp_addr.c b/postfix/src/smtp/smtp_addr.c index bd295aacf..b7cf54ecf 100644 --- a/postfix/src/smtp/smtp_addr.c +++ b/postfix/src/smtp/smtp_addr.c @@ -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); diff --git a/postfix/src/smtp/smtp_connect.c b/postfix/src/smtp/smtp_connect.c index c81120adf..bbc0ccacb 100644 --- a/postfix/src/smtp/smtp_connect.c +++ b/postfix/src/smtp/smtp_connect.c @@ -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 (":"). @@ -41,6 +44,10 @@ /* 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. */ @@ -65,6 +72,10 @@ #define INADDR_NONE 0xffffffff #endif +#ifndef IPPORT_SMTP +#define IPPORT_SMTP 25 +#endif + /* Utility library. */ #include @@ -83,7 +94,6 @@ #include #include -#include #include #include @@ -93,13 +103,17 @@ /* Application-specific. */ -#include "smtp.h" -#include "smtp_addr.h" +#include +#include +#include + +#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); diff --git a/postfix/src/smtp/smtp_proto.c b/postfix/src/smtp/smtp_proto.c index 18fcb3ea1..82b7debe3 100644 --- a/postfix/src/smtp/smtp_proto.c +++ b/postfix/src/smtp/smtp_proto.c @@ -11,6 +11,12 @@ /* /* 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. /* @@ -22,10 +28,20 @@ /* 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. */ @@ -114,6 +133,23 @@ * 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. @@ -129,8 +165,9 @@ #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 index 000000000..705e782ee --- /dev/null +++ b/postfix/src/smtp/smtp_reuse.c @@ -0,0 +1,266 @@ +/*++ +/* NAME +/* smtp_reuse 3 +/* SUMMARY +/* SMTP session cache glue +/* SYNOPSIS +/* #include +/* #include +/* +/* 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 +#include +#include +#include +#include +#include + +/* Utility library. */ + +#include +#include +#include +#include +#include +#include + +/* Global library. */ + +#include +#include + +/* Application-specific. */ + +#include +#include + + /* + * 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 index 000000000..9018c9fc7 --- /dev/null +++ b/postfix/src/smtp/smtp_reuse.h @@ -0,0 +1,32 @@ +/*++ +/* NAME +/* smtp_reuse 3h +/* SUMMARY +/* SMTP session cache glue +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * DNS library. + */ +#include + + /* + * 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 +/*--*/ diff --git a/postfix/src/smtp/smtp_sasl.h b/postfix/src/smtp/smtp_sasl.h index c7550aae2..cf8c6d59c 100644 --- a/postfix/src/smtp/smtp_sasl.h +++ b/postfix/src/smtp/smtp_sasl.h @@ -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 diff --git a/postfix/src/smtp/smtp_sasl_glue.c b/postfix/src/smtp/smtp_sasl_glue.c index 30251c8d3..2c0d88a36 100644 --- a/postfix/src/smtp/smtp_sasl_glue.c +++ b/postfix/src/smtp/smtp_sasl_glue.c @@ -23,6 +23,14 @@ /* /* 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 @@ -52,6 +60,13 @@ /* 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 diff --git a/postfix/src/smtp/smtp_session.c b/postfix/src/smtp/smtp_session.c index 8ac687a2d..9eab20e54 100644 --- a/postfix/src/smtp/smtp_session.c +++ b/postfix/src/smtp/smtp_session.c @@ -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 @@ -37,9 +83,11 @@ /* System library. */ #include +#include /* Utility library. */ +#include #include #include #include @@ -47,15 +95,22 @@ /* Global library. */ #include +#include +#include /* 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); +} diff --git a/postfix/src/smtp/smtp_state.c b/postfix/src/smtp/smtp_state.c index f871a391a..404279cd9 100644 --- a/postfix/src/smtp/smtp_state.c +++ b/postfix/src/smtp/smtp_state.c @@ -38,12 +38,10 @@ #include #include -#include /* Global library. */ -#include -#include +#include /* 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); } diff --git a/postfix/src/smtp/smtp_trouble.c b/postfix/src/smtp/smtp_trouble.c index f323dfb92..435a1ae35 100644 --- a/postfix/src/smtp/smtp_trouble.c +++ b/postfix/src/smtp/smtp_trouble.c @@ -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. diff --git a/postfix/src/smtpd/Makefile.in b/postfix/src/smtpd/Makefile.in index 292ac1fca..254f39e21 100644 --- a/postfix/src/smtpd/Makefile.in +++ b/postfix/src/smtpd/Makefile.in @@ -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 diff --git a/postfix/src/smtpstone/Makefile.in b/postfix/src/smtpstone/Makefile.in index 189517006..f3738bcc6 100644 --- a/postfix/src/smtpstone/Makefile.in +++ b/postfix/src/smtpstone/Makefile.in @@ -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 diff --git a/postfix/src/spawn/Makefile.in b/postfix/src/spawn/Makefile.in index 3a1cdb12e..be7554fa8 100644 --- a/postfix/src/spawn/Makefile.in +++ b/postfix/src/spawn/Makefile.in @@ -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 diff --git a/postfix/src/trivial-rewrite/Makefile.in b/postfix/src/trivial-rewrite/Makefile.in index 94f6d89aa..0b734b348 100644 --- a/postfix/src/trivial-rewrite/Makefile.in +++ b/postfix/src/trivial-rewrite/Makefile.in @@ -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 diff --git a/postfix/src/util/Makefile.in b/postfix/src/util/Makefile.in index 422390ca3..91ed1ba72 100644 --- a/postfix/src/util/Makefile.in +++ b/postfix/src/util/Makefile.in @@ -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 diff --git a/postfix/src/util/events.c b/postfix/src/util/events.c index 25af73f47..436693075 100644 --- a/postfix/src/util/events.c +++ b/postfix/src/util/events.c @@ -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 #include -#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); diff --git a/postfix/src/util/iostuff.h b/postfix/src/util/iostuff.h index 7da0f297d..05c57d1cb 100644 --- a/postfix/src/util/iostuff.h +++ b/postfix/src/util/iostuff.h @@ -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 diff --git a/postfix/src/util/ring.h b/postfix/src/util/ring.h index d0219cf80..47749e276 100644 --- a/postfix/src/util/ring.h +++ b/postfix/src/util/ring.h @@ -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 index 000000000..9698d7a26 --- /dev/null +++ b/postfix/src/util/stream_recv_fd.c @@ -0,0 +1,118 @@ +/*++ +/* NAME +/* stream_recv_fd 3 +/* SUMMARY +/* receive file descriptor +/* SYNOPSIS +/* #include +/* +/* 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 /* includes */ + +#ifdef STREAM_CONNECTIONS + +#include +#include +#include +#include +#include + +#endif + +/* Utility library. */ + +#include +#include + +/* 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 +#include +#include + +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 index 000000000..410783761 --- /dev/null +++ b/postfix/src/util/stream_send_fd.c @@ -0,0 +1,112 @@ +/*++ +/* NAME +/* stream_send_fd 3 +/* SUMMARY +/* send file descriptor +/* SYNOPSIS +/* #include +/* +/* 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 /* includes */ + +#ifdef STREAM_CONNECTIONS + +#include +#include +#include +#include +#include + +#endif + +/* Utility library. */ + +#include +#include + +/* 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 +#include +#include +#include + +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 diff --git a/postfix/src/util/sys_defs.h b/postfix/src/util/sys_defs.h index 264467cfb..8841e2202 100644 --- a/postfix/src/util/sys_defs.h +++ b/postfix/src/util/sys_defs.h @@ -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 index 000000000..43ddfe608 --- /dev/null +++ b/postfix/src/util/unix_recv_fd.c @@ -0,0 +1,156 @@ +/*++ +/* NAME +/* unix_recv_fd 3 +/* SUMMARY +/* receive file descriptor +/* SYNOPSIS +/* #include +/* +/* 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 /* includes */ +#include +#include + +/* Utility library. */ + +#include +#include + +/* 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 +#include +#include + +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 index 000000000..6fcc51143 --- /dev/null +++ b/postfix/src/util/unix_send_fd.c @@ -0,0 +1,137 @@ +/*++ +/* NAME +/* unix_send_fd 3 +/* SUMMARY +/* send file descriptor +/* SYNOPSIS +/* #include +/* +/* 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 /* includes */ +#include +#include + +/* Utility library. */ + +#include +#include + +/* 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 +#include +#include +#include + +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 diff --git a/postfix/src/util/vstream.c b/postfix/src/util/vstream.c index fc502d18c..b67553bf6 100644 --- a/postfix/src/util/vstream.c +++ b/postfix/src/util/vstream.c @@ -18,6 +18,9 @@ /* int vstream_fclose(stream) /* VSTREAM *stream; /* +/* int vstream_fdclose(stream) +/* VSTREAM *stream; +/* /* VSTREAM *vstream_printf(format, ...) /* char *format; /* @@ -152,6 +155,9 @@ /* 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,...) diff --git a/postfix/src/util/vstream.h b/postfix/src/util/vstream.h index 13f66e836..2e3824a2a 100644 --- a/postfix/src/util/vstream.h +++ b/postfix/src/util/vstream.h @@ -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)) diff --git a/postfix/src/verify/Makefile.in b/postfix/src/verify/Makefile.in index 2daf6659e..1748ec708 100644 --- a/postfix/src/verify/Makefile.in +++ b/postfix/src/verify/Makefile.in @@ -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 diff --git a/postfix/src/virtual/Makefile.in b/postfix/src/virtual/Makefile.in index fda53bd6d..3389ce566 100644 --- a/postfix/src/virtual/Makefile.in +++ b/postfix/src/virtual/Makefile.in @@ -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