+-TSCACHE_HEAD_NODE
-TABOUNCE
-TALIAS_TOKEN
-TANVIL_LOCAL
-TRESPONSE
-TREST_TABLE
-TRES_CONTEXT
+-TSCACHE
+-TSCACHE_CLNT
+-TSCACHE_MULTI
+-TSCACHE_MULTI_DEST
+-TSCACHE_MULTI_ENDP
+-TSCACHE_MULTI_HEAD
+-TSCACHE_SINGLE
+-TSCACHE_SINGLE_DEST
+-TSCACHE_SINGLE_ENDP
-TSCAN_DIR
-TSCAN_INFO
-TSCAN_OBJ
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
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.
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
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
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
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
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
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
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.
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
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.
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
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.
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
================================================
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.
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.
EOF
}
+ # Add missing scache service to master.cf.
+
+ grep '^scache.*scache' $config_directory/master.cf >/dev/null || {
+ echo Editing $config_directory/master.cf, adding missing entry for scache service
+ cat >>$config_directory/master.cf <<EOF || exit 1
+scache unix - - n - 1 scache
+EOF
+ }
+
# Report (but do not remove) obsolete files.
test -n "$obsolete" && {
$daemon_directory/proxymap:f:root:-:755
$daemon_directory/qmgr:f:root:-:755
$daemon_directory/qmqpd:f:root:-:755
+$daemon_directory/scache:f:root:-:755
$daemon_directory/showq:f:root:-:755
$daemon_directory/smtp:f:root:-:755
$daemon_directory/smtpd:f:root:-:755
$manpage_directory/man8/proxymap.8:f:root:-:644
$manpage_directory/man8/qmgr.8:f:root:-:644
$manpage_directory/man8/qmqpd.8:f:root:-:644
+$manpage_directory/man8/scache.8:f:root:-:644
$manpage_directory/man8/showq.8:f:root:-:644
$manpage_directory/man8/smtp.8:f:root:-:644
$manpage_directory/man8/smtpd.8:f:root:-:644
if ($result eq "pass") { return "DUNNO"; }
elsif ($result eq "fail") { return "REJECT " . ($smtp_comment || $header_comment); }
- elsif ($result eq "error") { return "450 temporary failure: $smtp_comemnt"; }
+ elsif ($result eq "error") { return "450 temporary failure: $smtp_comment"; }
else { return "DUNNO"; }
# unknown, softfail, and none all return DUNNO
lmtp.8.html master.8.html pickup.8.html pipe.8.html qmgr.8.html \
showq.8.html smtp.8.html smtpd.8.html trivial-rewrite.8.html \
oqmgr.8.html spawn.8.html flush.8.html virtual.8.html qmqpd.8.html \
- trace.8.html verify.8.html proxymap.8.html anvil.8.html
+ trace.8.html verify.8.html proxymap.8.html anvil.8.html \
+ scache.8.html
COMMANDS= mailq.1.html newaliases.1.html postalias.1.html postcat.1.html \
postconf.1.html postfix.1.html postkick.1.html postlock.1.html \
postlog.1.html postdrop.1.html postmap.1.html sendmail.1.html \
PATH=../mantools:$$PATH; \
srctoman $? | $(AWK) | nroff -man | uniq | $(MAN2HTML) | postlink >$@
+scache.8.html: ../src/scache/scache.c
+ PATH=../mantools:$$PATH; \
+ srctoman $? | $(AWK) | nroff -man | uniq | $(MAN2HTML) | postlink >$@
+
lmtp.8.html: ../src/lmtp/lmtp.c
PATH=../mantools:$$PATH; \
srctoman $? | $(AWK) | nroff -man | uniq | $(MAN2HTML) | postlink >$@
and reduces the number of open lookup tables by sharing one open
table among multiple processes. </p>
+<li> <p> The <a href="scache.8.html">scache(8)</a> server maintains the session cache for the
+Postfix <a href="smtp.8.html">smtp(8)</a> client. When session caching is enabled for selected
+destinations, the <a href="smtp.8.html">smtp(8)</a> client does not disconnect immediately
+after a mail transaction, but gives the connection to the session
+cache server. The <a href="smtp.8.html">smtp(8)</a> client continues with some other mail
+delivery request. Meanwhile, the session cache server keeps the
+connection open for a limited amount of time. During that time,
+any <a href="smtp.8.html">smtp(8)</a> process can ask the <a href="scache.8.html">scache(8)</a> server for that cached
+session and use it for mail delivery. </p>
+
+<table>
+
+<tr> <td> <td align="center" bgcolor="#f0f0ff"> <br> <a href="smtp.8.html">smtp(8)</a> <br>
+ </td> <td> <tt> -> </tt> </td> <td> <td align="center"
+bgcolor="#f0f0ff"> <br> <a href="scache.8.html">scache(8)</a> <br> </td> <td> <tt> ->
+</tt> </td> <td> <td align="center" bgcolor="#f0f0ff"> <br> <a href="smtp.8.html">smtp(8)</a>
+<br> </td>
+
+</table>
+
<li> <p> The <a href="showq.8.html">showq(8)</a> servers list the Postfix queue status. This
is the queue listing service that does the work for the <a href="mailq.1.html">mailq(1)</a>
and <a href="postqueue.1.html">postqueue(1)</a> commands. </p>
strings.
<b><a href="postconf.5.html#mime_nesting_limit">mime_nesting_limit</a> (100)</b>
- The maximal nesting level of multipart mail that
- the MIME processor will handle.
+ The maximal recursion level that the MIME processor
+ will handle.
<b><a href="postconf.5.html#strict_8bitmime">strict_8bitmime</a> (no)</b>
Enable both <a href="postconf.5.html#strict_7bit_headers">strict_7bit_headers</a> and strict_8bit-
strings.
<b><a href="postconf.5.html#mime_nesting_limit">mime_nesting_limit</a> (100)</b>
- The maximal nesting level of multipart mail that
- the MIME processor will handle.
+ The maximal recursion level that the MIME processor
+ will handle.
<b><a href="postconf.5.html#queue_file_attribute_count_limit">queue_file_attribute_count_limit</a> (100)</b>
The maximal number of (name=value) attributes that
<b>%u</b> When the input key is an address of the form
user@domain, <b>%u</b> is replaced by the (RFC
- 2254) quoted local part of the address. If
- no domain is specified, <b>%u</b> is replaced by
- the entire search string.
+ 2254) quoted local part of the address. Oth-
+ erwise, <b>%u</b> is replaced by the entire search
+ string.
<b>%d</b> When the input key is an address of the form
user@domain, <b>%d</b> is replaced by the (RFC
2254) quoted domain part of the address.
- When the input key has no domain qualifier,
- <b>%d</b> is replaced by the entire search string.
+ Otherwise, <b>%d</b> is replaced by the entire
+ search string.
- The "domain" parameter described below limits the
- input keys to addresses in matching domains. When
- the "domain" parameter is non-empty, LDAP queries
- for unqualified addresses or addresses in non-
+ The "domain" parameter described below limits the
+ input keys to addresses in matching domains. When
+ the "domain" parameter is non-empty, LDAP queries
+ for unqualified addresses or addresses in non-
matching domains are suppressed and return no
results.
NOTE: DO NOT put quotes around the query filter.
<b>result_filter (default: %s</b>)
- Format template applied to result attributes. Sup-
- ports the same expansions as the query_filter, and
- can be easily used to append (or prepend) text.
- This parameter supports the following '%' expan-
+ Format template applied to result attributes. Sup-
+ ports the same expansions as the query_filter, and
+ can be easily used to append (or prepend) text.
+ This parameter supports the following '%' expan-
sions:
- <b>%s</b> This is replaced by the value of the result
+ <b>%s</b> This is replaced by the value of the result
attribute.
- <b>%u</b> When the result attribute is an address of
- the form user@domain, <b>%u</b> is replaced local
- part of the address, if the result attribute
- is unqualified, <b>%u</b> is replaced by the entire
+ <b>%u</b> When the result attribute value is an
+ address of the form user@domain, <b>%u</b> is
+ replaced by the local part of the address.
+ Otherwise, <b>%u</b> is replaced by the entire
attribute value.
- <b>%d</b> When a result attribute is an address of the
- form user@domain, <b>%d</b> is replaced by the
- domain part of the attribute value. If an
- attribute value is unqualified <b>%d</b> is
- replaced by the entire attribute value.
+ <b>%d</b> When a result attribute value is an address
+ of the form user@domain, <b>%d</b> is replaced by
+ the domain part of the attribute value.
+ Otherwise, <b>%d</b> is replaced by the entire
+ attribute value.
For example, using "result_filter = <a href="smtp.8.html">smtp</a>:[%s]"
allows one to use a mailHost attribute as the basis
- of a <a href="transport.5.html">transport(5)</a> table. After applying the result
- filter, multiple values are concatenated as comma
- separated strings. The expansion_limit and
- size_limit parameters explained below allow one to
- restrict the number of values in the result, which
- is especially useful for maps that should return a
+ of a <a href="transport.5.html">transport(5)</a> table. After applying the result
+ filter, multiple values are concatenated as comma
+ separated strings. The expansion_limit and
+ size_limit parameters explained below allow one to
+ restrict the number of values in the result, which
+ is especially useful for maps that should return a
single value.
- The default value <b>%s</b> specifies that each attribute
+ The default value <b>%s</b> specifies that each attribute
value should be used as is.
NOTE: DO NOT put quotes around the result filter!
<b>domain (default: no domain list)</b>
- This is a list of domain names, paths to files, or
- dictionaries. When specified, only fully qualified
- search keys with a *non-empty* localpart and a
- matching domain are eligible for lookup: 'user'
- lookups, bare domain lookups and "@domain" lookups
- are not performed. This can significantly reduce
+ This is a list of domain names, paths to files, or
+ dictionaries. When specified, only fully qualified
+ search keys with a *non-empty* localpart and a
+ matching domain are eligible for lookup: 'user'
+ lookups, bare domain lookups and "@domain" lookups
+ are not performed. This can significantly reduce
the query load on the LDAP server.
domain = postfix.org, hash:/etc/postfix/search-
domains
- It is best not to use LDAP to store the domains
+ It is best not to use LDAP to store the domains
eligible for LDAP lookups.
- NOTE: DO NOT define this parameter for <a href="local.8.html">local(8)</a>
+ NOTE: DO NOT define this parameter for <a href="local.8.html">local(8)</a>
aliases.
<b>result_attribute (default: maildrop)</b>
- The attribute(s) Postfix will read from any direc-
+ The attribute(s) Postfix will read from any direc-
tory entries returned by the lookup, to be resolved
to an email address.
result_attribute = mailbox,maildrop
<b>special_result_attribute (No default)</b>
The attribute(s) of directory entries that can con-
- tain DNs or URLs. If found, a recursive subsequent
+ tain DNs or URLs. If found, a recursive subsequent
search is done using their values.
special_result_attribute = member
- DN recursion retrieves the same result_attributes
+ DN recursion retrieves the same result_attributes
as the main query, including the special attributes
- for further recursion. URI processing retrieves
- only those attributes that are included in the URI
- definition and are *also* listed in
- "result_attribute". If the URI lists any of the
- map's special result attributes, these are also
+ for further recursion. URI processing retrieves
+ only those attributes that are included in the URI
+ definition and are *also* listed in
+ "result_attribute". If the URI lists any of the
+ map's special result attributes, these are also
retrieved and used recursively.
<b>scope (default: sub)</b>
- The LDAP search scope: <b>sub</b>, <b>base</b>, or <b>one</b>. These
+ The LDAP search scope: <b>sub</b>, <b>base</b>, or <b>one</b>. These
translate into LDAP_SCOPE_SUBTREE, LDAP_SCOPE_BASE,
and LDAP_SCOPE_ONELEVEL.
<b>bind (default: yes)</b>
- Whether or not to bind to the LDAP server. Newer
+ Whether or not to bind to the LDAP server. Newer
LDAP implementations don't require clients to bind,
which saves time. Example:
bind = no
- If you do need to bind, you might consider config-
- uring Postfix to connect to the local machine on a
- port that's an SSL tunnel to your LDAP server. If
- your LDAP server doesn't natively support SSL, put
+ If you do need to bind, you might consider config-
+ uring Postfix to connect to the local machine on a
+ port that's an SSL tunnel to your LDAP server. If
+ your LDAP server doesn't natively support SSL, put
a tunnel (wrapper, proxy, whatever you want to call
- it) on that system too. This should prevent the
- password from traversing the network in the clear.
+ it) on that system too. This should prevent the
+ password from traversing the network in the clear.
<b>bind_dn (default: empty)</b>
- If you do have to bind, do it with this distin-
+ If you do have to bind, do it with this distin-
guished name. Example:
bind_dn = uid=postfix, dc=your, dc=com
<b>bind_pw (default: empty)</b>
- The password for the distinguished name above. If
+ The password for the distinguished name above. If
you have to use this, you probably want to make the
map configuration file readable only by the Postfix
- user. When using the obsolete <a href="ldap_table.5.html">ldap</a>:ldapsource syn-
+ user. When using the obsolete <a href="ldap_table.5.html">ldap</a>:ldapsource syn-
tax, with map parameters in main.cf, it is not pos-
- sible to securely store the bind password. This is
+ sible to securely store the bind password. This is
because main.cf needs to be world readable to allow
local accounts to submit mail via the sendmail com-
mand. Example:
<b>cache_expiry (IGNORED with a warning)</b>
<b>cache_size (IGNORED with a warning)</b>
- The above parameters are NO LONGER SUPPORTED by
+ The above parameters are NO LONGER SUPPORTED by
Postfix. Cache support has been dropped from
OpenLDAP as of release 2.1.13.
<b>recursion_limit (default: 1000)</b>
- A limit on the nesting depth of DN and URL special
- result attribute evaluation. The limit must be a
+ A limit on the nesting depth of DN and URL special
+ result attribute evaluation. The limit must be a
non-zero positive number.
<b>expansion_limit (default: 0)</b>
- A limit on the total number of result elements
- returned (as a comma separated list) by a lookup
- against the map. A setting of zero disables the
- limit. Lookups fail with a temporary error if the
- limit is exceeded. Setting the limit to 1 ensures
+ A limit on the total number of result elements
+ returned (as a comma separated list) by a lookup
+ against the map. A setting of zero disables the
+ limit. Lookups fail with a temporary error if the
+ limit is exceeded. Setting the limit to 1 ensures
that lookups do not return multiple values.
<b>size_limit (default: $expansion_limit)</b>
- A limit on the number of LDAP entries returned by
- any single LDAP query performed as part of the
- lookup. A setting of 0 disables the limit. Expan-
- sion of DN and URL references involves nested LDAP
- queries, each of which is separately subjected to
+ A limit on the number of LDAP entries returned by
+ any single LDAP query performed as part of the
+ lookup. A setting of 0 disables the limit. Expan-
+ sion of DN and URL references involves nested LDAP
+ queries, each of which is separately subjected to
this limit.
- Note: even a single LDAP entry can generate multi-
- ple lookup results, via multiple result attributes
- and/or multi-valued result attributes. This limit
+ Note: even a single LDAP entry can generate multi-
+ ple lookup results, via multiple result attributes
+ and/or multi-valued result attributes. This limit
caps the per query resource utilization on the LDAP
- server, not the final multiplicity of the lookup
- result. It is analogous to the "-z" option of
+ server, not the final multiplicity of the lookup
+ result. It is analogous to the "-z" option of
"ldapsearch".
<b>dereference (default: 0)</b>
- When to dereference LDAP aliases. (Note that this
+ When to dereference LDAP aliases. (Note that this
has nothing do with Postfix aliases.) The permitted
- values are those legal for the OpenLDAP/UM LDAP
+ values are those legal for the OpenLDAP/UM LDAP
implementations:
0 never
3 always
See ldap.h or the ldap_open(3) or ldapsearch(1) man
- pages for more information. And if you're using an
+ pages for more information. And if you're using an
LDAP package that has other possible values, please
- bring it to the attention of the postfix-
+ bring it to the attention of the postfix-
users@postfix.org mailing list.
<b>chase_referrals (default: 0)</b>
- Sets (or clears) LDAP_OPT_REFERRALS (requires LDAP
+ Sets (or clears) LDAP_OPT_REFERRALS (requires LDAP
version 3 support).
<b>version (default: 2)</b>
Specifies the LDAP protocol version to use.
<b>debuglevel (default: 0)</b>
- What level to set for debugging in the OpenLDAP
+ What level to set for debugging in the OpenLDAP
libraries.
<b>LDAP SSL AND STARTTLS PARAMETERS</b>
- If you're using the OpenLDAP libraries compiled with SSL
- support, Postfix can connect to LDAP SSL servers and can
+ If you're using the OpenLDAP libraries compiled with SSL
+ support, Postfix can connect to LDAP SSL servers and can
issue the STARTTLS command.
- LDAP SSL service can be requested by using a LDAP SSL URL
+ LDAP SSL service can be requested by using a LDAP SSL URL
in the server_host parameter:
server_host = ldaps://ldap.your.com:636
STARTTLS can be turned on with the start_tls parameter:
start_tls = yes
- Both forms require LDAP protocol version 3, which has to
+ Both forms require LDAP protocol version 3, which has to
be set explicitly with:
version = 3
If any of the Postfix programs querying the map is config-
- ured in master.cf to run chrooted, all the certificates
+ ured in master.cf to run chrooted, all the certificates
and keys involved have to be copied to the chroot jail. Of
- course, the private keys should only be readable by the
+ course, the private keys should only be readable by the
user "postfix".
- The following parameters are relevant to LDAP SSL and
+ The following parameters are relevant to LDAP SSL and
STARTTLS:
<b>start_tls (default: no)</b>
Whether or not to issue STARTTLS upon connection to
- the server. Don't set this with LDAP SSL (the SSL
+ the server. Don't set this with LDAP SSL (the SSL
session is setup automatically when the TCP connec-
tion is opened).
- <b>tls_ca_cert_dir (No default; set either this or</b>
+ <b>tls_ca_cert_dir (No default; set either this or</b>
<b>tls_ca_cert_file)</b>
Directory containing X509 Certificate Authority
- certificates in PEM format which are to be recog-
- nized by the client in SSL/TLS connections. The
- files each contain one CA certificate. The files
- are looked up by the CA subject name hash value,
- which must hence be available. If more than one CA
- certificate with the same name hash value exist,
- the extension must be different (e.g. 9d66eef0.0,
- 9d66eef0.1 etc). The search is performed in the
- ordering of the extension number, regardless of
+ certificates in PEM format which are to be recog-
+ nized by the client in SSL/TLS connections. The
+ files each contain one CA certificate. The files
+ are looked up by the CA subject name hash value,
+ which must hence be available. If more than one CA
+ certificate with the same name hash value exist,
+ the extension must be different (e.g. 9d66eef0.0,
+ 9d66eef0.1 etc). The search is performed in the
+ ordering of the extension number, regardless of
other properties of the certificates. Use the
c_rehash utility (from the OpenSSL distribution) to
create the necessary links.
- <b>tls_ca_cert_file (No default; set either this or</b>
+ <b>tls_ca_cert_file (No default; set either this or</b>
<b>tls_ca_cert_dir)</b>
File containing the X509 Certificate Authority cer-
- tificates in PEM format which are to be recognized
- by the client in SSL/TLS connections. This setting
+ tificates in PEM format which are to be recognized
+ by the client in SSL/TLS connections. This setting
takes precedence over tls_ca_cert_dir.
<b>tls_cert (No default; you must set this)</b>
- File containing client's X509 certificate to be
+ File containing client's X509 certificate to be
used by the client in SSL/ TLS connections.
<b>tls_key (No default; you must set this)</b>
- File containing the private key corresponding to
+ File containing the private key corresponding to
the above tls_cert.
<b>tls_require_cert (default: no)</b>
Whether or not to request server's X509 certificate
- and check its validity when establishing SSL/TLS
+ and check its validity when establishing SSL/TLS
connections.
<b>tls_random_file (No default)</b>
- Path of a file to obtain random bits from when
- /dev/[u]random is not available, to be used by the
+ Path of a file to obtain random bits from when
+ /dev/[u]random is not available, to be used by the
client in SSL/TLS connections.
<b>tls_cipher_suite (No default)</b>
Cipher suite to use in SSL/TLS negotiations.
<b>EXAMPLE</b>
- Here's a basic example for using LDAP to look up <a href="local.8.html">local(8)</a>
+ Here's a basic example for using LDAP to look up <a href="local.8.html">local(8)</a>
aliases. Assume that in main.cf, you have:
<a href="postconf.5.html#alias_maps">alias_maps</a> = hash:/etc/aliases,
<a href="ldap_table.5.html">ldap</a>:/etc/postfix/ldap-aliases.cf
server_host = ldap.my.com
search_base = dc=my, dc=com
- Upon receiving mail for a local address "ldapuser" that
- isn't found in the /etc/aliases database, Postfix will
- search the LDAP server listening at port 389 on
- ldap.my.com. It will bind anonymously, search for any
- directory entries whose mailacceptinggeneralid attribute
- is "ldapuser", read the "maildrop" attributes of those
- found, and build a list of their maildrops, which will be
- treated as <a href="http://www.faqs.org/rfcs/rfc822.html">RFC822</a> addresses to which the message will be
+ Upon receiving mail for a local address "ldapuser" that
+ isn't found in the /etc/aliases database, Postfix will
+ search the LDAP server listening at port 389 on
+ ldap.my.com. It will bind anonymously, search for any
+ directory entries whose mailacceptinggeneralid attribute
+ is "ldapuser", read the "maildrop" attributes of those
+ found, and build a list of their maildrops, which will be
+ treated as <a href="http://www.faqs.org/rfcs/rfc822.html">RFC822</a> addresses to which the message will be
delivered.
<b>SEE ALSO</b>
<a href="LDAP_README.html">LDAP_README</a>, Postfix LDAP client guide
<b>LICENSE</b>
- The Secure Mailer license must be distributed with this
+ The Secure Mailer license must be distributed with this
software.
<b>AUTHOR(S)</b>
- Carsten Hoeger, Hery Rakotoarisoa, John Hensley, Keith
- Stevenson, LaMont Jones, Liviu Daia, Manuel Guesdon, Mike
- Mattice, Prabhat K Singh, Sami Haahtinen, Samuel Tardieu,
+ Carsten Hoeger, Hery Rakotoarisoa, John Hensley, Keith
+ Stevenson, LaMont Jones, Liviu Daia, Manuel Guesdon, Mike
+ Mattice, Prabhat K Singh, Sami Haahtinen, Samuel Tardieu,
Victor Duchovni, and many others.
LDAP_TABLE(5)
file at the end of a service definition. The syntax is as
follows:
+ <b>directory=</b><i>pathname</i> (optional, default: <b>$<a href="postconf.5.html#queue_directory">queue_directory</a></b>)
+ Change to the named directory before executing the
+ external command. Delivery is deferred in case of
+ failure.
+
+ This feature is available as of Postfix 2.2.
+
<b>eol=string</b> (optional, default: <b>\n</b>)
The output record delimiter. Typically one would
use either <b>\r\n</b> or <b>\n</b>. The usual C-style backslash
escape sequences are recognized: <b>\a \b \f \n \r \t</b>
- <b>\v \</b><i>octal</i> and <b>\\</b>.
+ <b>\v \</b><i>ddd</i> (up to three octal digits) and <b>\\</b>.
<b>flags=BDFORhqu.</b>> (optional)
Optional message processing flags. By default, a
most <b>@</b> character) to lower case. This is
recommended for delivery via <b>UUCP</b>.
- <b>.</b> Prepend <b>.</b> to lines starting with "<b>.</b>". This
+ <b>.</b> Prepend "<b>.</b>" to lines starting with "<b>.</b>". This
is needed by, for example, <b>BSMTP</b> software.
- > Prepend > to lines starting with "<b>From</b> ".
+ > Prepend ">" to lines starting with "<b>From</b> ".
This is expected by, for example, <b>UUCP</b> soft-
ware.
$(<i>name</i>) are also recognized. Specify <b>$$</b> where a single <b>$</b>
is wanted.
- Available in Postfix 2.2 and later:
-
- <b>directory=</b><i>pathname</i> (optional)
- Change to the specified directory before executing
- the command. Failure causes mail delivery to be
- deferred.
-
<b>DIAGNOSTICS</b>
Command exit status codes are expected to follow the con-
ventions defined in <<b>sysexits.h</b>>.
<DT><b><a name="bounce_size_limit">bounce_size_limit</a>
(default: 50000)</b></DT><DD>
-<p>
-The maximal amount of original message text that is sent in a
-non-delivery notification. Specify a byte count.
-</p>
+<p> The maximal amount of original message text that is sent in a
+non-delivery notification. Specify a byte count. If you increase
+this limit, then you should increase the <a href="postconf.5.html#mime_nesting_limit">mime_nesting_limit</a> value
+proportionally. </p>
</DD>
by the amount specified in $<a href="postconf.5.html#debug_peer_level">debug_peer_level</a>. </p>
<p> Specify domain names, network/netmask patterns, "/file/name"
-patterns or "<a href="DATABASE_README.html">type:table</a>" lookup tables. The result from lookup
-tables is ignored. </p>
+patterns or "<a href="DATABASE_README.html">type:table</a>" lookup tables. The right-hand side result
+from "<a href="DATABASE_README.html">type:table</a>" lookups is ignored. </p>
<p> Pattern matching of domain names is controlled by the
<a href="postconf.5.html#parent_domain_matches_subdomains">parent_domain_matches_subdomains</a> parameter. </p>
</DD>
<DT><b><a name="lmtp_rset_timeout">lmtp_rset_timeout</a>
-(default: 120s)</b></DT><DD>
+(default: 20s)</b></DT><DD>
-<p>
-The LMTP client time limit for sending the RSET command, and for
-receiving the server response. The LMTP client sends RSET in order
-to find out if a cached connection is still alive.
-</p>
+<p> The LMTP client time limit for sending the RSET command, and
+for receiving the server response. The LMTP client sends RSET in
+order to finish a recipient address probe, or to verify that a
+cached connection is still alive. </p>
<p>
Time units: s (seconds), m (minutes), h (hours), d (days), w (weeks).
(default: 100)</b></DT><DD>
<p>
-The maximal nesting level of multipart mail that the MIME processor
-will handle. Postfix refuses mail that is nested deeper.
+The maximal recursion level that the MIME processor will handle.
+Postfix refuses mail that is nested deeper than the specified limit.
</p>
<p>
</p>
+</DD>
+
+<DT><b><a name="session_cache_service">session_cache_service</a>
+(default: scache)</b></DT><DD>
+
+<p> The name of the <a href="scache.8.html">scache(8)</a> session cache service. This service
+maintains a limited pool of cached sessions. </p>
+
+
+</DD>
+
+<DT><b><a name="session_cache_ttl_limit">session_cache_ttl_limit</a>
+(default: 2s)</b></DT><DD>
+
+<p> The maximal time-to-live value that the session cache server
+allows. Requests that specify a larger TTL will be stored with the
+maximum allowed TTL. The purpose of this additional control is to
+protect the infrastructure against careless people. The cache TTL
+is already bounded by $<a href="postconf.5.html#max_idle">max_idle</a>. </p>
+
+
</DD>
<DT><b><a name="setgid_group">setgid_group</a>
</p>
+</DD>
+
+<DT><b><a name="smtp_connection_cache_domains">smtp_connection_cache_domains</a>
+(default: empty)</b></DT><DD>
+
+<p> The SMTP destinations for which SMTP connection caching is
+enabled. With SMTP connection caching, a connection is not closed
+immediately after completion of a mail transaction. Instead, the
+connection is kept open for up to $<a href="postconf.5.html#smtp_connection_cache_time_limit">smtp_connection_cache_time_limit</a>
+seconds. This allows connections to be reused for other deliveries,
+and can improve mail delivery performance. </p>
+
+<p> Specify a comma or white space separated list of destinations
+or pseudo-destinations:
+</p>
+
+<ul>
+
+<li> a domain name (the right-hand side of an email address),
+
+<li> a relay host (including optional [] and/or non-default TCP
+port), using the exact same spelling as in main.cf or in the
+transport map,
+
+<li> a /file/name with domains and/or relay hosts,
+
+<li> a "<a href="DATABASE_README.html">type:table</a>" with domains and/or relay hosts on the left-hand
+side. The right-hand side result from "<a href="DATABASE_README.html">type:table</a>" lookups is
+ignored.
+
+</ul>
+
+<p></p>
+
+
+</DD>
+
+<DT><b><a name="smtp_connection_cache_reuse_limit">smtp_connection_cache_reuse_limit</a>
+(default: 10)</b></DT><DD>
+
+<p> When SMTP session caching is enabled, the number of times that
+an SMTP session is reused before it is closed.
+</p>
+
+
+</DD>
+
+<DT><b><a name="smtp_connection_cache_time_limit">smtp_connection_cache_time_limit</a>
+(default: 2s)</b></DT><DD>
+
+<p> When SMTP session caching is enabled, the amount of time that
+an unused SMTP client socket is kept open before it is closed. Do
+not specify larger values without permission from the remote sites.
+</p>
+
+
</DD>
<DT><b><a name="smtp_data_done_timeout">smtp_data_done_timeout</a>
</DD>
<DT><b><a name="smtp_rset_timeout">smtp_rset_timeout</a>
-(default: 120s)</b></DT><DD>
+(default: 20s)</b></DT><DD>
<p> The SMTP client time limit for sending the RSET command, and
-for receiving the server response. </p>
+for receiving the server response. The SMTP client sends RSET in
+order to finish a recipient address probe, or to verify that a
+cached session is still usable. </p>
<p> This feature is available in Postfix 2.1 and later. </p>
--- /dev/null
+<!doctype html public "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+<html> <head>
+<meta http-equiv="Content-Type" content="text/html; charset=us-ascii">
+<title> Postfix manual - scache(8) </title>
+</head> <body> <pre>
+SCACHE(8) SCACHE(8)
+
+<b>NAME</b>
+ scache - Postfix session cache server
+
+<b>SYNOPSIS</b>
+ <b>scache</b> [generic Postfix daemon options]
+
+<b>DESCRIPTION</b>
+ The scache server maintains the Postfix session cache.
+ This information can be used by, for example, the Postfix
+ SMTP client.
+
+ The session cache is organized into logical destination
+ names, physical endpoint names, and sessions.
+
+ As a specific example, logical SMTP destinations specify
+ (transport, domain, port), and physical SMTP endpoints
+ specify (transport, IP address, port). An SMTP session
+ may be saved after a successful mail transaction.
+
+ In the general case, one logical destination may refer to
+ zero or more physical endpoints, one physical endpoint may
+ be referenced by zero or more logical destinations, and
+ one endpoint may refer to zero or more sessions.
+
+ The exact syntax of a logical destination or endpoint name
+ is application dependent; the <b>scache</b> service does not
+ care. A session is stored as a file descriptor together
+ with application-dependent information that is needed to
+ re-activate a session object. Again, the <b>scache</b> service is
+ completely unaware about the details of that information.
+
+ All information is stored with a finite time to live
+ (ttl). The session cache daemon terminates when no client
+ is connected for <b><a href="postconf.5.html#max_idle">max_idle</a></b> time units.
+
+ This server implements the following requests:
+
+ <b>save_endp</b> <i>ttl endpoint endpoint</i><b>_</b><i>properties file</i><b>_</b><i>descriptor</i>
+ Save the specified file descriptor and session
+ property data under the specified endpoint name.
+ The endpoint properties are used by the client to
+ re-activate a passivated session object. queue ID
+ is queued for the specified destination.
+
+ <b>find_endp</b> <i>endpoint</i>
+ Look up cached properties and a cached file
+ descriptor for the specified endpoint.
+
+ <b>save_dest</b> <i>ttl destination destination</i><b>_</b><i>properties endpoint</i>
+ Save the binding between a logical destination and
+ an endpoint under the destination name, together
+ with destination specific session properties. The
+ destination properties are used by the client to
+ re-activate a passivated session object.
+
+ <b>find_dest</b> <i>destination</i>
+ Look up cached destination properties, cached end-
+ point properties, and a cached file descriptor for
+ the specified logical destination.
+
+<b>SECURITY</b>
+ The session cache server is not security-sensitive. It
+ does not talk to the network, and it does not talk to
+ local users. The scache server can run chrooted at fixed
+ low privilege.
+
+ The session cache server is not a trusted process. It must
+ not be used to store information that is security sensi-
+ tive.
+
+<b>DIAGNOSTICS</b>
+ Problems and transactions are logged to <b>syslogd</b>(8).
+
+<b>BUGS</b>
+ Sessions cannot be cached across multiple machines.
+
+ When a session expires from the cache it is closed without
+ protocol specific handshake.
+
+<b>CONFIGURATION PARAMETERS</b>
+ Changes to <b>main.cf</b> are picked up automatically as
+ <a href="scache.8.html">scache(8)</a> processes run for only a limited amount of time.
+ Use the command "<b>postfix reload</b>" to speed up a change.
+
+ The text below provides only a parameter summary. See
+ <a href="postconf.5.html">postconf(5)</a> for more details including examples.
+
+<b>RESOURCE CONTROLS</b>
+ <b><a href="postconf.5.html#session_cache_ttl_limit">session_cache_ttl_limit</a> (2s)</b>
+ The maximal time-to-live value that the session
+ cache server allows.
+
+<b>MISCELLANEOUS CONTROLS</b>
+ <b><a href="postconf.5.html#config_directory">config_directory</a> (see 'postconf -d' output)</b>
+ The default location of the Postfix main.cf and
+ master.cf configuration files.
+
+ <b><a href="postconf.5.html#daemon_timeout">daemon_timeout</a> (18000s)</b>
+ How much time a Postfix daemon process may take to
+ handle a request before it is terminated by a
+ built-in watchdog timer.
+
+ <b><a href="postconf.5.html#ipc_timeout">ipc_timeout</a> (3600s)</b>
+ The time limit for sending or receiving information
+ over an internal communication channel.
+
+ <b><a href="postconf.5.html#max_idle">max_idle</a> (100s)</b>
+ The maximum amount of time that an idle Postfix
+ daemon process waits for the next service request
+ before exiting.
+
+ <b><a href="postconf.5.html#process_id">process_id</a> (read-only)</b>
+ The process ID of a Postfix command or daemon pro-
+ cess.
+
+ <b><a href="postconf.5.html#process_name">process_name</a> (read-only)</b>
+ The process name of a Postfix command or daemon
+ process.
+
+ <b><a href="postconf.5.html#syslog_facility">syslog_facility</a> (mail)</b>
+ The syslog facility of Postfix logging.
+
+ <b><a href="postconf.5.html#syslog_name">syslog_name</a> (postfix)</b>
+ The mail system name that is prepended to the pro-
+ cess name in syslog records, so that "smtpd"
+ becomes, for example, "postfix/smtpd".
+
+<b>SEE ALSO</b>
+ <a href="smtp.8.html">smtp(8)</a>, SMTP client
+ <a href="postconf.5.html">postconf(5)</a>, configuration parameters
+ <a href="master.8.html">master(8)</a>, process manager
+ syslogd(8), system logging
+
+<b>LICENSE</b>
+ The Secure Mailer license must be distributed with this
+ software.
+
+<b>HISTORY</b>
+ This service was introduced with Postfix version 2.2.
+
+<b>AUTHOR(S)</b>
+ Wietse Venema
+ IBM T.J. Watson Research
+ P.O. Box 704
+ Yorktown Heights, NY 10598, USA
+
+ SCACHE(8)
+</pre> </body> </html>
fails due to a recoverable error condition, the SMTP
client will try to deliver the mail to an alternate host.
+ After a successful mail transaction, a session may be
+ saved to the <a href="scache.8.html"><b>scache(8)</a></b> session cache server, so that it
+ may be used by any SMTP client for a subsequent transac-
+ tion. Session caching is disabled by default.
+
<b>SECURITY</b>
The SMTP client is moderately security-sensitive. It talks
- to SMTP servers and to DNS servers on the network. The
+ to SMTP servers and to DNS servers on the network. The
SMTP client can be run chrooted at fixed low privilege.
<b>STANDARDS</b>
<a href="http://www.faqs.org/rfcs/rfc2920.html">RFC 2920</a> (SMTP Pipelining)
<b>DIAGNOSTICS</b>
- Problems and transactions are logged to <b>syslogd</b>(8). Cor-
- rupted message files are marked so that the queue manager
+ Problems and transactions are logged to <b>syslogd</b>(8). Cor-
+ rupted message files are marked so that the queue manager
can move them to the <b>corrupt</b> queue for further inspection.
- Depending on the setting of the <b><a href="postconf.5.html#notify_classes">notify_classes</a></b> parameter,
- the postmaster is notified of bounces, protocol problems,
+ Depending on the setting of the <b><a href="postconf.5.html#notify_classes">notify_classes</a></b> parameter,
+ the postmaster is notified of bounces, protocol problems,
and of other trouble.
+<b>BUGS</b>
+ SMTP session caching does not work with TLS. The necessary
+ support for object passivation and re-activation does not
+ exist.
+
+ SMTP session caching assumes that SASL credentials are
+ valid for all destinations that map onto the same IP
+ address and TCP port.
+
<b>CONFIGURATION PARAMETERS</b>
Changes to <b>main.cf</b> are picked up automatically, as <a href="smtp.8.html">smtp(8)</a>
- processes run for only a limited amount of time. Use the
+ processes run for only a limited amount of time. Use the
command "<b>postfix reload</b>" to speed up a change.
- The text below provides only a parameter summary. See
+ The text below provides only a parameter summary. See
<a href="postconf.5.html">postconf(5)</a> for more details including examples.
<b>COMPATIBILITY CONTROLS</b>
Never send EHLO at the start of an SMTP session.
<b><a href="postconf.5.html#smtp_defer_if_no_mx_address_found">smtp_defer_if_no_mx_address_found</a> (no)</b>
- Defer mail delivery when no MX record resolves to
+ Defer mail delivery when no MX record resolves to
an IP address.
<b><a href="postconf.5.html#smtp_line_length_limit">smtp_line_length_limit</a> (990)</b>
that Postfix will send via SMTP.
<b><a href="postconf.5.html#smtp_pix_workaround_delay_time">smtp_pix_workaround_delay_time</a> (10s)</b>
- How long the Postfix SMTP client pauses before
+ How long the Postfix SMTP client pauses before
sending ".<CR><LF>" in order to work around the PIX
firewall "<CR><LF>.<CR><LF>" bug.
<b><a href="postconf.5.html#smtp_pix_workaround_threshold_time">smtp_pix_workaround_threshold_time</a> (500s)</b>
- How long a message must be queued before the PIX
- firewall "<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.
<b><a href="postconf.5.html#smtp_quote_rfc821_envelope">smtp_quote_rfc821_envelope</a> (yes)</b>
- Quote addresses in SMTP MAIL FROM and RCPT TO com-
+ Quote addresses in SMTP MAIL FROM and RCPT TO com-
mands as required by <a href="http://www.faqs.org/rfcs/rfc821.html">RFC 821</a>.
<b><a href="postconf.5.html#smtp_skip_5xx_greeting">smtp_skip_5xx_greeting</a> (yes)</b>
(go away, do not try again later).
<b><a href="postconf.5.html#smtp_skip_quit_response">smtp_skip_quit_response</a> (yes)</b>
- Do not wait for the response to the SMTP QUIT com-
+ Do not wait for the response to the SMTP QUIT com-
mand.
Available in Postfix version 2.0 and earlier:
Available in Postfix version 2.0 and later:
<b><a href="postconf.5.html#disable_mime_output_conversion">disable_mime_output_conversion</a> (no)</b>
- Disable the conversion of 8BITMIME format to 7BIT
+ Disable the conversion of 8BITMIME format to 7BIT
format.
<b><a href="postconf.5.html#mime_boundary_length_limit">mime_boundary_length_limit</a> (2048)</b>
strings.
<b><a href="postconf.5.html#mime_nesting_limit">mime_nesting_limit</a> (100)</b>
- The maximal nesting level of multipart mail that
- the MIME processor will handle.
+ The maximal recursion level that the MIME processor
+ will handle.
<b>EXTERNAL CONTENT INSPECTION CONTROLS</b>
Available in Postfix version 2.1 and later:
<b><a href="postconf.5.html#smtp_send_xforward_command">smtp_send_xforward_command</a> (no)</b>
- Send the non-standard XFORWARD command when the
- Postfix SMTP server EHLO response announces XFOR-
+ Send the non-standard XFORWARD command when the
+ Postfix SMTP server EHLO response announces XFOR-
WARD support.
<b>SASL AUTHENTICATION CONTROLS</b>
<b><a href="postconf.5.html#smtp_sasl_auth_enable">smtp_sasl_auth_enable</a> (no)</b>
- Enable SASL authentication in the Postfix SMTP
+ Enable SASL authentication in the Postfix SMTP
client.
<b><a href="postconf.5.html#smtp_sasl_password_maps">smtp_sasl_password_maps</a> (empty)</b>
- Optional SMTP client lookup tables with one user-
- name:password entry per remote hostname or domain.
+ Optional SMTP client lookup tables with one user-
+ name:password entry per remote hostname or domain.
<b><a href="postconf.5.html#smtp_sasl_security_options">smtp_sasl_security_options</a> (noplaintext, noanonymous)</b>
- What authentication mechanisms the Postfix SMTP
+ What authentication mechanisms the Postfix SMTP
client is allowed to use.
<b>RESOURCE AND RATE CONTROLS</b>
<b><a href="postconf.5.html#smtp_destination_concurrency_limit">smtp_destination_concurrency_limit</a> ($<a href="postconf.5.html#default_destination_concurrency_limit">default_destina</a>-</b>
<b><a href="postconf.5.html#default_destination_concurrency_limit">tion_concurrency_limit</a>)</b>
- The maximal number of parallel deliveries to the
- same destination via the smtp message delivery
+ The maximal number of parallel deliveries to the
+ same destination via the smtp message delivery
transport.
<b><a href="postconf.5.html#smtp_destination_recipient_limit">smtp_destination_recipient_limit</a> ($<a href="postconf.5.html#default_destination_recipient_limit">default_destina</a>-</b>
<b><a href="postconf.5.html#default_destination_recipient_limit">tion_recipient_limit</a>)</b>
- The maximal number of recipients per delivery via
+ The maximal number of recipients per delivery via
the smtp message delivery transport.
<b><a href="postconf.5.html#smtp_connect_timeout">smtp_connect_timeout</a> (30s)</b>
- The SMTP client time limit for completing a TCP
+ The SMTP client time limit for completing a TCP
connection, or zero (use the operating system
built-in time limit).
<b><a href="postconf.5.html#smtp_helo_timeout">smtp_helo_timeout</a> (300s)</b>
- The SMTP client time limit for sending the HELO or
- EHLO command, and for receiving the initial server
+ The SMTP client time limit for sending the HELO or
+ EHLO command, and for receiving the initial server
response.
<b><a href="postconf.5.html#smtp_xforward_timeout">smtp_xforward_timeout</a> (300s)</b>
command, and for receiving the server response.
<b><a href="postconf.5.html#smtp_mail_timeout">smtp_mail_timeout</a> (300s)</b>
- The SMTP client time limit for sending the MAIL
- FROM command, and for receiving the server
+ The SMTP client time limit for sending the MAIL
+ FROM command, and for receiving the server
response.
<b><a href="postconf.5.html#smtp_rcpt_timeout">smtp_rcpt_timeout</a> (300s)</b>
- The SMTP client time limit for sending the SMTP
- RCPT TO command, and for receiving the server
+ The SMTP client time limit for sending the SMTP
+ RCPT TO command, and for receiving the server
response.
<b><a href="postconf.5.html#smtp_data_init_timeout">smtp_data_init_timeout</a> (120s)</b>
- The SMTP client time limit for sending the SMTP
- DATA command, and for receiving the server
+ The SMTP client time limit for sending the SMTP
+ DATA command, and for receiving the server
response.
<b><a href="postconf.5.html#smtp_data_xfer_timeout">smtp_data_xfer_timeout</a> (180s)</b>
- The SMTP client time limit for sending the SMTP
+ The SMTP client time limit for sending the SMTP
message content.
<b><a href="postconf.5.html#smtp_data_done_timeout">smtp_data_done_timeout</a> (600s)</b>
- The SMTP client time limit for sending the SMTP
+ The SMTP client time limit for sending the SMTP
".", and for receiving the server response.
<b><a href="postconf.5.html#smtp_quit_timeout">smtp_quit_timeout</a> (300s)</b>
- The SMTP client time limit for sending the QUIT
+ The SMTP client time limit for sending the QUIT
command, and for receiving the server response.
Available in Postfix version 2.1 and later:
lookups, or zero (no limit).
<b><a href="postconf.5.html#smtp_mx_session_limit">smtp_mx_session_limit</a> (2)</b>
- The maximal number of SMTP sessions per delivery
- request before giving up or delivering to a fall-
+ The maximal number of SMTP sessions per delivery
+ request before giving up or delivering to a fall-
back relay host, or zero (no limit).
- <b><a href="postconf.5.html#smtp_rset_timeout">smtp_rset_timeout</a> (120s)</b>
- The SMTP client time limit for sending the RSET
+ <b><a href="postconf.5.html#smtp_rset_timeout">smtp_rset_timeout</a> (20s)</b>
+ The SMTP client time limit for sending the RSET
command, and for receiving the server response.
+ Available in Postfix version 2.2 and later:
+
+ <b><a href="postconf.5.html#smtp_connection_cache_domains">smtp_connection_cache_domains</a> (empty)</b>
+ The SMTP destinations for which SMTP connection
+ caching is enabled.
+
+ <b><a href="postconf.5.html#smtp_connection_cache_reuse_limit">smtp_connection_cache_reuse_limit</a> (10)</b>
+ When SMTP session caching is enabled, the number of
+ times that an SMTP session is reused before it is
+ closed.
+
+ <b><a href="postconf.5.html#smtp_connection_cache_time_limit">smtp_connection_cache_time_limit</a> (2s)</b>
+ When SMTP session caching is enabled, the amount of
+ time that an unused SMTP client socket is kept open
+ before it is closed.
+
<b>TROUBLE SHOOTING CONTROLS</b>
<b><a href="postconf.5.html#debug_peer_level">debug_peer_level</a> (2)</b>
The increment in verbose logging level when a
<b>SEE ALSO</b>
<a href="qmgr.8.html">qmgr(8)</a>, queue manager
<a href="bounce.8.html">bounce(8)</a>, delivery status reports
+ <a href="scache.8.html">scache(8)</a>, session cache server
<a href="postconf.5.html">postconf(5)</a>, configuration parameters
<a href="master.8.html">master(8)</a>, process manager
syslogd(8), system logging
P.O. Box 704
Yorktown Heights, NY 10598, USA
+ Command pipelining in cooperation with:
+ Jon Ribbens
+ Oaktree Internet Solutions Ltd.,
+ Internet House,
+ Canal Basin,
+ Coventry,
+ CV1 4LY, United Kingdom.
+
+ Connection caching in cooperation with:
+ Victor Duchovni
+ Morgan Stanley
+
SMTP(8)
</pre> </body> </html>
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 \
(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 $?)
.IP "\fB\fB%u\fR\fR"
When the input key is an address of the form user@domain,
\fB%u\fR is replaced by the (RFC 2254) quoted local part of the
-address. If no domain is specified, \fB%u\fR is replaced by the
-entire search string.
+address. Otherwise, \fB%u\fR is replaced by the entire
+search string.
.IP "\fB\fB%d\fR\fR"
When the input key is an address of the form user@domain,
\fB%d\fR is replaced by the (RFC 2254) quoted domain part of the
-address. When the input key has no domain qualifier, \fB%d\fR is
-replaced by the entire search string.
+address. Otherwise, \fB%d\fR is replaced by the entire
+search string.
.RE
.IP
The "domain" parameter described below limits the input
.IP "\fB\fB%s\fR\fR"
This is replaced by the value of the result attribute.
.IP "\fB%u\fR
-When the result attribute is an address of the form
-user@domain, \fB%u\fR is replaced local part of the address, if
-the result attribute is unqualified, \fB%u\fR is replaced by the
-entire attribute value.
+When the result attribute value is an address of the form
+user@domain, \fB%u\fR is replaced by the local part of the
+address. Otherwise, \fB%u\fR is replaced by the entire
+attribute value.
.IP "\fB\fB%d\fR\fR"
-When a result attribute is an address of the form user@domain,
-\fB%d\fR is replaced by the domain part of the attribute value.
-If an attribute value is unqualified \fB%d\fR is replaced by the
-entire attribute value.
+When a result attribute value is an address of the form
+user@domain, \fB%d\fR is replaced by the domain part of
+the attribute value. Otherwise, \fB%d\fR is replaced by
+the entire attribute value.
.RE
.IP
For example, using "result_filter = smtp:[%s]" allows one
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
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.
.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).
.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)
.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
.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.
.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)
.IP "\fBmime_boundary_length_limit (2048)\fR"
The maximal length of MIME multipart boundary strings.
.IP "\fBmime_nesting_limit (100)\fR"
-The maximal nesting level of multipart mail that the MIME processor
-will handle.
+The maximal recursion level that the MIME processor will handle.
.IP "\fBstrict_8bitmime (no)\fR"
Enable both strict_7bit_headers and strict_8bitmime_body.
.IP "\fBstrict_7bit_headers (no)\fR"
.IP "\fBmime_boundary_length_limit (2048)\fR"
The maximal length of MIME multipart boundary strings.
.IP "\fBmime_nesting_limit (100)\fR"
-The maximal nesting level of multipart mail that the MIME processor
-will handle.
+The maximal recursion level that the MIME processor will handle.
.IP "\fBqueue_file_attribute_count_limit (100)\fR"
The maximal number of (name=value) attributes that may be stored
in a Postfix queue file.
.fi
The external command attributes are given in the \fBmaster.cf\fR
file at the end of a service definition. The syntax is as follows:
+.IP "\fBdirectory=\fIpathname\fR (optional, default: \fB$queue_directory\fR)"
+Change to the named directory before executing the external command.
+Delivery is deferred in case of failure.
+.sp
+This feature is available as of Postfix 2.2.
.IP "\fBeol=string\fR (optional, default: \fB\en\fR)"
The output record delimiter. Typically one would use either
\fB\er\en\fR or \fB\en\fR. The usual C-style backslash escape
sequences are recognized: \fB\ea \eb \ef \en \er \et \ev
-\e\fIoctal\fR and \fB\e\e\fR.
+\e\fIddd\fR (up to three octal digits) and \fB\e\e\fR.
.IP "\fBflags=BDFORhqu.>\fR (optional)"
Optional message processing flags. By default, a message is
copied unchanged.
the left of the right-most \fB@\fR character) to lower case.
This is recommended for delivery via \fBUUCP\fR.
.IP \fB.\fR
-Prepend \fB.\fR to lines starting with "\fB.\fR". This is needed
+Prepend "\fB.\fR" to lines starting with "\fB.\fR". This is needed
by, for example, \fBBSMTP\fR software.
.IP \fB>\fR
-Prepend \fB>\fR to lines starting with "\fBFrom \fR". This is expected
+Prepend "\fB>\fR" to lines starting with "\fBFrom \fR". This is expected
by, for example, \fBUUCP\fR software.
.RE
.IP "\fBsize\fR=\fIsize_limit\fR (optional)"
In addition to the form ${\fIname\fR}, the forms $\fIname\fR and
$(\fIname\fR) are also recognized. Specify \fB$$\fR where a single
\fB$\fR is wanted.
-.PP
-Available in Postfix 2.2 and later:
-.IP "\fBdirectory=\fIpathname\fR (optional)"
-Change to the specified directory before executing the command.
-Failure causes mail delivery to be deferred.
.SH DIAGNOSTICS
.ad
.fi
--- /dev/null
+.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
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
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
.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
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
.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
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
#
# 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
s;\bsendmail_path\b;<a href="postconf.5.html#sendmail_path">$&</a>;g;
s;\bservice_throttle_time\b;<a href="postconf.5.html#service_throttle_time">$&</a>;g;
s;\bsetgid_group\b;<a href="postconf.5.html#setgid_group">$&</a>;g;
+
+ s;\bsession_cache_service\b;<a href="postconf.5.html#session_cache_service">$&</a>;g;
+ s;\bsession_cache_ttl_limit\b;<a href="postconf.5.html#session_cache_ttl_limit">$&</a>;g;
+
s;\bshow_user_unknown_table_name\b;<a href="postconf.5.html#show_user_unknown_table_name">$&</a>;g;
s;\bshowq_service_name\b;<a href="postconf.5.html#showq_service_name">$&</a>;g;
s;\bsmtp_always_send_ehlo\b;<a href="postconf.5.html#smtp_always_send_ehlo">$&</a>;g;
s;\bsmtp_bind_address\b;<a href="postconf.5.html#smtp_bind_address">$&</a>;g;
s;\bsmtp_connect_timeout\b;<a href="postconf.5.html#smtp_connect_timeout">$&</a>;g;
+
+ s;\bsmtp_connection_cache_reuse_limit\b;<a href="postconf.5.html#smtp_connection_cache_reuse_limit">$&</a>;g;
+ s;\bsmtp_connection_cache_time_limit\b;<a href="postconf.5.html#smtp_connection_cache_time_limit">$&</a>;g;
+ s;\bsmtp_connection_cache_domains\b;<a href="postconf.5.html#smtp_connection_cache_domains">$&</a>;g;
+
s;\bsmtp_data_done_timeout\b;<a href="postconf.5.html#smtp_data_done_timeout">$&</a>;g;
s;\bsmtp_data_init_timeout\b;<a href="postconf.5.html#smtp_data_init_timeout">$&</a>;g;
s;\bsmtp_data_xfer_timeout\b;<a href="postconf.5.html#smtp_data_xfer_timeout">$&</a>;g;
s/[<bB>]*proxymap[<\/bB>]*\(8\)/<a href="proxymap.8.html">$&<\/a>/g;
s/[<bB>]*reg[-<\/bB>]*\n*[ <bB>]*exp[<\/bBiI>]*_[<\/iIbB>]*table[<\/bB>]*\(5\)/<a href="regexp_table.5.html">$&<\/a>/g;
s/[<bB>]*relocated[<\/bB>]*\(5\)/<a href="relocated.5.html">$&<\/a>/g;
+ s/[<bB>]*scache[<\/bB>]*\(8\)/<a href="scache.8.html">$&<\/a>/g;
s/[<bB>]*trans[-<\/bB>]*\n*[ <bB>]*port[<\/bB>]*\(5\)/<a href="transport.5.html">$&<\/a>/g;
s/[<bB>]*verify[<\/bB>]*\(8\)/<a href="verify.8.html">$&<\/a>/g;
s/[<bB>]*virtual[<\/bB>]*\(5\)/<a href="virtual.5.html">$&<\/a>/g;
and reduces the number of open lookup tables by sharing one open
table among multiple processes. </p>
+<li> <p> The scache(8) server maintains the session cache for the
+Postfix smtp(8) client. When session caching is enabled for selected
+destinations, the smtp(8) client does not disconnect immediately
+after a mail transaction, but gives the connection to the session
+cache server. The smtp(8) client continues with some other mail
+delivery request. Meanwhile, the session cache server keeps the
+connection open for a limited amount of time. During that time,
+any smtp(8) process can ask the scache(8) server for that cached
+session and use it for mail delivery. </p>
+
+<table>
+
+<tr> <td> <td align="center" bgcolor="#f0f0ff"> <br> smtp(8) <br>
+ </td> <td> <tt> -> </tt> </td> <td> <td align="center"
+bgcolor="#f0f0ff"> <br> scache(8) <br> </td> <td> <tt> ->
+</tt> </td> <td> <td align="center" bgcolor="#f0f0ff"> <br> smtp(8)
+<br> </td>
+
+</table>
+
<li> <p> The showq(8) servers list the Postfix queue status. This
is the queue listing service that does the work for the mailq(1)
and postqueue(1) commands. </p>
# .IP "\fB\fB%u\fR\fR"
# When the input key is an address of the form user@domain,
# \fB%u\fR is replaced by the (RFC 2254) quoted local part of the
-# address. If no domain is specified, \fB%u\fR is replaced by the
-# entire search string.
+# address. Otherwise, \fB%u\fR is replaced by the entire
+# search string.
# .IP "\fB\fB%d\fR\fR"
# When the input key is an address of the form user@domain,
# \fB%d\fR is replaced by the (RFC 2254) quoted domain part of the
-# address. When the input key has no domain qualifier, \fB%d\fR is
-# replaced by the entire search string.
+# address. Otherwise, \fB%d\fR is replaced by the entire
+# search string.
# .RE
# .IP
# The "domain" parameter described below limits the input
# .IP "\fB\fB%s\fR\fR"
# This is replaced by the value of the result attribute.
# .IP "\fB%u\fR
-# When the result attribute is an address of the form
-# user@domain, \fB%u\fR is replaced local part of the address, if
-# the result attribute is unqualified, \fB%u\fR is replaced by the
-# entire attribute value.
+# When the result attribute value is an address of the form
+# user@domain, \fB%u\fR is replaced by the local part of the
+# address. Otherwise, \fB%u\fR is replaced by the entire
+# attribute value.
# .IP "\fB\fB%d\fR\fR"
-# When a result attribute is an address of the form user@domain,
-# \fB%d\fR is replaced by the domain part of the attribute value.
-# If an attribute value is unqualified \fB%d\fR is replaced by the
-# entire attribute value.
+# When a result attribute value is an address of the form
+# user@domain, \fB%d\fR is replaced by the domain part of
+# the attribute value. Otherwise, \fB%d\fR is replaced by
+# the entire attribute value.
# .RE
# .IP
# For example, using "result_filter = smtp:[%s]" allows one
%PARAM bounce_size_limit 50000
-<p>
-The maximal amount of original message text that is sent in a
-non-delivery notification. Specify a byte count.
-</p>
+<p> The maximal amount of original message text that is sent in a
+non-delivery notification. Specify a byte count. If you increase
+this limit, then you should increase the mime_nesting_limit value
+proportionally. </p>
%PARAM canonical_maps
by the amount specified in $debug_peer_level. </p>
<p> Specify domain names, network/netmask patterns, "/file/name"
-patterns or "type:table" lookup tables. The result from lookup
-tables is ignored. </p>
+patterns or "type:table" lookup tables. The right-hand side result
+from "type:table" lookups is ignored. </p>
<p> Pattern matching of domain names is controlled by the
parent_domain_matches_subdomains parameter. </p>
The default time unit is s (seconds).
</p>
-%PARAM lmtp_rset_timeout 120s
+%PARAM lmtp_rset_timeout 20s
-<p>
-The LMTP client time limit for sending the RSET command, and for
-receiving the server response. The LMTP client sends RSET in order
-to find out if a cached connection is still alive.
-</p>
+<p> The LMTP client time limit for sending the RSET command, and
+for receiving the server response. The LMTP client sends RSET in
+order to finish a recipient address probe, or to verify that a
+cached connection is still alive. </p>
<p>
Time units: s (seconds), m (minutes), h (hours), d (days), w (weeks).
smtp ... smtp -o smtp_bind_address=11.22.33.44
</pre>
+%PARAM smtp_connection_cache_time_limit 2s
+
+<p> When SMTP session caching is enabled, the amount of time that
+an unused SMTP client socket is kept open before it is closed. Do
+not specify larger values without permission from the remote sites.
+</p>
+
+%PARAM smtp_connection_cache_reuse_limit 10
+
+<p> When SMTP session caching is enabled, the number of times that
+an SMTP session is reused before it is closed.
+</p>
+
+%PARAM smtp_connection_cache_domains
+
+<p> The SMTP destinations for which SMTP connection caching is
+enabled. With SMTP connection caching, a connection is not closed
+immediately after completion of a mail transaction. Instead, the
+connection is kept open for up to $smtp_connection_cache_time_limit
+seconds. This allows connections to be reused for other deliveries,
+and can improve mail delivery performance. </p>
+
+<p> Specify a comma or white space separated list of destinations
+or pseudo-destinations:
+</p>
+
+<ul>
+
+<li> a domain name (the right-hand side of an email address),
+
+<li> a relay host (including optional [] and/or non-default TCP
+port), using the exact same spelling as in main.cf or in the
+transport map,
+
+<li> a /file/name with domains and/or relay hosts,
+
+<li> a "type:table" with domains and/or relay hosts on the left-hand
+side. The right-hand side result from "type:table" lookups is
+ignored.
+
+</ul>
+
+<p></p>
+
%PARAM smtp_connect_timeout 30s
<p>
%PARAM mime_nesting_limit 100
<p>
-The maximal nesting level of multipart mail that the MIME processor
-will handle. Postfix refuses mail that is nested deeper.
+The maximal recursion level that the MIME processor will handle.
+Postfix refuses mail that is nested deeper than the specified limit.
</p>
<p>
is a performance feature of the Postfix SMTP client.
</p>
-%PARAM smtp_rset_timeout 120s
+%PARAM smtp_rset_timeout 20s
<p> The SMTP client time limit for sending the RSET command, and
-for receiving the server response. </p>
+for receiving the server response. The SMTP client sends RSET in
+order to finish a recipient address probe, or to verify that a
+cached session is still usable. </p>
<p> This feature is available in Postfix 2.1 and later. </p>
remote domains. Available before Postfix version 2.0. With Postfix 2.1
and later, this is replaced by separate controls: virtual_alias_domains
and virtual_alias_maps. </p>
+
+%PARAM session_cache_service scache
+
+<p> The name of the scache(8) session cache service. This service
+maintains a limited pool of cached sessions. </p>
+
+%PARAM session_cache_ttl_limit 2s
+
+<p> The maximal time-to-live value that the session cache server
+allows. Requests that specify a larger TTL will be stored with the
+maximum allowed TTL. The purpose of this additional control is to
+protect the infrastructure against careless people. The cache TTL
+is already bounded by $max_idle. </p>
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
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
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
/* .IP "\fBmime_boundary_length_limit (2048)\fR"
/* The maximal length of MIME multipart boundary strings.
/* .IP "\fBmime_nesting_limit (100)\fR"
-/* The maximal nesting level of multipart mail that the MIME processor
-/* will handle.
+/* The maximal recursion level that the MIME processor will handle.
/* .IP "\fBstrict_8bitmime (no)\fR"
/* Enable both strict_7bit_headers and strict_8bitmime_body.
/* .IP "\fBstrict_7bit_headers (no)\fR"
/* .IP "\fBmime_boundary_length_limit (2048)\fR"
/* The maximal length of MIME multipart boundary strings.
/* .IP "\fBmime_nesting_limit (100)\fR"
-/* The maximal nesting level of multipart mail that the MIME processor
-/* will handle.
+/* The maximal recursion level that the MIME processor will handle.
/* .IP "\fBqueue_file_attribute_count_limit (100)\fR"
/* The maximal number of (name=value) attributes that may be stored
/* in a Postfix queue file.
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
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
/*
/* 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.
/* 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
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);
+}
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
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
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
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 \
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 \
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)
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
$(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(LIBS) $(SYSLIBS)
mv junk $@.o
-xtext: $(LIB)
+xtext: $(LIB) $(LIBS)
mv $@.o junk
$(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(LIBS) $(SYSLIBS)
mv junk $@.o
-anvil_clnt: $(LIB)
+anvil_clnt: $(LIB) $(LIBS)
mv $@.o junk
$(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(LIBS) $(SYSLIBS)
mv junk $@.o
+scache: scache.c $(LIB) $(LIBS)
+ $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(LIBS) $(SYSLIBS)
+
tests: tok822_test mime_test mime_nest mime_8bit mime_dom mime_trunc \
mime_cvt mime_cvt2 mime_cvt3 strip_addr_test tok822_limit_test \
- virtual8_test xtext_test
+ virtual8_test xtext_test scache_multi_test
tok822_test: tok822_parse tok822_parse.in tok822_parse.ref
./tok822_parse <tok822_parse.in >tok822_parse.tmp 2>&1
resolve_clnt.ref | diff - resolve_clnt.tmp
rm -f resolve_clnt.tmp
+scache_multi_test: scache scache_multi.in scache_multi.ref
+ ./scache <scache_multi.in >scache_multi.tmp
+ diff scache_multi.ref scache_multi.tmp
+ rm -f scache_multi.tmp
+
printfck: $(OBJS) $(PROG)
rm -rf printfck
mkdir printfck
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
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
#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;
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"
#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;
#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.
*/
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"
#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.
*/
#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
#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.
*/
* 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"
/* 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
--- /dev/null
+/*++
+/* NAME
+/* scache 3
+/* SUMMARY
+/* generic session cache API
+/* SYNOPSIS
+/* #include <scache.h>
+/* DESCRIPTION
+/* void scache_free(scache)
+/* SCACHE *scache;
+/*
+/* void scache_save_endp(scache, endp_ttl, endp_label, endp_prop, fd)
+/* SCACHE *scache;
+/* int endp_ttl;
+/* const char *endp_label;
+/* const char *endp_prop;
+/* int fd;
+/*
+/* int scache_find_endp(scache, endp_label, endp_prop)
+/* SCACHE *scache;
+/* const char *endp_label;
+/* VSTRING *endp_prop;
+/*
+/* void scache_save_dest(scache, dest_ttl, dest_label,
+/* dest_prop, endp_label)
+/* SCACHE *scache;
+/* int dest_ttl;
+/* const char *dest_label;
+/* const char *dest_prop;
+/* const char *endp_label;
+/*
+/* int scache_find_dest(dest_label, dest_prop, endp_prop)
+/* SCACHE *scache;
+/* const char *dest_label;
+/* VSTRING *dest_prop;
+/* VSTRING *endp_prop;
+/* DESCRIPTION
+/* This module implements a generic session cache interface.
+/* Specific cache types are described in scache_single(3),
+/* scache_clnt(3) and scache_multi(3). These documents also
+/* describe now to instantiate a specific session cache type.
+/*
+/* The code maintains two types of association: a) physical
+/* endpoint to file descriptor, and b) logical endpoint
+/* to physical endpoint. Physical endpoints are stored and
+/* look up under their low-level session details such as
+/* numerical addresses, while logical endpoints are stored
+/* and looked up by the domain name that humans use. One logical
+/* endpoint can refer to multiple physical endpoints, one
+/* physical endpoint may be referenced by multiple logical
+/* endpoints, and one physical endpoint may have multiple
+/* sessions.
+/*
+/* scache_free() destroys the specified session cache.
+/*
+/* scache_save_endp() stores an open session under the specified
+/* physical endpoint name.
+/*
+/* scache_find_endp() looks up a saved session under the
+/* specified physical endpoint name.
+/*
+/* scache_save_dest() associates the specified physical endpoint
+/* with the specified logical endpoint name.
+/*
+/* scache_find_dest() looks up a saved session under the
+/* specified physical endpoint name.
+/*
+/* Arguments:
+/* .IP endp_ttl
+/* How long the session should be cached. When information
+/* expires it is purged automatically.
+/* .IP endp_label
+/* The transport name and the physical endpoint name under
+/* which the session is stored and looked up.
+/*
+/* In the case of SMTP, the physical endpoint includes the numerical
+/* IP address, address family information, and the numerical TCP port.
+/* .IP endp_prop
+/* Application-specific data with endpoint attributes. It is up to
+/* the application to passivate (flatten) and re-activate this content
+/* upon storage and retrieval, respectively.
+/* .sp
+/* In the case of SMTP, the endpoint attributes specify the
+/* server hostname, IP address, numerical TCP port, as well
+/* as ESMTP features advertised by the server, and when information
+/* expires. All this in some application-specific format that is easy
+/* to unravel when re-activating a cached session.
+/* .IP dest_ttl
+/* How long the destination-to-endpoint binding should be
+/* cached. When information expires it is purged automatically.
+/* .IP dest_label
+/* The transport name and the logical destination under which the
+/* destination-to-endpoint binding is stored and looked up.
+/*
+/* In the case of SMTP, the logical destination is the DNS
+/* host or domain name with or without [], plus the numerical TCP port.
+/* .IP dest_prop
+/* Application-specific attributes that describe features of
+/* this logical to physical binding. It is up to the application
+/* to passivate (flatten) and re-activate this content.
+/* upon storage and retrieval, respectively
+/* .sp
+/* In case the of an SMTP logical destination to physical
+/* endpoint binding, the attributes specify the logical
+/* destination name, numerical port, whether the physical
+/* endpoint is best mx host with respect to a logical or
+/* fall-back destination, and when information expires.
+/* .IP fd
+/* File descriptor with session to be cached.
+/* DIAGNOSTICS
+/* scache_find_endp() and scache_find_dest() return -1 when
+/* the lookup fails, and a file descriptor upon success.
+/*
+/* Other diagnostics: fatal error: memory allocation problem;
+/* panic: internal consistency failure.
+/* SEE ALSO
+/* scache_single(3), single-session, in-memory cache
+/* scache_clnt(3), session cache client
+/* scache_multi(3), multi-session, in-memory cache
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <vstream.h>
+#include <vstring.h>
+#include <vstring_vstream.h>
+#include <argv.h>
+#include <events.h>
+
+/* Global library. */
+
+#include <scache.h>
+
+#ifdef TEST
+
+ /*
+ * Driver program for cache regression tests. Although all variants are
+ * relatively simple to verify by hand for single session storage, more
+ * sophisticated instrumentation is needed to demonstrate that the
+ * multi-sessin cache manager properly handles collisions in the time domain
+ * and in the various name space domains.
+ */
+static SCACHE *scache;
+static VSTRING *endp_prop;
+static VSTRING *dest_prop;
+static int verbose_level = 3;
+
+ /*
+ * Cache mode lookup table. We don't do the client-server variant because
+ * that drags in a ton of configuration junk; the client-server protocol is
+ * relatively easy to verify manually.
+ */
+struct cache_type {
+ char *mode;
+ SCACHE *(*create) (void);
+};
+
+static struct cache_type cache_types[] = {
+ "single", scache_single_create,
+ "multi", scache_multi_create,
+ 0,
+};
+
+#define STR(x) vstring_str(x)
+
+/* cache_type - select session cache type */
+
+static void cache_type(ARGV *argv)
+{
+ struct cache_type *cp;
+
+ if (argv->argc != 2) {
+ msg_error("usage: %s mode", argv->argv[0]);
+ return;
+ }
+ if (scache != 0)
+ scache_free(scache);
+ for (cp = cache_types; cp->mode != 0; cp++) {
+ if (strcmp(cp->mode, argv->argv[1]) == 0) {
+ scache = cp->create();
+ return;
+ }
+ }
+ msg_error("unknown cache type: %s", argv->argv[1]);
+}
+
+/* handle_events - handle events while time advances */
+
+static void handle_events(ARGV *argv)
+{
+ int delay;
+ time_t before;
+ time_t after;
+
+ if (argv->argc != 2 || (delay = atoi(argv->argv[1])) <= 0) {
+ msg_error("usage: %s time", argv->argv[0]);
+ return;
+ }
+ before = event_time();
+ event_drain(delay);
+ after = event_time();
+ if (after < before + delay)
+ sleep(before + delay - after);
+}
+
+/* save_endp - save endpoint->session binding */
+
+static void save_endp(ARGV *argv)
+{
+ int ttl;
+ int fd;
+
+ if (argv->argc != 5
+ || (ttl = atoi(argv->argv[1])) <= 0
+ || (fd = atoi(argv->argv[4])) <= 0) {
+ msg_error("usage: save_endp ttl endpoint endp_props fd");
+ return;
+ }
+ if (DUP2(0, fd) < 0)
+ msg_fatal("dup2(0, %d): %m", fd);
+ scache_save_endp(scache, ttl, argv->argv[2], argv->argv[3], fd);
+}
+
+/* find_endp - find endpoint->session binding */
+
+static void find_endp(ARGV *argv)
+{
+ int fd;
+
+ if (argv->argc != 2) {
+ msg_error("usage: find_endp endpoint");
+ return;
+ }
+ if ((fd = scache_find_endp(scache, argv->argv[1], endp_prop)) >= 0)
+ close(fd);
+}
+
+/* save_dest - save destination->endpoint binding */
+
+static void save_dest(ARGV *argv)
+{
+ int ttl;
+
+ if (argv->argc != 5 || (ttl = atoi(argv->argv[1])) <= 0) {
+ msg_error("usage: save_dest ttl destination dest_props endpoint");
+ return;
+ }
+ scache_save_dest(scache, ttl, argv->argv[2], argv->argv[3], argv->argv[4]);
+}
+
+/* find_dest - find destination->endpoint->session binding */
+
+static void find_dest(ARGV *argv)
+{
+ int fd;
+
+ if (argv->argc != 2) {
+ msg_error("usage: find_dest destination");
+ return;
+ }
+ if ((fd = scache_find_dest(scache, argv->argv[1], dest_prop, endp_prop)) >= 0)
+ close(fd);
+}
+
+/* verbose - adjust noise level during cache manipulation */
+
+static void verbose(ARGV *argv)
+{
+ int level;
+
+ if (argv->argc != 2 || (level = atoi(argv->argv[1])) < 0) {
+ msg_error("usage: verbose level");
+ return;
+ }
+ verbose_level = level;
+}
+
+ /*
+ * The command lookup table.
+ */
+struct action {
+ char *command;
+ void (*action) (ARGV *);
+ int flags;
+};
+
+#define FLAG_NEED_CACHE (1<<0)
+
+static void help(ARGV *);
+
+static struct action actions[] = {
+ "cache_type", cache_type, 0,
+ "save_endp", save_endp, FLAG_NEED_CACHE,
+ "find_endp", find_endp, FLAG_NEED_CACHE,
+ "save_dest", save_dest, FLAG_NEED_CACHE,
+ "find_dest", find_dest, FLAG_NEED_CACHE,
+ "sleep", handle_events, 0,
+ "verbose", verbose, 0,
+ "?", help, 0,
+ 0,
+};
+
+/* help - list commands */
+
+static void help(ARGV *argv)
+{
+ struct action *ap;
+
+ vstream_printf("commands:");
+ for (ap = actions; ap->command != 0; ap++)
+ vstream_printf(" %s", ap->command);
+ vstream_printf("\n");
+ vstream_fflush(VSTREAM_OUT);
+}
+
+/* get_buffer - prompt for input or log input */
+
+static int get_buffer(VSTRING *buf, VSTREAM *fp, int interactive)
+{
+ int status;
+
+ if (interactive) {
+ vstream_printf("> ");
+ vstream_fflush(VSTREAM_OUT);
+ }
+ if ((status = vstring_get_nonl(buf, fp)) != VSTREAM_EOF) {
+ if (!interactive) {
+ vstream_printf(">>> %s\n", STR(buf));
+ vstream_fflush(VSTREAM_OUT);
+ }
+ }
+ return (status);
+}
+
+/* at last, the main program */
+
+int main(int unused_argc, char **unused_argv)
+{
+ VSTRING *buf = vstring_alloc(1);
+ ARGV *argv;
+ struct action *ap;
+ int interactive = isatty(0);
+
+ endp_prop = vstring_alloc(1);
+ dest_prop = vstring_alloc(1);
+
+ vstream_fileno(VSTREAM_ERR) = 1;
+
+ while (get_buffer(buf, VSTREAM_IN, interactive) != VSTREAM_EOF) {
+ argv = argv_split(STR(buf), " \t\r\n");
+ if (argv->argc > 0 && argv->argv[0][0] != '#') {
+ msg_verbose = verbose_level;
+ for (ap = actions; ap->command != 0; ap++) {
+ if (strcmp(ap->command, argv->argv[0]) == 0) {
+ if ((ap->flags & FLAG_NEED_CACHE) != 0 && scache == 0)
+ msg_error("no session cache");
+ else
+ ap->action(argv);
+ break;
+ }
+ }
+ msg_verbose = 0;
+ if (ap->command == 0)
+ msg_error("bad command: %s", argv->argv[0]);
+ }
+ argv_free(argv);
+ }
+ scache_free(scache);
+ vstring_free(endp_prop);
+ vstring_free(dest_prop);
+ vstring_free(buf);
+ exit(0);
+}
+
+#endif
--- /dev/null
+#ifndef _SCACHE_H_INCLUDED_
+#define _SCACHE_H_INCLUDED_
+
+/*++
+/* NAME
+/* scache 3h
+/* SUMMARY
+/* generic session cache API
+/* SYNOPSIS
+/* #include <scache.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * Utility library.
+ */
+#include <vstring.h>
+
+typedef struct SCACHE SCACHE;
+
+ /*
+ * In order to cache a session, we specify:
+ *
+ * - TTL for this session.
+ *
+ * - File descriptor.
+ *
+ * - Transport name and physical endpoint. The transport name must be included,
+ * because fall-back destinations can be transport-dependent, and routing
+ * for a given destination may be context dependent.
+ *
+ * In the case of SMTP, the physical endpoint is the numerical IP address and
+ * TCP port.
+ *
+ * - Application-specific endpoint properties.
+ *
+ * In the case of SMTP, the properties specify the ESMTP features advertised by
+ * the server.
+ *
+ * Note: with message delivery transports that have only one endpoint per
+ * logical destination, the above is the only information that needs to be
+ * maintained in a connection cache.
+ */
+typedef void (*SCACHE_SAVE_ENDP_FN) (SCACHE *, int, const char *, const char *, int);
+typedef int (*SCACHE_FIND_ENDP_FN) (SCACHE *, const char *, VSTRING *);
+
+ /*
+ * The following information is stored in order to make a binding from
+ * logical destination to physical destination. One logical destination can
+ * have multiple physical destinations (and one physical destination can
+ * have multiple sessions).
+ *
+ * - TTL for this binding.
+ *
+ * - Transport name and logical destination.
+ *
+ * In the case of SMTP: the next-hop (NOT: fall-back) destination domain and
+ * destination network port. It is not useful to create a link for an
+ * address literal (but it is not harmful either: it just wastes a few
+ * bits). This information specifies the destination domain in [] if no MX
+ * lookup is done.
+ *
+ * - Application-specific properties.
+ *
+ * In case the of SMTP, the properties specify a) whether a physical endpoint
+ * is best mx host with respect to a logical or fall-back destination (this
+ * information is needed by the loop detection code in smtp_proto.c).
+ *
+ * - Transport name and physical endpoint (see above).
+ *
+ * Note 1: there is no need to store the binding's MX preference or equivalent
+ * with respect to the logical destination; a client should store only the
+ * first successful session for a given delivery request (otherwise the
+ * client would keep talking to a less preferred server after the cached
+ * connection for a more preferred server expires). After a failed delivery,
+ * the client should not attempt to cache follow-up sessions with less
+ * preferred endpoints under the same logical destination.
+ *
+ * Note 2: logical to physical bindings exist independently from cached
+ * sessions. The two types of information have independent TTLs; creation
+ * and destruction proceed independently. Thus, a logical to physical
+ * binding can refer to an endpoint for which all cached connections are
+ * occupied or expired.
+ */
+typedef void (*SCACHE_SAVE_DEST_FN) (SCACHE *, int, const char *, const char *, const char *);
+typedef int (*SCACHE_FIND_DEST_FN) (SCACHE *, const char *, VSTRING *, VSTRING *);
+
+ /*
+ * Generic session cache object. Actual session cache objects are derived
+ * types with some additional, cache dependent, private information.
+ */
+struct SCACHE {
+ SCACHE_SAVE_ENDP_FN save_endp;
+ SCACHE_FIND_ENDP_FN find_endp;
+ SCACHE_SAVE_DEST_FN save_dest;
+ SCACHE_FIND_DEST_FN find_dest;
+ void (*free) (struct SCACHE *);
+};
+
+extern SCACHE *scache_single_create(void);
+extern SCACHE *scache_clnt_create(const char *, int, int);
+extern SCACHE *scache_multi_create(void);
+
+#define scache_save_endp(scache, ttl, endp_label, endp_prop, fd) \
+ (scache)->save_endp((scache), (ttl), (endp_label), (endp_prop), (fd))
+#define scache_find_endp(scache, endp_label, endp_prop) \
+ (scache)->find_endp((scache), (endp_label), (endp_prop))
+#define scache_save_dest(scache, ttl, dest_label, dest_prop, endp_label) \
+ (scache)->save_dest((scache), (ttl), (dest_label), (dest_prop), (endp_label))
+#define scache_find_dest(scache, dest_label, dest_prop, endp_prop) \
+ (scache)->find_dest((scache), (dest_label), (dest_prop), (endp_prop))
+#define scache_free(scache) (scache)->free(scache)
+
+ /*
+ * Cache types.
+ */
+#define SCACHE_TYPE_SINGLE 1 /* single-instance cache */
+#define SCACHE_TYPE_CLIENT 2 /* session cache client */
+#define SCACHE_TYPE_MULTI 3 /* multi-instance cache */
+
+ /*
+ * Client-server protocol.
+ */
+#define SCACHE_REQ_FIND_ENDP "find_endp"
+#define SCACHE_REQ_SAVE_ENDP "save_endp"
+#define SCACHE_REQ_FIND_DEST "find_dest"
+#define SCACHE_REQ_SAVE_DEST "save_dest"
+
+ /*
+ * Session cache server status codes.
+ */
+#define SCACHE_STAT_OK 0 /* request completed successfully */
+#define SCACHE_STAT_BAD 1 /* malformed request */
+#define SCACHE_STAT_FAIL 2 /* request completed unsuccessfully */
+
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*--*/
+
+#endif
--- /dev/null
+/*++
+/* NAME
+/* scache_clnt 3
+/* SUMMARY
+/* session cache manager client
+/* SYNOPSIS
+/* #include <scache.h>
+/* DESCRIPTION
+/* SCACHE *scache_clnt_create(server, idle_limit, ttl_limit)
+/* const char *server;
+/* int idle_limit;
+/* int ttl_limit;
+/* DESCRIPTION
+/* This module implements the client-side protocol of the
+/* session cache service.
+/*
+/* scache_clnt_create() creates a session cache service client.
+/*
+/* Arguments:
+/* .IP server
+/* The session cache service name.
+/* .IP idle_limit
+/* Idle time after which the client disconnects.
+/* .IP ttl_limit
+/* Upper bound on the time that a connection is allowed to persist.
+/* .IP endp_ttl
+/* How long the session should be cached. When information
+/* expires it is purged automatically.
+/* DIAGNOSTICS
+/* Fatal error: memory allocation problem;
+/* panic: internal consistency failure.
+/* SEE ALSO
+/* scache(3), generic session cache API
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <errno.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+
+/*#define msg_verbose 1*/
+
+/* Global library. */
+
+#include <mail_proto.h>
+#include <mail_params.h>
+#include <clnt_stream.h>
+#include <scache.h>
+
+/* Application-specific. */
+
+ /*
+ * SCACHE_CLNT is a derived type from the SCACHE super-class.
+ */
+typedef struct {
+ SCACHE scache[1]; /* super-class */
+ CLNT_STREAM *clnt_stream; /* client endpoint */
+} SCACHE_CLNT;
+
+#define STR(x) vstring_str(x)
+
+/* scache_clnt_save_endp - save endpoint */
+
+static void scache_clnt_save_endp(SCACHE *scache, int endp_ttl,
+ const char *endp_label,
+ const char *endp_prop, int fd)
+{
+ SCACHE_CLNT *sp = (SCACHE_CLNT *) scache;
+ const char *myname = "scache_clnt_save_endp";
+ VSTREAM *stream;
+ int status;
+
+ if (msg_verbose)
+ msg_info("%s: endp=%s prop=%s fd=%d",
+ myname, endp_label, endp_prop, fd);
+
+ /*
+ * Sanity check.
+ */
+ if (endp_ttl <= 0)
+ msg_panic("%s: bad endp_ttl: %d", myname, endp_ttl);
+
+ /*
+ * Keep trying until we get a complete response. The session cache
+ * service is CPU bound and making the client asynchronous would just
+ * complicate the code.
+ */
+ for (;;) {
+ stream = clnt_stream_access(sp->clnt_stream);
+ errno = 0;
+ if (attr_print(stream, ATTR_FLAG_NONE,
+ ATTR_TYPE_STR, MAIL_ATTR_REQ, SCACHE_REQ_SAVE_ENDP,
+ ATTR_TYPE_NUM, MAIL_ATTR_TTL, endp_ttl,
+ ATTR_TYPE_STR, MAIL_ATTR_LABEL, endp_label,
+ ATTR_TYPE_STR, MAIL_ATTR_PROP, endp_prop,
+ ATTR_TYPE_END) != 0
+ || vstream_fflush(stream)
+ || LOCAL_SEND_FD(vstream_fileno(stream), fd) < 0
+ || attr_scan(stream, ATTR_FLAG_STRICT,
+ ATTR_TYPE_NUM, MAIL_ATTR_STATUS, &status,
+ ATTR_TYPE_END) != 1) {
+ if (msg_verbose || (errno != EPIPE && errno != ENOENT))
+ msg_warn("problem talking to service %s: %m",
+ VSTREAM_PATH(stream));
+ } else {
+ if (close(fd) < 0)
+ msg_warn("%s: close(%d): %m", myname, fd);
+ break;
+ }
+ sleep(1); /* XXX make configurable */
+ clnt_stream_recover(sp->clnt_stream);
+ }
+}
+
+/* scache_clnt_find_endp - look up cached session */
+
+static int scache_clnt_find_endp(SCACHE *scache, const char *endp_label,
+ VSTRING *endp_prop)
+{
+ SCACHE_CLNT *sp = (SCACHE_CLNT *) scache;
+ const char *myname = "scache_clnt_find_endp";
+ VSTREAM *stream;
+ int status;
+ int fd;
+
+ /*
+ * Keep trying until we get a complete response. The session cache
+ * service is CPU bound and making the client asynchronous would just
+ * complicate the code.
+ */
+ for (;;) {
+ stream = clnt_stream_access(sp->clnt_stream);
+ errno = 0;
+ if (attr_print(stream, ATTR_FLAG_NONE,
+ ATTR_TYPE_STR, MAIL_ATTR_REQ, SCACHE_REQ_FIND_ENDP,
+ ATTR_TYPE_STR, MAIL_ATTR_LABEL, endp_label,
+ ATTR_TYPE_END) != 0
+ || vstream_fflush(stream)
+ || attr_scan(stream, ATTR_FLAG_STRICT,
+ ATTR_TYPE_NUM, MAIL_ATTR_STATUS, &status,
+ ATTR_TYPE_STR, MAIL_ATTR_PROP, endp_prop,
+ ATTR_TYPE_END) != 2
+ || (status == 0
+ && (fd = LOCAL_RECV_FD(vstream_fileno(stream))) < 0)) {
+ if (msg_verbose || (errno != EPIPE && errno != ENOENT))
+ msg_warn("problem talking to service %s: %m",
+ VSTREAM_PATH(stream));
+ } else {
+ break;
+ }
+ sleep(1); /* XXX make configurable */
+ clnt_stream_recover(sp->clnt_stream);
+ }
+
+ if (status == 0) {
+ if (msg_verbose)
+ msg_info("%s: endp=%s prop=%s fd=%d",
+ myname, endp_label, STR(endp_prop), fd);
+ return (fd);
+ }
+ if (msg_verbose)
+ msg_info("%s: not found: %s", myname, endp_label);
+ return (-1);
+}
+
+/* scache_clnt_save_dest - create destination/endpoint association */
+
+static void scache_clnt_save_dest(SCACHE *scache, int dest_ttl,
+ const char *dest_label,
+ const char *dest_prop,
+ const char *endp_label)
+{
+ SCACHE_CLNT *sp = (SCACHE_CLNT *) scache;
+ const char *myname = "scache_clnt_save_dest";
+ VSTREAM *stream;
+ int status;
+
+ if (msg_verbose)
+ msg_info("%s: dest_label=%s dest_prop=%s endp_label=%s",
+ myname, dest_label, dest_prop, endp_label);
+
+ /*
+ * Sanity check.
+ */
+ if (dest_ttl <= 0)
+ msg_panic("%s: bad dest_ttl: %d", myname, dest_ttl);
+
+ /*
+ * Keep trying until we get a complete response. The session cache
+ * service is CPU bound and making the client asynchronous would just
+ * complicate the code.
+ */
+ for (;;) {
+ stream = clnt_stream_access(sp->clnt_stream);
+ errno = 0;
+ if (attr_print(stream, ATTR_FLAG_NONE,
+ ATTR_TYPE_STR, MAIL_ATTR_REQ, SCACHE_REQ_SAVE_DEST,
+ ATTR_TYPE_NUM, MAIL_ATTR_TTL, dest_ttl,
+ ATTR_TYPE_STR, MAIL_ATTR_LABEL, dest_label,
+ ATTR_TYPE_STR, MAIL_ATTR_PROP, dest_prop,
+ ATTR_TYPE_STR, MAIL_ATTR_LABEL, endp_label,
+ ATTR_TYPE_END) != 0
+ || vstream_fflush(stream)
+ || attr_scan(stream, ATTR_FLAG_STRICT,
+ ATTR_TYPE_NUM, MAIL_ATTR_STATUS, &status,
+ ATTR_TYPE_END) != 1) {
+ if (msg_verbose || (errno != EPIPE && errno != ENOENT))
+ msg_warn("problem talking to service %s: %m",
+ VSTREAM_PATH(stream));
+ } else {
+ break;
+ }
+ sleep(1); /* XXX make configurable */
+ clnt_stream_recover(sp->clnt_stream);
+ }
+}
+
+/* scache_clnt_find_dest - look up cached session */
+
+static int scache_clnt_find_dest(SCACHE *scache, const char *dest_label,
+ VSTRING *dest_prop,
+ VSTRING *endp_prop)
+{
+ SCACHE_CLNT *sp = (SCACHE_CLNT *) scache;
+ const char *myname = "scache_clnt_find_dest";
+ VSTREAM *stream;
+ int status;
+ int fd;
+
+ /*
+ * Keep trying until we get a complete response. The session cache
+ * service is CPU bound and making the client asynchronous would just
+ * complicate the code.
+ */
+ for (;;) {
+ stream = clnt_stream_access(sp->clnt_stream);
+ errno = 0;
+ if (attr_print(stream, ATTR_FLAG_NONE,
+ ATTR_TYPE_STR, MAIL_ATTR_REQ, SCACHE_REQ_FIND_DEST,
+ ATTR_TYPE_STR, MAIL_ATTR_LABEL, dest_label,
+ ATTR_TYPE_END) != 0
+ || vstream_fflush(stream)
+ || attr_scan(stream, ATTR_FLAG_STRICT,
+ ATTR_TYPE_NUM, MAIL_ATTR_STATUS, &status,
+ ATTR_TYPE_STR, MAIL_ATTR_PROP, dest_prop,
+ ATTR_TYPE_STR, MAIL_ATTR_PROP, endp_prop,
+ ATTR_TYPE_END) != 3
+ || (status == 0
+ && (fd = LOCAL_RECV_FD(vstream_fileno(stream))) < 0)) {
+ if (msg_verbose || (errno != EPIPE && errno != ENOENT))
+ msg_warn("problem talking to service %s: %m",
+ VSTREAM_PATH(stream));
+ } else {
+ break;
+ }
+ sleep(1); /* XXX make configurable */
+ clnt_stream_recover(sp->clnt_stream);
+ }
+
+ if (status == 0) {
+ if (msg_verbose)
+ msg_info("%s: dest=%s dest_prop=%s endp_prop=%s fd=%d",
+ myname, dest_label, STR(dest_prop), STR(endp_prop), fd);
+ return (fd);
+ }
+ if (msg_verbose)
+ msg_info("%s: not found: %s", myname, dest_label);
+
+ return (-1);
+}
+
+/* scache_clnt_free - destroy cache */
+
+static void scache_clnt_free(SCACHE *scache)
+{
+ SCACHE_CLNT *sp = (SCACHE_CLNT *) scache;
+
+ clnt_stream_free(sp->clnt_stream);
+ myfree((char *) sp);
+}
+
+/* scache_clnt_create - initialize */
+
+SCACHE *scache_clnt_create(const char *server, int idle_limit, int ttl_limit)
+{
+ SCACHE_CLNT *sp = (SCACHE_CLNT *) mymalloc(sizeof(*sp));
+
+ sp->scache->save_endp = scache_clnt_save_endp;
+ sp->scache->find_endp = scache_clnt_find_endp;
+ sp->scache->save_dest = scache_clnt_save_dest;
+ sp->scache->find_dest = scache_clnt_find_dest;
+ sp->scache->free = scache_clnt_free;
+
+ sp->clnt_stream = clnt_stream_create(MAIL_CLASS_PRIVATE, server,
+ idle_limit, ttl_limit);
+
+ return (sp->scache);
+}
--- /dev/null
+/*++
+/* NAME
+/* scache_multi 3
+/* SUMMARY
+/* multi-session cache
+/* SYNOPSIS
+/* #include <scache.h>
+/* DESCRIPTION
+/* SCACHE *scache_multi_create()
+/* DESCRIPTION
+/* This module implements an in-memory, multi-session cache.
+/*
+/* scache_multi_create() instantiates a session cache that
+/* stores multiple sessions.
+/* DIAGNOSTICS
+/* Fatal error: memory allocation problem;
+/* panic: internal consistency failure.
+/* SEE ALSO
+/* scache(3), generic session cache API
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <unistd.h>
+#include <stddef.h> /* offsetof() */
+
+/* Utility library. */
+
+#include <msg.h>
+#include <ring.h>
+#include <htable.h>
+#include <vstring.h>
+#include <mymalloc.h>
+#include <events.h>
+
+/*#define msg_verbose 1*/
+
+/* Global library. */
+
+#include <scache.h>
+
+/* Application-specific. */
+
+ /*
+ * SCACHE_MULTI is a derived type from the SCACHE super-class.
+ *
+ * Each destination has an entry in the destination hash table, and each
+ * destination->endpoint binding is kept in a circular list under its
+ * destination hash table entry.
+ *
+ * Likewise, each endpoint has an entry in the endpoint hash table, and each
+ * endpoint->session binding is kept in a circular list under its hash table
+ * entry.
+ *
+ * We do not attempt to limit the number of destination or endpoint entries,
+ * nor do we attempt to limit the number of sessions. Doing so would require
+ * a write-through cache. Currently, the CTABLE cache module supports only
+ * read-through caching.
+ *
+ * This is no problem because the number of cached destinations is limited by
+ * design. Sites that specify a wild-card domain pattern, and thus cache
+ * every session in recent history, may be in for a surprise.
+ */
+typedef struct {
+ SCACHE scache[1]; /* super-class */
+ HTABLE *dest_cache; /* destination->endpoint bindings */
+ HTABLE *endp_cache; /* endpoint->session bindings */
+} SCACHE_MULTI;
+
+ /*
+ * Storage for a destination or endpoint list head. The list head has
+ * references to its own hash table entry, so that we can remove a list when
+ * it becomes empty. List items are stored in a circular list under the list
+ * head.
+ */
+typedef struct {
+ RING ring[1]; /* circular list linkage */
+ HTABLE *parent_table; /* parent info */
+ char *parent_key; /* parent info */
+} SCACHE_MULTI_HEAD;
+
+#define RING_TO_MULTI_HEAD(p) RING_TO_APPL((p), SCACHE_MULTI_HEAD, ring)
+
+ /*
+ * Storage for a destination->endpoint binding. This is an element in a
+ * circular list, whose list head specifies the destination.
+ */
+typedef struct {
+ RING ring[1]; /* circular list linkage */
+ char *endp_label; /* endpoint name */
+ char *dest_prop; /* binding properties */
+} SCACHE_MULTI_DEST;
+
+#define RING_TO_MULTI_DEST(p) RING_TO_APPL((p), SCACHE_MULTI_DEST, ring)
+
+static void scache_multi_expire_dest(int, char *);
+
+ /*
+ * Storage for an endpoint->session binding. This is an element in a
+ * circular list, whose list head specifies the endpoint.
+ */
+typedef struct {
+ RING ring[1]; /* circular list linkage */
+ int fd; /* cached session */
+ char *endp_prop; /* binding properties */
+} SCACHE_MULTI_ENDP;
+
+#define RING_TO_MULTI_ENDP(p) RING_TO_APPL((p), SCACHE_MULTI_ENDP, ring)
+
+static void scache_multi_expire_endp(int, char *);
+
+ /*
+ * When deleting a circular list element, are we deleting the entire
+ * circular list, or are we removing a single list element. We need this
+ * distinction to avoid a re-entrancy problem between htable_delete() and
+ * htable_free().
+ */
+#define BOTTOM_UP 1 /* one item */
+#define TOP_DOWN 2 /* whole list */
+
+/* scache_multi_drop_endp - destroy endpoint->session binding */
+
+static void scache_multi_drop_endp(SCACHE_MULTI_ENDP *endp, int direction)
+{
+ const char *myname = "scache_multi_drop_endp";
+ SCACHE_MULTI_HEAD *head;
+
+ if (msg_verbose)
+ msg_info("%s: endp_prop=%s fd=%d", myname,
+ endp->endp_prop, endp->fd);
+
+ /*
+ * Stop the timer.
+ */
+ event_cancel_timer(scache_multi_expire_endp, (char *) endp);
+
+ /*
+ * In bottom-up mode, remove the list head from the endpoint hash when
+ * the list becomes empty. Otherwise, remove the endpoint->session
+ * binding from the list.
+ */
+ if (direction == BOTTOM_UP
+ && ring_pred(endp->ring) == ring_succ(endp->ring)) {
+ head = RING_TO_MULTI_HEAD(ring_pred(endp->ring));
+ htable_delete(head->parent_table, head->parent_key, myfree);
+ } else
+ ring_detach(endp->ring);
+
+ /*
+ * Destroy the endpoint->session binding.
+ */
+ if (endp->fd >= 0 && close(endp->fd) != 0)
+ msg_warn("%s: close(%d): %m", myname, endp->fd);
+ myfree(endp->endp_prop);
+
+ myfree((char *) endp);
+}
+
+/* scache_multi_expire_endp - event timer call-back */
+
+static void scache_multi_expire_endp(int unused_event, char *context)
+{
+ SCACHE_MULTI_ENDP *endp = (SCACHE_MULTI_ENDP *) context;
+
+ scache_multi_drop_endp(endp, BOTTOM_UP);
+}
+
+/* scache_multi_free_endp - hash table destructor call-back */
+
+static void scache_multi_free_endp(char *ptr)
+{
+ SCACHE_MULTI_HEAD *head = (SCACHE_MULTI_HEAD *) ptr;
+ SCACHE_MULTI_ENDP *endp;
+ RING *ring;
+
+ /*
+ * Delete each endpoint->session binding in the list, then delete the
+ * list head. Note: this changes the list, so we must iterate carefully.
+ */
+ while ((ring = ring_succ(head->ring)) != head->ring) {
+ endp = RING_TO_MULTI_ENDP(ring);
+ scache_multi_drop_endp(endp, TOP_DOWN);
+ }
+ myfree((char *) head);
+}
+
+/* scache_multi_save_endp - save endpoint->session binding */
+
+static void scache_multi_save_endp(SCACHE *scache, int ttl,
+ const char *endp_label,
+ const char *endp_prop, int fd)
+{
+ const char *myname = "scache_multi_save_endp";
+ SCACHE_MULTI *sp = (SCACHE_MULTI *) scache;
+ SCACHE_MULTI_HEAD *head;
+ SCACHE_MULTI_ENDP *endp;
+
+ if (ttl < 0)
+ msg_panic("%s: bad ttl: %d", myname, ttl);
+
+ /*
+ * Look up or instantiate the list head with the endpoint name.
+ */
+ if ((head = (SCACHE_MULTI_HEAD *)
+ htable_find(sp->endp_cache, endp_label)) == 0) {
+ head = (SCACHE_MULTI_HEAD *) mymalloc(sizeof(*head));
+ ring_init(head->ring);
+ head->parent_table = sp->endp_cache;
+ head->parent_key =
+ htable_enter(sp->endp_cache, endp_label, (char *) head)->key;
+ }
+
+ /*
+ * Add the endpoint->session binding to the list. There can never be a
+ * duplicate, because each session must have a different file descriptor.
+ */
+ endp = (SCACHE_MULTI_ENDP *) mymalloc(sizeof(*endp));
+ endp->fd = fd;
+ endp->endp_prop = mystrdup(endp_prop);
+ ring_prepend(head->ring, endp->ring);
+
+ /*
+ * Make sure this binding will go away eventually.
+ */
+ event_request_timer(scache_multi_expire_endp, (char *) endp, ttl);
+
+ if (msg_verbose)
+ msg_info("%s: endp_label=%s -> endp_prop=%s fd=%d",
+ myname, endp_label, endp_prop, fd);
+}
+
+/* scache_multi_find_endp - look up session for named endpoint */
+
+static int scache_multi_find_endp(SCACHE *scache, const char *endp_label,
+ VSTRING *endp_prop)
+{
+ const char *myname = "scache_multi_find_endp";
+ SCACHE_MULTI *sp = (SCACHE_MULTI *) scache;
+ SCACHE_MULTI_HEAD *head;
+ SCACHE_MULTI_ENDP *endp;
+ RING *ring;
+ int fd;
+
+ /*
+ * Look up the list head with the endpoint name.
+ */
+ if ((head = (SCACHE_MULTI_HEAD *)
+ htable_find(sp->endp_cache, endp_label)) == 0) {
+ if (msg_verbose)
+ msg_info("%s: no endpoint cache: endp_label=%s",
+ myname, endp_label);
+ return (-1);
+ }
+
+ /*
+ * Use the first available session. Remove the session from the cache
+ * because we're giving it to someone else.
+ */
+ if ((ring = ring_succ(head->ring)) != head->ring) {
+ endp = RING_TO_MULTI_ENDP(ring);
+ fd = endp->fd;
+ endp->fd = -1;
+ vstring_strcpy(endp_prop, endp->endp_prop);
+ if (msg_verbose)
+ msg_info("%s: found: endp_label=%s -> endp_prop=%s fd=%d",
+ myname, endp_label, endp->endp_prop, fd);
+ scache_multi_drop_endp(endp, BOTTOM_UP);
+ return (fd);
+ }
+ if (msg_verbose)
+ msg_info("%s: not found: endp_label=%s", myname, endp_label);
+ return (-1);
+}
+
+/* scache_multi_drop_dest - delete destination->endpoint binding */
+
+static void scache_multi_drop_dest(SCACHE_MULTI_DEST *dest, int direction)
+{
+ const char *myname = "scache_multi_drop_dest";
+ SCACHE_MULTI_HEAD *head;
+
+ if (msg_verbose)
+ msg_info("%s: dest_prop=%s endp_label=%s",
+ myname, dest->dest_prop, dest->endp_label);
+
+ /*
+ * Stop the timer.
+ */
+ event_cancel_timer(scache_multi_expire_dest, (char *) dest);
+
+ /*
+ * In bottom-up mode, remove the list head from the destination hash when
+ * the list becomes empty. Otherwise, remove the destination->endpoint
+ * binding from the list.
+ */
+ if (direction == BOTTOM_UP
+ && ring_pred(dest->ring) == ring_succ(dest->ring)) {
+ head = RING_TO_MULTI_HEAD(ring_pred(dest->ring));
+ htable_delete(head->parent_table, head->parent_key, myfree);
+ } else
+ ring_detach(dest->ring);
+
+ /*
+ * Destroy the destination->endpoint binding.
+ */
+ myfree(dest->dest_prop);
+ myfree(dest->endp_label);
+
+ myfree((char *) dest);
+}
+
+/* scache_multi_expire_dest - event timer call-back */
+
+static void scache_multi_expire_dest(int unused_event, char *context)
+{
+ SCACHE_MULTI_DEST *dest = (SCACHE_MULTI_DEST *) context;
+
+ scache_multi_drop_dest(dest, BOTTOM_UP);
+}
+
+/* scache_multi_free_dest - hash table destructor call-back */
+
+static void scache_multi_free_dest(char *ptr)
+{
+ SCACHE_MULTI_HEAD *head = (SCACHE_MULTI_HEAD *) ptr;
+ SCACHE_MULTI_DEST *dest;
+ RING *ring;
+
+ /*
+ * Delete each destination->endpoint binding in the list, then delete the
+ * list head. Note: this changes the list, so we must iterate carefully.
+ */
+ while ((ring = ring_succ(head->ring)) != head->ring) {
+ dest = RING_TO_MULTI_DEST(ring);
+ scache_multi_drop_dest(dest, TOP_DOWN);
+ }
+ myfree((char *) head);
+}
+
+/* scache_multi_save_dest - save destination->endpoint binding */
+
+static void scache_multi_save_dest(SCACHE *scache, int ttl,
+ const char *dest_label,
+ const char *dest_prop,
+ const char *endp_label)
+{
+ const char *myname = "scache_multi_save_dest";
+ SCACHE_MULTI *sp = (SCACHE_MULTI *) scache;
+ SCACHE_MULTI_HEAD *head;
+ SCACHE_MULTI_DEST *dest;
+ RING *ring;
+ int refresh = 0;
+
+ if (ttl < 0)
+ msg_panic("%s: bad ttl: %d", myname, ttl);
+
+ /*
+ * Look up or instantiate the list head with the destination name.
+ */
+ if ((head = (SCACHE_MULTI_HEAD *)
+ htable_find(sp->dest_cache, dest_label)) == 0) {
+ head = (SCACHE_MULTI_HEAD *) mymalloc(sizeof(*head));
+ ring_init(head->ring);
+ head->parent_table = sp->dest_cache;
+ head->parent_key =
+ htable_enter(sp->dest_cache, dest_label, (char *) head)->key;
+ }
+
+ /*
+ * Look up or instantiate the destination->endpoint binding. Update the
+ * expiration time if this destination->endpoint binding already exists.
+ */
+ RING_FOREACH(ring, head->ring) {
+ dest = RING_TO_MULTI_DEST(ring);
+ if (strcmp(dest->endp_label, endp_label) == 0
+ && strcmp(dest->dest_prop, dest_prop) == 0) {
+ refresh = 1;
+ break;
+ }
+ }
+ if (refresh == 0) {
+ dest = (SCACHE_MULTI_DEST *) mymalloc(sizeof(*dest));
+ dest->endp_label = mystrdup(endp_label);
+ dest->dest_prop = mystrdup(dest_prop);
+ ring_prepend(head->ring, dest->ring);
+ }
+
+ /*
+ * Make sure this binding will go away eventually.
+ */
+ event_request_timer(scache_multi_expire_dest, (char *) dest, ttl);
+
+ if (msg_verbose)
+ msg_info("%s: dest_label=%s -> dest_prop=%s endp_label=%s%s",
+ myname, dest_label, dest_prop, endp_label,
+ refresh ? " (refreshed)" : "");
+}
+
+/* scache_multi_find_dest - look up session for named destination */
+
+static int scache_multi_find_dest(SCACHE *scache, const char *dest_label,
+ VSTRING *dest_prop,
+ VSTRING *endp_prop)
+{
+ const char *myname = "scache_multi_find_dest";
+ SCACHE_MULTI *sp = (SCACHE_MULTI *) scache;
+ SCACHE_MULTI_HEAD *head;
+ SCACHE_MULTI_DEST *dest;
+ RING *ring;
+ int fd;
+
+ /*
+ * Look up the list head with the destination name.
+ */
+ if ((head = (SCACHE_MULTI_HEAD *)
+ htable_find(sp->dest_cache, dest_label)) == 0) {
+ if (msg_verbose)
+ msg_info("%s: no destination cache: dest_label=%s",
+ myname, dest_label);
+ return (-1);
+ }
+
+ /*
+ * Search endpoints for the first available session.
+ */
+ RING_FOREACH(ring, head->ring) {
+ dest = RING_TO_MULTI_DEST(ring);
+ fd = scache_multi_find_endp(scache, dest->endp_label, endp_prop);
+ if (fd >= 0) {
+ vstring_strcpy(dest_prop, dest->dest_prop);
+ return (fd);
+ }
+ }
+ if (msg_verbose)
+ msg_info("%s: not found: dest_label=%s", myname, dest_label);
+ return (-1);
+}
+
+/* scache_multi_free - destroy single-element cache object */
+
+static void scache_multi_free(SCACHE *scache)
+{
+ SCACHE_MULTI *sp = (SCACHE_MULTI *) scache;
+
+ htable_free(sp->dest_cache, scache_multi_free_dest);
+ htable_free(sp->endp_cache, scache_multi_free_endp);
+
+ myfree((char *) sp);
+}
+
+/* scache_multi_create - initialize */
+
+SCACHE *scache_multi_create(void)
+{
+ SCACHE_MULTI *sp = (SCACHE_MULTI *) mymalloc(sizeof(*sp));
+
+ sp->scache->save_endp = scache_multi_save_endp;
+ sp->scache->find_endp = scache_multi_find_endp;
+ sp->scache->save_dest = scache_multi_save_dest;
+ sp->scache->find_dest = scache_multi_find_dest;
+ sp->scache->free = scache_multi_free;
+
+ sp->dest_cache = htable_create(1);
+ sp->endp_cache = htable_create(1);
+
+ return (sp->scache);
+}
--- /dev/null
+# 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.
+
--- /dev/null
+>>> # 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
--- /dev/null
+/*++
+/* NAME
+/* scache_single 3
+/* SUMMARY
+/* single-item session cache
+/* SYNOPSIS
+/* #include <scache.h>
+/* DESCRIPTION
+/* SCACHE *scache_single_create()
+/* DESCRIPTION
+/* This module implements an in-memory, single-session cache.
+/*
+/* scache_single_create() creates a session cache instance
+/* that stores a single session.
+/* DIAGNOSTICS
+/* Fatal error: memory allocation problem;
+/* panic: internal consistency failure.
+/* SEE ALSO
+/* scache(3), generic session cache API
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <unistd.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <vstring.h>
+#include <mymalloc.h>
+#include <events.h>
+
+/*#define msg_verbose 1*/
+
+/* Global library. */
+
+#include <scache.h>
+
+/* Application-specific. */
+
+ /*
+ * Data structure for one saved connection. It is left up to the application
+ * to serialize attributes upon passivation, and to de-serialize them upon
+ * re-activation.
+ */
+typedef struct {
+ VSTRING *endp_label; /* physical endpoint name */
+ VSTRING *endp_prop; /* endpoint properties, serialized */
+ int fd; /* the session */
+} SCACHE_SINGLE_ENDP;
+
+ /*
+ * Data structure for a logical name to physical endpoint binding. It is
+ * left up to the application to serialize attributes upon passivation, and
+ * to de-serialize then upon re-activation.
+ */
+typedef struct {
+ VSTRING *dest_label; /* logical destination name */
+ VSTRING *dest_prop; /* binding properties, serialized */
+ VSTRING *endp_label; /* physical endpoint name */
+} SCACHE_SINGLE_DEST;
+
+ /*
+ * SCACHE_SINGLE is a derived type from the SCACHE super-class.
+ */
+typedef struct {
+ SCACHE scache[1]; /* super-class */
+ SCACHE_SINGLE_ENDP endp; /* one cached session */
+ SCACHE_SINGLE_DEST dest; /* one cached binding */
+} SCACHE_SINGLE;
+
+static void scache_single_expire_endp(int, char *);
+static void scache_single_expire_dest(int, char *);
+
+#define SCACHE_SINGLE_ENDP_BUSY(sp) (VSTRING_LEN(sp->endp.endp_label) > 0)
+#define SCACHE_SINGLE_DEST_BUSY(sp) (VSTRING_LEN(sp->dest.dest_label) > 0)
+
+#define STR(x) vstring_str(x)
+
+/* scache_single_free_endp - discard endpoint */
+
+static void scache_single_free_endp(SCACHE_SINGLE *sp)
+{
+ const char *myname = "scache_single_free_endp";
+
+ if (msg_verbose)
+ msg_info("%s: %s", myname, STR(sp->endp.endp_label));
+
+ event_cancel_timer(scache_single_expire_endp, (char *) sp);
+ if (sp->endp.fd >= 0 && close(sp->endp.fd) < 0)
+ msg_warn("close session endpoint %s: %m", STR(sp->endp.endp_label));
+ VSTRING_RESET(sp->endp.endp_label);
+ VSTRING_TERMINATE(sp->endp.endp_label);
+ VSTRING_RESET(sp->endp.endp_prop);
+ VSTRING_TERMINATE(sp->endp.endp_prop);
+ sp->endp.fd = -1;
+}
+
+/* scache_single_expire_endp - discard expired session */
+
+static void scache_single_expire_endp(int unused_event, char *context)
+{
+ SCACHE_SINGLE *sp = (SCACHE_SINGLE *) context;
+
+ scache_single_free_endp(sp);
+}
+
+/* scache_single_save_endp - save endpoint */
+
+static void scache_single_save_endp(SCACHE *scache, int endp_ttl,
+ const char *endp_label,
+ const char *endp_prop, int fd)
+{
+ SCACHE_SINGLE *sp = (SCACHE_SINGLE *) scache;
+ const char *myname = "scache_single_save_endp";
+
+ if (endp_ttl <= 0)
+ msg_panic("%s: bad endp_ttl: %d", myname, endp_ttl);
+
+ if (SCACHE_SINGLE_ENDP_BUSY(sp))
+ scache_single_free_endp(sp); /* dump the cached fd */
+
+ vstring_strcpy(sp->endp.endp_label, endp_label);
+ vstring_strcpy(sp->endp.endp_prop, endp_prop);
+ sp->endp.fd = fd;
+ event_request_timer(scache_single_expire_endp, (char *) sp, endp_ttl);
+
+ if (msg_verbose)
+ msg_info("%s: %s fd=%d", myname, endp_label, fd);
+}
+
+/* scache_single_find_endp - look up cached session */
+
+static int scache_single_find_endp(SCACHE *scache, const char *endp_label,
+ VSTRING *endp_prop)
+{
+ SCACHE_SINGLE *sp = (SCACHE_SINGLE *) scache;
+ const char *myname = "scache_single_find_endp";
+ int fd;
+
+ if (!SCACHE_SINGLE_ENDP_BUSY(sp)) {
+ if (msg_verbose)
+ msg_info("%s: no endpoint cache: %s", myname, endp_label);
+ return (-1);
+ }
+ if (strcmp(STR(sp->endp.endp_label), endp_label) == 0) {
+ vstring_strcpy(endp_prop, STR(sp->endp.endp_prop));
+ fd = sp->endp.fd;
+ sp->endp.fd = -1;
+ scache_single_free_endp(sp);
+ if (msg_verbose)
+ msg_info("%s: found: %s fd=%d", myname, endp_label, fd);
+ return (fd);
+ }
+ if (msg_verbose)
+ msg_info("%s: not found: %s", myname, endp_label);
+ return (-1);
+}
+
+/* scache_single_free_dest - discard destination/endpoint association */
+
+static void scache_single_free_dest(SCACHE_SINGLE *sp)
+{
+ const char *myname = "scache_single_free_dest";
+
+ if (msg_verbose)
+ msg_info("%s: %s -> %s", myname, STR(sp->dest.dest_label),
+ STR(sp->dest.endp_label));
+
+ event_cancel_timer(scache_single_expire_dest, (char *) sp);
+ VSTRING_RESET(sp->dest.dest_label);
+ VSTRING_TERMINATE(sp->dest.dest_label);
+ VSTRING_RESET(sp->dest.dest_prop);
+ VSTRING_TERMINATE(sp->dest.dest_prop);
+ VSTRING_RESET(sp->dest.endp_label);
+ VSTRING_TERMINATE(sp->dest.endp_label);
+}
+
+/* scache_single_expire_dest - discard expired destination/endpoint binding */
+
+static void scache_single_expire_dest(int unused_event, char *context)
+{
+ SCACHE_SINGLE *sp = (SCACHE_SINGLE *) context;
+
+ scache_single_free_dest(sp);
+}
+
+/* scache_single_save_dest - create destination/endpoint association */
+
+static void scache_single_save_dest(SCACHE *scache, int dest_ttl,
+ const char *dest_label,
+ const char *dest_prop,
+ const char *endp_label)
+{
+ SCACHE_SINGLE *sp = (SCACHE_SINGLE *) scache;
+ const char *myname = "scache_single_save_dest";
+ int refresh;
+
+ if (dest_ttl <= 0)
+ msg_panic("%s: bad dest_ttl: %d", myname, dest_ttl);
+
+ /*
+ * Optimize: reset timer only, if nothing has changed.
+ */
+ refresh =
+ (SCACHE_SINGLE_DEST_BUSY(sp)
+ && strcmp(STR(sp->dest.dest_label), dest_label) == 0
+ && strcmp(STR(sp->dest.dest_prop), dest_prop) == 0
+ && strcmp(STR(sp->dest.endp_label), endp_label) == 0);
+
+ if (refresh == 0) {
+ vstring_strcpy(sp->dest.dest_label, dest_label);
+ vstring_strcpy(sp->dest.dest_prop, dest_prop);
+ vstring_strcpy(sp->dest.endp_label, endp_label);
+ }
+ event_request_timer(scache_single_expire_dest, (char *) sp, dest_ttl);
+
+ if (msg_verbose)
+ msg_info("%s: %s -> %s%s", myname, dest_label, endp_label,
+ refresh ? " (refreshed)" : "");
+}
+
+/* scache_single_find_dest - look up cached session */
+
+static int scache_single_find_dest(SCACHE *scache, const char *dest_label,
+ VSTRING *dest_prop, VSTRING *endp_prop)
+{
+ SCACHE_SINGLE *sp = (SCACHE_SINGLE *) scache;
+ const char *myname = "scache_single_find_dest";
+ int fd;
+
+ if (!SCACHE_SINGLE_DEST_BUSY(sp)) {
+ if (msg_verbose)
+ msg_info("%s: no destination cache: %s", myname, dest_label);
+ return (-1);
+ }
+ if (strcmp(STR(sp->dest.dest_label), dest_label) == 0) {
+ if (msg_verbose)
+ msg_info("%s: found: %s", myname, dest_label);
+ if ((fd = scache_single_find_endp(scache, STR(sp->dest.endp_label), endp_prop)) >= 0) {
+ vstring_strcpy(dest_prop, STR(sp->dest.dest_prop));
+ return (fd);
+ }
+ }
+ if (msg_verbose)
+ msg_info("%s: not found: %s", myname, dest_label);
+ return (-1);
+}
+
+/* scache_single_free - destroy single-element cache object */
+
+static void scache_single_free(SCACHE *scache)
+{
+ SCACHE_SINGLE *sp = (SCACHE_SINGLE *) scache;
+
+ vstring_free(sp->endp.endp_label);
+ vstring_free(sp->endp.endp_prop);
+ if (sp->endp.fd >= 0)
+ close(sp->endp.fd);
+
+ vstring_free(sp->dest.dest_label);
+ vstring_free(sp->dest.dest_prop);
+ vstring_free(sp->dest.endp_label);
+
+ myfree((char *) sp);
+}
+
+/* scache_single_create - initialize */
+
+SCACHE *scache_single_create(void)
+{
+ SCACHE_SINGLE *sp = (SCACHE_SINGLE *) mymalloc(sizeof(*sp));
+
+ sp->scache->save_endp = scache_single_save_endp;
+ sp->scache->find_endp = scache_single_find_endp;
+ sp->scache->save_dest = scache_single_save_dest;
+ sp->scache->find_dest = scache_single_find_dest;
+ sp->scache->free = scache_single_free;
+
+ sp->endp.endp_label = vstring_alloc(10);
+ sp->endp.endp_prop = vstring_alloc(10);
+ sp->endp.fd = -1;
+
+ sp->dest.dest_label = vstring_alloc(10);
+ sp->dest.dest_prop = vstring_alloc(10);
+ sp->dest.endp_label = vstring_alloc(10);
+
+ return (sp->scache);
+}
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
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
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
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
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
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
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
/* .fi
/* The external command attributes are given in the \fBmaster.cf\fR
/* file at the end of a service definition. The syntax is as follows:
+/* .IP "\fBdirectory=\fIpathname\fR (optional, default: \fB$queue_directory\fR)"
+/* Change to the named directory before executing the external command.
+/* Delivery is deferred in case of failure.
+/* .sp
+/* This feature is available as of Postfix 2.2.
/* .IP "\fBeol=string\fR (optional, default: \fB\en\fR)"
/* The output record delimiter. Typically one would use either
/* \fB\er\en\fR or \fB\en\fR. The usual C-style backslash escape
/* sequences are recognized: \fB\ea \eb \ef \en \er \et \ev
-/* \e\fIoctal\fR and \fB\e\e\fR.
+/* \e\fIddd\fR (up to three octal digits) and \fB\e\e\fR.
/* .IP "\fBflags=BDFORhqu.>\fR (optional)"
/* Optional message processing flags. By default, a message is
/* copied unchanged.
/* the left of the right-most \fB@\fR character) to lower case.
/* This is recommended for delivery via \fBUUCP\fR.
/* .IP \fB.\fR
-/* Prepend \fB.\fR to lines starting with "\fB.\fR". This is needed
+/* Prepend "\fB.\fR" to lines starting with "\fB.\fR". This is needed
/* by, for example, \fBBSMTP\fR software.
/* .IP \fB>\fR
-/* Prepend \fB>\fR to lines starting with "\fBFrom \fR". This is expected
+/* Prepend "\fB>\fR" to lines starting with "\fBFrom \fR". This is expected
/* by, for example, \fBUUCP\fR software.
/* .RE
/* .IP "\fBsize\fR=\fIsize_limit\fR (optional)"
/* In addition to the form ${\fIname\fR}, the forms $\fIname\fR and
/* $(\fIname\fR) are also recognized. Specify \fB$$\fR where a single
/* \fB$\fR is wanted.
-/* .PP
-/* Available in Postfix 2.2 and later:
-/* .IP "\fBdirectory=\fIpathname\fR (optional)"
-/* Change to the specified directory before executing the command.
-/* Failure causes mail delivery to be deferred.
/* DIAGNOSTICS
/* Command exit status codes are expected to
/* follow the conventions defined in <\fBsysexits.h\fR>.
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
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
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
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
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
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
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
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
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
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
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
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
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
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
--- /dev/null
+../../.indent.pro
\ No newline at end of file
--- /dev/null
+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
--- /dev/null
+/*++
+/* NAME
+/* scache 8
+/* SUMMARY
+/* Postfix session cache server
+/* SYNOPSIS
+/* \fBscache\fR [generic Postfix daemon options]
+/* DESCRIPTION
+/* The scache server maintains the Postfix session cache. This
+/* information can be used by, for example, the Postfix SMTP client.
+/*
+/* The session cache is organized into logical destination
+/* names, physical endpoint names, and sessions.
+/*
+/* As a specific example, logical SMTP destinations specify
+/* (transport, domain, port), and physical SMTP endpoints
+/* specify (transport, IP address, port). An SMTP session
+/* may be saved after a successful mail transaction.
+/*
+/* In the general case, one logical destination may refer to
+/* zero or more physical endpoints, one physical endpoint may
+/* be referenced by zero or more logical destinations, and
+/* one endpoint may refer to zero or more sessions.
+/*
+/* The exact syntax of a logical destination or endpoint name
+/* is application dependent; the \fBscache\fR service does
+/* not care. A session is stored as a file descriptor together
+/* with application-dependent information that is needed to
+/* re-activate a session object. Again, the \fBscache\fR
+/* service is completely unaware about the details of that
+/* information.
+/*
+/* All information is stored with a finite time to live (ttl).
+/* The session cache daemon terminates when no client is
+/* connected for \fBmax_idle\fR time units.
+/*
+/* This server implements the following requests:
+/* .IP "\fBsave_endp\fI ttl endpoint endpoint_properties file_descriptor\fR"
+/* Save the specified file descriptor and session property data
+/* under the specified endpoint name. The endpoint properties
+/* are used by the client to re-activate a passivated session
+/* object.
+/* queue ID is queued for the specified destination.
+/* .IP "\fBfind_endp\fI endpoint\fR"
+/* Look up cached properties and a cached file descriptor for the
+/* specified endpoint.
+/* .IP "\fBsave_dest\fI ttl destination destination_properties endpoint\fR"
+/* Save the binding between a logical destination and an
+/* endpoint under the destination name, together with destination
+/* specific session properties. The destination properties
+/* are used by the client to re-activate a passivated session
+/* object.
+/* .IP "\fBfind_dest\fI destination\fR"
+/* Look up cached destination properties, cached endpoint properties,
+/* and a cached file descriptor for the specified logical destination.
+/* SECURITY
+/* .ad
+/* .fi
+/* The session cache server is not security-sensitive. It does not
+/* talk to the network, and it does not talk to local users.
+/* The scache server can run chrooted at fixed low privilege.
+/*
+/* The session cache server is not a trusted process. It must
+/* not be used to store information that is security sensitive.
+/* DIAGNOSTICS
+/* Problems and transactions are logged to \fBsyslogd\fR(8).
+/* BUGS
+/* Sessions cannot be cached across multiple machines.
+/*
+/* When a session expires from the cache it is closed without
+/* protocol specific handshake.
+/* CONFIGURATION PARAMETERS
+/* .ad
+/* .fi
+/* Changes to \fBmain.cf\fR are picked up automatically as scache(8)
+/* processes run for only a limited amount of time. Use the command
+/* "\fBpostfix reload\fR" to speed up a change.
+/*
+/* The text below provides only a parameter summary. See
+/* postconf(5) for more details including examples.
+/* RESOURCE CONTROLS
+/* .ad
+/* .fi
+/* .IP "\fBsession_cache_ttl_limit (2s)\fR"
+/* The maximal time-to-live value that the session cache server
+/* allows.
+/* MISCELLANEOUS CONTROLS
+/* .ad
+/* .fi
+/* .IP "\fBconfig_directory (see 'postconf -d' output)\fR"
+/* The default location of the Postfix main.cf and master.cf
+/* configuration files.
+/* .IP "\fBdaemon_timeout (18000s)\fR"
+/* How much time a Postfix daemon process may take to handle a
+/* request before it is terminated by a built-in watchdog timer.
+/* .IP "\fBipc_timeout (3600s)\fR"
+/* The time limit for sending or receiving information over an internal
+/* communication channel.
+/* .IP "\fBmax_idle (100s)\fR"
+/* The maximum amount of time that an idle Postfix daemon process
+/* waits for the next service request before exiting.
+/* .IP "\fBprocess_id (read-only)\fR"
+/* The process ID of a Postfix command or daemon process.
+/* .IP "\fBprocess_name (read-only)\fR"
+/* The process name of a Postfix command or daemon process.
+/* .IP "\fBsyslog_facility (mail)\fR"
+/* The syslog facility of Postfix logging.
+/* .IP "\fBsyslog_name (postfix)\fR"
+/* The mail system name that is prepended to the process name in syslog
+/* records, so that "smtpd" becomes, for example, "postfix/smtpd".
+/* SEE ALSO
+/* smtp(8), SMTP client
+/* postconf(5), configuration parameters
+/* master(8), process manager
+/* syslogd(8), system logging
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* HISTORY
+/* This service was introduced with Postfix version 2.2.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <iostuff.h>
+#include <htable.h>
+#include <ring.h>
+
+/* Global library. */
+
+#include <mail_params.h>
+#include <mail_proto.h>
+#include <scache.h>
+
+/* Single server skeleton. */
+
+#include <mail_server.h>
+#include <mail_conf.h>
+
+/* Application-specific. */
+
+ /*
+ * Tunable parameters.
+ */
+int var_scache_ttl_lim;
+
+ /*
+ * Request parameters.
+ */
+static VSTRING *scache_request;
+static VSTRING *scache_dest_label;
+static VSTRING *scache_dest_prop;
+static VSTRING *scache_endp_label;
+static VSTRING *scache_endp_prop;
+
+ /*
+ * Session cache instance.
+ */
+static SCACHE *scache;
+
+ /*
+ * Silly little macros.
+ */
+#define STR(x) vstring_str(x)
+#define VSTREQ(x,y) (strcmp(STR(x),y) == 0)
+
+/* scache_save_endp_service - protocol to save endpoint->stream binding */
+
+static void scache_save_endp_service(VSTREAM *client_stream)
+{
+ const char *myname = "scache_save_endp_service";
+ int ttl;
+ int fd;
+
+ if (attr_scan(client_stream,
+ ATTR_FLAG_STRICT,
+ ATTR_TYPE_NUM, MAIL_ATTR_TTL, &ttl,
+ ATTR_TYPE_STR, MAIL_ATTR_LABEL, scache_endp_label,
+ ATTR_TYPE_STR, MAIL_ATTR_PROP, scache_endp_prop,
+ ATTR_TYPE_END) != 3
+ || ttl <= 0) {
+ msg_warn("%s: bad or missing request parameter", myname);
+ attr_print(client_stream, ATTR_FLAG_NONE,
+ ATTR_TYPE_NUM, MAIL_ATTR_STATUS, SCACHE_STAT_BAD,
+ ATTR_TYPE_END);
+ return;
+ } else if ((fd = LOCAL_RECV_FD(vstream_fileno(client_stream))) < 0) {
+ msg_warn("%s: unable to receive file descriptor", myname);
+ attr_print(client_stream, ATTR_FLAG_NONE,
+ ATTR_TYPE_NUM, MAIL_ATTR_STATUS, SCACHE_STAT_FAIL,
+ ATTR_TYPE_END);
+ return;
+ } else {
+ scache_save_endp(scache,
+ ttl > var_scache_ttl_lim ? var_scache_ttl_lim : ttl,
+ STR(scache_endp_label), STR(scache_endp_prop), fd);
+ attr_print(client_stream, ATTR_FLAG_NONE,
+ ATTR_TYPE_NUM, MAIL_ATTR_STATUS, SCACHE_STAT_OK,
+ ATTR_TYPE_END);
+ return;
+ }
+}
+
+/* scache_find_endp_service - protocol to find session for endpoint */
+
+static void scache_find_endp_service(VSTREAM *client_stream)
+{
+ const char *myname = "scache_find_endp_service";
+ int fd;
+
+ if (attr_scan(client_stream,
+ ATTR_FLAG_STRICT,
+ ATTR_TYPE_STR, MAIL_ATTR_LABEL, scache_endp_label,
+ ATTR_TYPE_END) != 1) {
+ msg_warn("%s: bad or missing request parameter", myname);
+ attr_print(client_stream, ATTR_FLAG_NONE,
+ ATTR_TYPE_NUM, MAIL_ATTR_STATUS, SCACHE_STAT_BAD,
+ ATTR_TYPE_STR, MAIL_ATTR_PROP, "",
+ ATTR_TYPE_END);
+ return;
+ } else if ((fd = scache_find_endp(scache, STR(scache_endp_label),
+ scache_endp_prop)) < 0) {
+ attr_print(client_stream, ATTR_FLAG_NONE,
+ ATTR_TYPE_NUM, MAIL_ATTR_STATUS, SCACHE_STAT_FAIL,
+ ATTR_TYPE_STR, MAIL_ATTR_PROP, "",
+ ATTR_TYPE_END);
+ return;
+ } else {
+ attr_print(client_stream, ATTR_FLAG_NONE,
+ ATTR_TYPE_NUM, MAIL_ATTR_STATUS, SCACHE_STAT_OK,
+ ATTR_TYPE_STR, MAIL_ATTR_PROP, STR(scache_endp_prop),
+ ATTR_TYPE_END);
+ if (vstream_fflush(client_stream) != 0
+ || LOCAL_SEND_FD(vstream_fileno(client_stream), fd) < 0)
+ msg_warn("%s: cannot send file descriptor: %m", myname);
+ if (close(fd) < 0)
+ msg_warn("close(%d): %m", fd);
+ return;
+ }
+}
+
+/* scache_save_dest_service - protocol to save destiation->endpoint binding */
+
+static void scache_save_dest_service(VSTREAM *client_stream)
+{
+ const char *myname = "scache_save_dest_service";
+ int ttl;
+
+ if (attr_scan(client_stream,
+ ATTR_FLAG_STRICT,
+ ATTR_TYPE_NUM, MAIL_ATTR_TTL, &ttl,
+ ATTR_TYPE_STR, MAIL_ATTR_LABEL, scache_dest_label,
+ ATTR_TYPE_STR, MAIL_ATTR_PROP, scache_dest_prop,
+ ATTR_TYPE_STR, MAIL_ATTR_LABEL, scache_endp_label,
+ ATTR_TYPE_END) != 4
+ || ttl <= 0) {
+ msg_warn("%s: bad or missing request parameter", myname);
+ attr_print(client_stream, ATTR_FLAG_NONE,
+ ATTR_TYPE_NUM, MAIL_ATTR_STATUS, SCACHE_STAT_BAD,
+ ATTR_TYPE_END);
+ return;
+ } else {
+ scache_save_dest(scache,
+ ttl > var_scache_ttl_lim ? var_scache_ttl_lim : ttl,
+ STR(scache_dest_label), STR(scache_dest_prop),
+ STR(scache_endp_label));
+ attr_print(client_stream, ATTR_FLAG_NONE,
+ ATTR_TYPE_NUM, MAIL_ATTR_STATUS, SCACHE_STAT_OK,
+ ATTR_TYPE_END);
+ return;
+ }
+}
+
+/* scache_find_dest_service - protocol to find session for destination */
+
+static void scache_find_dest_service(VSTREAM *client_stream)
+{
+ const char *myname = "scache_find_dest_service";
+ int fd;
+
+ if (attr_scan(client_stream,
+ ATTR_FLAG_STRICT,
+ ATTR_TYPE_STR, MAIL_ATTR_LABEL, scache_dest_label,
+ ATTR_TYPE_END) != 1) {
+ msg_warn("%s: bad or missing request parameter", myname);
+ attr_print(client_stream, ATTR_FLAG_NONE,
+ ATTR_TYPE_NUM, MAIL_ATTR_STATUS, SCACHE_STAT_BAD,
+ ATTR_TYPE_STR, MAIL_ATTR_PROP, "",
+ ATTR_TYPE_STR, MAIL_ATTR_PROP, "",
+ ATTR_TYPE_END);
+ return;
+ } else if ((fd = scache_find_dest(scache, STR(scache_dest_label),
+ scache_dest_prop,
+ scache_endp_prop)) < 0) {
+ attr_print(client_stream, ATTR_FLAG_NONE,
+ ATTR_TYPE_NUM, MAIL_ATTR_STATUS, SCACHE_STAT_FAIL,
+ ATTR_TYPE_STR, MAIL_ATTR_PROP, "",
+ ATTR_TYPE_STR, MAIL_ATTR_PROP, "",
+ ATTR_TYPE_END);
+ return;
+ } else {
+ attr_print(client_stream, ATTR_FLAG_NONE,
+ ATTR_TYPE_NUM, MAIL_ATTR_STATUS, SCACHE_STAT_OK,
+ ATTR_TYPE_STR, MAIL_ATTR_PROP, STR(scache_dest_prop),
+ ATTR_TYPE_STR, MAIL_ATTR_PROP, STR(scache_endp_prop),
+ ATTR_TYPE_END);
+ if (vstream_fflush(client_stream) != 0
+ || LOCAL_SEND_FD(vstream_fileno(client_stream), fd) < 0)
+ msg_warn("%s: cannot send file descriptor: %m", myname);
+ if (close(fd) < 0)
+ msg_warn("close(%d): %m", fd);
+ return;
+ }
+}
+
+/* scache_service - perform service for client */
+
+static void scache_service(VSTREAM *client_stream, char *unused_service,
+ char **argv)
+{
+
+ /*
+ * Sanity check. This service takes no command-line arguments.
+ */
+ if (argv[0])
+ msg_fatal("unexpected command-line argument: %s", argv[0]);
+
+ /*
+ * This routine runs whenever a client connects to the UNIX-domain socket
+ * dedicated to the scache service. All connection-management stuff is
+ * handled by the common code in multi_server.c.
+ */
+ if (attr_scan(client_stream,
+ ATTR_FLAG_MORE | ATTR_FLAG_STRICT,
+ ATTR_TYPE_STR, MAIL_ATTR_REQ, scache_request,
+ ATTR_TYPE_END) == 1) {
+ if (VSTREQ(scache_request, SCACHE_REQ_SAVE_DEST)) {
+ scache_save_dest_service(client_stream);
+ } else if (VSTREQ(scache_request, SCACHE_REQ_FIND_DEST)) {
+ scache_find_dest_service(client_stream);
+ } else if (VSTREQ(scache_request, SCACHE_REQ_SAVE_ENDP)) {
+ scache_save_endp_service(client_stream);
+ } else if (VSTREQ(scache_request, SCACHE_REQ_FIND_ENDP)) {
+ scache_find_endp_service(client_stream);
+ } else {
+ msg_warn("unrecognized request: \"%s\", ignored",
+ STR(scache_request));
+ attr_print(client_stream, ATTR_FLAG_NONE,
+ ATTR_TYPE_NUM, MAIL_ATTR_STATUS, SCACHE_STAT_BAD,
+ ATTR_TYPE_END);
+ }
+ }
+ vstream_fflush(client_stream);
+}
+
+/* post_jail_init - initialization after privilege drop */
+
+static void post_jail_init(char *unused_name, char **unused_argv)
+{
+
+ /*
+ * Pre-allocate the cache instance.
+ */
+ scache = scache_multi_create();
+
+ /*
+ * Pre-allocate buffers.
+ */
+ scache_request = vstring_alloc(10);
+ scache_dest_label = vstring_alloc(10);
+ scache_dest_prop = vstring_alloc(10);
+ scache_endp_label = vstring_alloc(10);
+ scache_endp_prop = vstring_alloc(10);
+
+ /*
+ * Disable the max_use limit. We still terminate when no client is
+ * connected for $idle_limit time units.
+ */
+ var_use_limit = 0;
+}
+
+/* main - pass control to the multi-threaded skeleton */
+
+int main(int argc, char **argv)
+{
+ static CONFIG_TIME_TABLE time_table[] = {
+ VAR_SCACHE_TTL_LIM, DEF_SCACHE_TTL_LIM, &var_scache_ttl_lim, 1, 0,
+ 0,
+ };
+
+ multi_server_main(argc, argv, scache_service,
+ MAIL_SERVER_TIME_TABLE, time_table,
+ MAIL_SERVER_POST_INIT, post_jail_init,
+ MAIL_SERVER_SOLITARY,
+ 0);
+}
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
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
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)
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
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
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
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
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
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
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
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
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
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
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
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
/* When a server is not reachable, or when mail delivery fails due
/* to a recoverable error condition, the SMTP client will try to
/* deliver the mail to an alternate host.
+/*
+/* After a successful mail transaction, a session may be saved
+/* to the \fBscache(8)\fR session cache server, so that it
+/* may be used by any SMTP client for a subsequent transaction.
+/* Session caching is disabled by default.
/* SECURITY
/* .ad
/* .fi
/* Depending on the setting of the \fBnotify_classes\fR parameter,
/* the postmaster is notified of bounces, protocol problems, and of
/* other trouble.
+/* BUGS
+/* SMTP session caching does not work with TLS. The necessary
+/* support for object passivation and re-activation does not
+/* exist.
+/*
+/* SMTP session caching assumes that SASL credentials are valid for
+/* all destinations that map onto the same IP address and TCP port.
/* CONFIGURATION PARAMETERS
/* .ad
/* .fi
/* .IP "\fBmime_boundary_length_limit (2048)\fR"
/* The maximal length of MIME multipart boundary strings.
/* .IP "\fBmime_nesting_limit (100)\fR"
-/* The maximal nesting level of multipart mail that the MIME processor
-/* will handle.
+/* The maximal recursion level that the MIME processor will handle.
/* EXTERNAL CONTENT INSPECTION CONTROLS
/* .ad
/* .fi
/* The maximal number of SMTP sessions per delivery request before
/* giving up or delivering to a fall-back relay host, or zero (no
/* limit).
-/* .IP "\fBsmtp_rset_timeout (120s)\fR"
+/* .IP "\fBsmtp_rset_timeout (20s)\fR"
/* The SMTP client time limit for sending the RSET command, and
/* for receiving the server response.
+/* .PP
+/* Available in Postfix version 2.2 and later:
+/* .IP "\fBsmtp_connection_cache_domains (empty)\fR"
+/* The SMTP destinations for which SMTP connection caching is
+/* enabled.
+/* .IP "\fBsmtp_connection_cache_reuse_limit (10)\fR"
+/* When SMTP session caching is enabled, the number of times that
+/* an SMTP session is reused before it is closed.
+/* .IP "\fBsmtp_connection_cache_time_limit (2s)\fR"
+/* When SMTP session caching is enabled, the amount of time that
+/* an unused SMTP client socket is kept open before it is closed.
/* TROUBLE SHOOTING CONTROLS
/* .ad
/* .fi
/* SEE ALSO
/* qmgr(8), queue manager
/* bounce(8), delivery status reports
+/* scache(8), session cache server
/* postconf(5), configuration parameters
/* master(8), process manager
/* syslogd(8), system logging
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
+/*
+/* Command pipelining in cooperation with:
+/* Jon Ribbens
+/* Oaktree Internet Solutions Ltd.,
+/* Internet House,
+/* Canal Basin,
+/* Coventry,
+/* CV1 4LY, United Kingdom.
+/*
+/* Connection caching in cooperation with:
+/* Victor Duchovni
+/* Morgan Stanley
/*--*/
/* System library. */
#include <mail_conf.h>
#include <debug_peer.h>
#include <flush_clnt.h>
+#include <scache.h>
+#include <string_list.h>
/* Single server skeleton. */
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
*/
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;
state = smtp_state_alloc();
state->request = request;
state->src = request->fp;
+ state->service = service;
SMTP_RCPT_INIT(state);
/*
/* 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;
* 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,
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.
*/
* 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 */
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[] = {
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[] = {
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);
#include <vstream.h>
#include <vstring.h>
#include <argv.h>
+#include <htable.h>
/*
* Global library.
*/
#include <deliver_request.h>
+#include <scache.h>
+#include <string_list.h>
/*
* State information associated with each SMTP delivery. We're bundling the
*/
typedef struct SMTP_STATE {
VSTREAM *src; /* queue file stream */
+ const char *service; /* transport name */
DELIVER_REQUEST *request; /* envelope info, offsets */
struct SMTP_SESSION *session; /* network connection */
int status; /* delivery status */
int space_left; /* output length control */
+ /*
+ * Session cache support. The (nexthop_lookup_mx, nexthop_domain,
+ * nexthop_port) triple is a parsed next-hop specification, and should be
+ * a data type by itself. The (service, nexthop_mumble) members specify
+ * the name under which the first good session should be cached. The
+ * nexthop_mumble members are initialized by the connection management
+ * module. nexthop_domain is reset to null after one session is saved
+ * under the (service, nexthop_mumble) label, or upon exit from the
+ * connection management module.
+ */
+ HTABLE *cache_used; /* cached addresses that were used */
+ VSTRING *dest_label; /* cached logical/physical binding */
+ VSTRING *dest_prop; /* binding properties, passivated */
+ VSTRING *endp_label; /* cached session physical endpoint */
+ VSTRING *endp_prop; /* endpoint properties, passivated */
+ int nexthop_lookup_mx; /* do/don't MX expand nexthop_domain */
+ char *nexthop_domain; /* next-hop name or bare address */
+ unsigned nexthop_port; /* next-hop TCP port, network order */
+
/*
* Flags and counters to control the handling of mail delivery errors.
* There is some redundancy for sanity checking. At the end of an SMTP
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.
*/
#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.
#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 */
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 */
} 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
*/
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
{
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);
/* that fail to complete the SMTP handshake and tries to find
/* an alternate server when an SMTP session fails to deliver.
/*
+/* This layer also controls what sessions are retrieved from
+/* the session cache, and what sessions are saved to the cache.
+/*
/* The destination is either a host (or domain) name or a numeric
/* address. Symbolic or numeric service port information may be
/* appended, separated by a colon (":").
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
+/*
+/* Connection caching in cooperation with:
+/* Victor Duchovni
+/* Morgan Stanley
/*--*/
/* System library. */
#define INADDR_NONE 0xffffffff
#endif
+#ifndef IPPORT_SMTP
+#define IPPORT_SMTP 25
+#endif
+
/* Utility library. */
#include <msg.h>
#include <mail_params.h>
#include <own_inet_addr.h>
-#include <debug_peer.h>
#include <deliver_pass.h>
#include <mail_error.h>
/* Application-specific. */
-#include "smtp.h"
-#include "smtp_addr.h"
+#include <smtp.h>
+#include <smtp_addr.h>
+#include <smtp_reuse.h>
+
+#define STR(x) vstring_str(x)
/* smtp_connect_addr - connect to explicit address */
-static SMTP_SESSION *smtp_connect_addr(char *dest, DNS_RR *addr, unsigned port,
- VSTRING *why)
+static SMTP_SESSION *smtp_connect_addr(const char *dest, DNS_RR *addr,
+ unsigned port, VSTRING *why,
+ int sess_flags)
{
char *myname = "smtp_connect_addr";
struct sockaddr_in sin;
}
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 */
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)
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;
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
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.
* 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.
* 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);
}
/*
/*
* Cleanup.
*/
+ if (HAVE_NEXTHOP_STATE(state))
+ FREE_NEXTHOP_STATE(state);
argv_free(sites);
vstring_free(why);
return (state->status);
/*
/* int smtp_xfer(state)
/* SMTP_STATE *state;
+/*
+/* int smtp_rset(state)
+/* SMTP_STATE *state;
+/*
+/* int smtp_quit(state)
+/* SMTP_STATE *state;
/* DESCRIPTION
/* This module implements the client side of the SMTP protocol.
/*
/* Recipients are marked as "done" in the mail queue file when
/* bounced or delivered. The message delivery status is updated
/* accordingly.
+/*
+/* smtp_rset() sends a single RSET command and waits for the
+/* response. In case of no response, or negative response, it
+/* turns off caching for the current session.
+/*
+/* smtp_quit() sends a single QUIT command and waits for the
+/* response if configured to do so. It always turns off caching
+/* for the current session.
/* DIAGNOSTICS
-/* smtp_helo() and smtp_xfer() return 0 in case of success, -1 in case
-/* of failure. For smtp_xfer(), success means the ability to perform
-/* an SMTP conversation, not necessarily the ability to deliver mail.
+/* smtp_helo(), smtp_xfer(), smtp_rset() and smtp_quit() return
+/* 0 in case of success, -1 in case of failure. For smtp_xfer(),
+/* smtp_rset() and smtp_quit(), success means the ability to
+/* perform an SMTP conversation, not necessarily the ability
+/* to deliver mail, or the achievement of server happiness.
/*
/* Warnings: corrupt message file. A corrupt message is marked
/* as "corrupt" by changing its queue file permissions.
/* Coventry,
/* CV1 4LY, United Kingdom.
/*
+/* Connection caching in cooperation with:
+/* Victor Duchovni
+/* Morgan Stanley
/*--*/
/* System library. */
* Normal sessions go from MAIL->RCPT->DATA->DOT->QUIT->LAST. The states
* MAIL, RCPT, and DATA may also be followed by ABORT->QUIT->LAST.
*
+ * When connection caching is enabled, the QUIT state is suppressed. Normal
+ * sessions proceed as MAIL->RCPT->DATA->DOT->LAST, while aborted sessions
+ * end with ABORT->LAST. The connection is left open for a limited time. An
+ * RSET probe should be sent before attempting to reuse an open connection
+ * for a new transaction.
+ *
+ * The code to send an RSET probe is a special case with its own initial state
+ * and with its own dedicated state transitions. The session proceeds as
+ * RSET->LAST. This code is kept inside the main protocol engine for
+ * consistent error handling and error reporting. It is not to be confused
+ * with the code that sends RSET to abort a mail transaction in progress.
+ *
+ * The code to send QUIT without message delivery transaction jumps into the
+ * main state machine. If this introduces complications, then we should
+ * introduce a second QUIT state with its own dedicated state transitions,
+ * just like we did for RSET probes.
+ *
* By default, the receiver skips the QUIT response. Some SMTP servers
* disconnect after responding to ".", and some SMTP servers wait before
* responding to QUIT.
#define SMTP_STATE_DATA 4
#define SMTP_STATE_DOT 5
#define SMTP_STATE_ABORT 6
-#define SMTP_STATE_QUIT 7
-#define SMTP_STATE_LAST 8
+#define SMTP_STATE_RSET 7
+#define SMTP_STATE_QUIT 8
+#define SMTP_STATE_LAST 9
int *xfer_timeouts[SMTP_STATE_LAST] = {
&var_smtp_xfwd_tmout, /* name/addr */
&var_smtp_data0_tmout,
&var_smtp_data2_tmout,
&var_smtp_rset_tmout,
+ &var_smtp_rset_tmout,
&var_smtp_quit_tmout,
};
"sending DATA command",
"sending end of data -- message may be sent more than once",
"sending final RSET",
+ "sending RSET probe",
"sending QUIT",
};
"DATA command",
"end of DATA command",
"final RSET command",
+ "RSET probe",
"QUIT command",
};
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;
XFORWARD_HELO, SMTP_FEATURE_XFORWARD_HELO,
0, 0,
};
+ SOCKOPT_SIZE optlen;
/*
* Prepare for disaster.
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;
/*
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) {
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));
}
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));
}
}
-/* 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;
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.
#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
* 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.
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;
*/
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;
/*
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;
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;
/*
* 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.
*/
}
}
}
- 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;
* 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.
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;
}
}
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);
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));
+}
--- /dev/null
+/*++
+/* NAME
+/* smtp_reuse 3
+/* SUMMARY
+/* SMTP session cache glue
+/* SYNOPSIS
+/* #include <smtp.h>
+/* #include <smtp_reuse.h>
+/*
+/* void smtp_save_session(state)
+/* SMTP_STATE *state;
+/*
+/* SMTP_SESSION *smtp_reuse_domain(state, lookup_mx, domain, port)
+/* SMTP_STATE *state;
+/* int lookup_mx;
+/* char *domain;
+/* unsigned port;
+/*
+/* SMTP_SESSION *smtp_reuse_addr(state, addr, port)
+/* SMTP_STATE *state;
+/* DNS_RR *addr;
+/* unsigned port;
+/* DESCRIPTION
+/* This module implements the SMTP client specific interface to
+/* the generic session cache infrastructure.
+/*
+/* smtp_save_session() stores the current session under the
+/* next-hop logical destination (if available) and under the
+/* remote server address. The SMTP_SESSION object is destroyed.
+/*
+/* smtp_reuse_domain() looks up a cached session by its logical
+/* destination, and verifies that the session is still alive.
+/* The restored session information includes the "best MX" bit.
+/* The result is null in case of failure.
+/*
+/* smtp_reuse_addr() looks up a cached session by its server
+/* address, and verifies that the session is still alive.
+/* The result is null in case of failure.
+/*
+/* Arguments:
+/* .IP state
+/* SMTP client state, including the current session, the original
+/* next-hop domain, etc.
+/* .IP lookup_mx
+/* Whether or not the domain is subject to MX lookup.
+/* .IP domain
+/* Domain name or bare numerical address.
+/* .IP addr
+/* The remote server name and address.
+/* .IP port
+/* The remote server port, network byte order.
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <string.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <vstream.h>
+#include <vstring.h>
+#include <htable.h>
+#include <stringops.h>
+
+/* Global library. */
+
+#include <scache.h>
+#include <mail_params.h>
+
+/* Application-specific. */
+
+#include <smtp.h>
+#include <smtp_reuse.h>
+
+ /*
+ * We encode the MX lookup/A lookup method into the name under which SMTP
+ * session information is cached. The following macros serve to make the
+ * remainder of the code less obscure.
+ */
+#define NO_MX_LOOKUP 0
+
+#define SMTP_SCACHE_LABEL(mx_lookup_flag) \
+ ((mx_lookup_flag) ? "%s:%s:%u" : "%s:[%s]:%u")
+
+#define STR(x) vstring_str(x)
+
+/* smtp_save_session - save session under next-hop name and server address */
+
+void smtp_save_session(SMTP_STATE *state)
+{
+ SMTP_SESSION *session = state->session;
+ int fd;
+
+ /*
+ * Encode the next-hop logical destination, if available. Reuse storage
+ * that is also used for cache lookup queries.
+ *
+ * Note: if the label needs to be made more specific (with e.g., SASL login
+ * information), just append the text with vstring_sprintf_append().
+ */
+ if (HAVE_NEXTHOP_STATE(state))
+ vstring_sprintf(state->dest_label,
+ SMTP_SCACHE_LABEL(state->nexthop_lookup_mx),
+ state->service, state->nexthop_domain,
+ ntohs(state->nexthop_port));
+
+ /*
+ * Encode the physical endpoint name. Reuse storage that is also used for
+ * cache lookup queries.
+ *
+ * Note: if the label needs to be made more specific (with e.g., SASL login
+ * information), just append the text with vstring_sprintf_append().
+ */
+ vstring_sprintf(state->endp_label,
+ SMTP_SCACHE_LABEL(NO_MX_LOOKUP),
+ state->service, session->addr, ntohs(session->port));
+
+ /*
+ * Passivate the SMTP_SESSION object, destroying the object in the
+ * process. Reuse storage that is also used for cache lookup results.
+ */
+ fd = smtp_session_passivate(session, state->dest_prop, state->endp_prop);
+ state->session = 0;
+
+ /*
+ * Save the session under the next-hop name, if available.
+ *
+ * XXX The logical to physical binding can be kept for as long as the DNS
+ * allows us to (but that could result in the caching of lots of unused
+ * bindings). The session should be idle for no more than 30 seconds or
+ * so.
+ */
+ if (HAVE_NEXTHOP_STATE(state))
+ scache_save_dest(smtp_scache, var_smtp_cache_conn, STR(state->dest_label),
+ STR(state->dest_prop), STR(state->endp_label));
+
+ /*
+ * Save every good sessions under its physical endpoint address.
+ */
+ scache_save_endp(smtp_scache, var_smtp_cache_conn, STR(state->endp_label),
+ STR(state->endp_prop), fd);
+}
+
+/* smtp_reuse_common - common session reuse code */
+
+static SMTP_SESSION *smtp_reuse_common(SMTP_STATE *state, int fd,
+ const char *label)
+{
+ const char *myname = "smtp_reuse_common";
+ SMTP_SESSION *session;
+
+ /*
+ * Re-activate the SMTP_SESSION object.
+ */
+ session = smtp_session_activate(fd, state->dest_prop, state->endp_prop);
+ if (session == 0) {
+ msg_warn("%s: bad cached session attribute for %s", myname, label);
+ (void) close(fd);
+ return (0);
+ }
+ state->session = session;
+
+ /*
+ * Send an RSET probe to verify that the session is still good.
+ */
+ if (smtp_rset(state) < 0
+ || (session->features & SMTP_FEATURE_RSET_REJECTED) != 0) {
+ smtp_session_free(session);
+ return (state->session = 0);
+ }
+
+ /*
+ * Update the list of used cached addresses.
+ */
+ htable_enter(state->cache_used, session->addr, (char *) 0);
+
+ return (session);
+}
+
+/* smtp_reuse_domain - reuse session cached under domain name */
+
+SMTP_SESSION *smtp_reuse_domain(SMTP_STATE *state, int lookup_mx,
+ const char *domain, unsigned port)
+{
+ SMTP_SESSION *session;
+ int fd;
+
+ /*
+ * Look up the session by its logical name.
+ *
+ * Note: if the label needs to be made more specific (with e.g., SASL login
+ * information), just append the text with vstring_sprintf_append().
+ */
+ vstring_sprintf(state->dest_label, SMTP_SCACHE_LABEL(lookup_mx),
+ state->service, domain, ntohs(port));
+ if ((fd = scache_find_dest(smtp_scache, STR(state->dest_label),
+ state->dest_prop, state->endp_prop)) < 0)
+ return (0);
+
+ /*
+ * Re-activate the SMTP_SESSION object, and verify that the session is
+ * still good.
+ */
+ session = smtp_reuse_common(state, fd, STR(state->dest_label));
+ return (session);
+}
+
+/* smtp_reuse_addr - reuse session cached under numerical address */
+
+SMTP_SESSION *smtp_reuse_addr(SMTP_STATE *state, DNS_RR *addr, unsigned port)
+{
+ SMTP_SESSION *session;
+ int fd;
+
+#define INADDRP(x) ((struct in_addr *) (x))
+
+ /*
+ * Look up the session by its IP address. This means that we have no
+ * destination-to-address binding properties.
+ *
+ * Note: if the label needs to be made more specific (with e.g., SASL login
+ * information), just append the text with vstring_sprintf_append().
+ */
+ if (addr->data_len > sizeof(struct in_addr))
+ return (0);
+ vstring_sprintf(state->endp_label, SMTP_SCACHE_LABEL(NO_MX_LOOKUP),
+ state->service, inet_ntoa(*INADDRP(addr->data)),
+ ntohs(port));
+ if ((fd = scache_find_endp(smtp_scache, STR(state->endp_label),
+ state->endp_prop)) < 0)
+ return (0);
+ VSTRING_RESET(state->dest_prop);
+ VSTRING_TERMINATE(state->dest_prop);
+
+ /*
+ * Re-activate the SMTP_SESSION object, and verify that the session is
+ * still good.
+ */
+ session = smtp_reuse_common(state, fd, STR(state->endp_label));
+
+ /*
+ * XXX What if hostnames don't match (addr->name versus session->name),
+ * or if the SASL login name for this host does not match the SASL login
+ * name that was used when opening this session? If something depends
+ * critically on such information being identical, then that information
+ * should be included in the logical and physical labels under which a
+ * session is cached.
+ */
+ return (session);
+}
--- /dev/null
+/*++
+/* NAME
+/* smtp_reuse 3h
+/* SUMMARY
+/* SMTP session cache glue
+/* SYNOPSIS
+/* #include <smtp_reuse.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * DNS library.
+ */
+#include <dns.h>
+
+ /*
+ * Internal interfaces.
+ */
+extern void smtp_save_session(SMTP_STATE *);
+extern SMTP_SESSION *smtp_reuse_domain(SMTP_STATE *, int, const char *, unsigned);
+extern SMTP_SESSION *smtp_reuse_addr(SMTP_STATE *, DNS_RR *, unsigned);
+
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*--*/
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
/*
/* void smtp_sasl_cleanup(session)
/* SMTP_SESSION *session;
+/*
+/* void smtp_sasl_passivate(session, buf)
+/* SMTP_SESSION *session;
+/* VSTRING *buf;
+/*
+/* int smtp_sasl_activate(session, buf)
+/* SMTP_SESSION *session;
+/* char *buf;
/* DESCRIPTION
/* smtp_sasl_initialize() initializes the SASL library. This
/* routine must be called once at process startup, before any
/* end of every SMTP session that uses SASL authentication.
/* This routine is a noop for non-SASL sessions.
/*
+/* smtp_sasl_passivate() appends flattened SASL attributes to the
+/* specified buffer. The SASL attributes are not destroyed.
+/*
+/* smtp_sasl_activate() restores SASL attributes from the
+/* specified buffer. The buffer is modified. A result < 0
+/* means there was an error.
+/*
/* Arguments:
/* .IP session
/* Session context.
*
* 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 : "");
}
}
+/* 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
/* SYNOPSIS
/* #include "smtp.h"
/*
-/* SMTP_SESSION *smtp_session_alloc(stream, dest, host, addr)
+/* SMTP_SESSION *smtp_session_alloc(stream, dest, host, addr, port, flags)
/* VSTREAM *stream;
/* char *dest;
/* char *host;
/* char *addr;
+/* unsigned port;
+/* int flags;
/*
/* void smtp_session_free(session)
/* SMTP_SESSION *session;
+/*
+/* int smtp_session_passivate(session, dest_prop, endp_prop)
+/* SMTP_SESSION *session;
+/* VSTRING *dest_prop;
+/* VSTRING *endp_prop;
+/*
+/* SMTP_SESSION *smtp_session_activate(fd, dest_prop, endp_prop)
+/* int fd;
+/* VSTRING *dest_prop;
+/* VSTRING *endp_prop;
/* DESCRIPTION
/* smtp_session_alloc() allocates memory for an SMTP_SESSION structure
/* and initializes it with the given stream and destination, host name
/* and address information. The host name and address strings are
-/* copied. The code assumes that the stream is connected to the "best"
-/* alternative.
+/* copied. The port is in network byte order.
/*
/* smtp_session_free() destroys an SMTP_SESSION structure and its
-/* members, making memory available for reuse.
+/* members, making memory available for reuse. It will handle the
+/* case of a null stream and will assume it was given a different
+/* purpose.
+/*
+/* smtp_session_passivate() flattens an SMTP session so that
+/* it can be cached. The SMTP_SESSION structure is destroyed.
+/*
+/* smtp_session_activate() inflates a flattened SMTP session
+/* so that it can be used. The input is modified.
+/*
+/* Arguments:
+/* .IP stream
+/* A full-duplex stream.
+/* .IP dest
+/* The unmodified next-hop or fall-back destination including
+/* the optional [] and including the optional port or service.
+/* .IP host
+/* The name of the host that we are connected to.
+/* .IP addr
+/* The address of the host that we are connected to.
+/* .IP port
+/* The remote port, network byte order.
+/* .IP flags
+/* Zero or more of the following:
+/* .RS
+/* .IP SMTP_SESS_FLAG_CACHE
+/* Enable session caching.
+/* .RE
+/* .IP
+/* The manifest constant SMTP_SESS_FLAG_NONE requests no options.
+/* .IP dest_prop
+/* Destination specific session properties: the server is the
+/* best MX host for the current logical destination.
+/* .IP endp_prop
+/* Endpoint specific session properties: all the features
+/* advertised by the remote server.
/* LICENSE
/* .ad
/* .fi
/* System library. */
#include <sys_defs.h>
+#include <stdlib.h>
/* Utility library. */
+#include <msg.h>
#include <mymalloc.h>
#include <vstream.h>
#include <stringops.h>
/* Global library. */
#include <mime_state.h>
+#include <debug_peer.h>
+#include <mail_params.h>
/* Application-specific. */
#include "smtp.h"
+#define STR(x) vstring_str(x)
+
+/*#define msg_verbose 1*/
+
/* smtp_session_alloc - allocate and initialize SMTP_SESSION structure */
-SMTP_SESSION *smtp_session_alloc(VSTREAM *stream, char *dest, char *host,
- char *addr)
+SMTP_SESSION *smtp_session_alloc(VSTREAM *stream, const char *dest,
+ const char *host, const char *addr,
+ unsigned port, int flags)
{
SMTP_SESSION *session;
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;
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);
}
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);
+}
#include <mymalloc.h>
#include <vstring.h>
-#include <vstream.h>
/* Global library. */
-#include <mail_conf.h>
-#include <mime_state.h>
+#include <mail_params.h>
/* Application-specific. */
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);
}
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);
}
* 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.
/*
* Don't attempt to cache this session.
*/
- session->features &= ~SMTP_FEATURE_CACHE_SESSION;
+ session->reuse_count = 0;
/*
* Cleanup.
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
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
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
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
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 \
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 \
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
$(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
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
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
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);
}
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.
#ifdef TEST
/*
- * Proof-of-concept test program for the event manager. Print "dingdong"
- * every 5 seconds, while echoing any lines read from stdin. The "dingdong
- * changes case each time it is printed.
+ * Proof-of-concept test program for the event manager. Schedule a series
+of events at one-second intervals and let them happen,
+ while echoing any lines read from stdin.
*/
#include <stdio.h>
#include <ctype.h>
-#define DELAY 5
+/* timer_event - display event */
-/* dingdong - print text every DELAY seconds */
-
-static void dingdong(char *context)
+static void timer_event(int unused_event, char *context)
{
- printf("%c", *context);
+ printf("%ld: %s\n", (long) event_present, context);
fflush(stdout);
- *context = (ISUPPER(*context) ? TOLOWER(*context) : TOUPPER(*context));
- event_request_timer(dingdong, context, (char *) DELAY);
}
/* echo - echo text received on stdin */
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);
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
#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
--- /dev/null
+/*++
+/* NAME
+/* stream_recv_fd 3
+/* SUMMARY
+/* receive file descriptor
+/* SYNOPSIS
+/* #include <iostuff.h>
+/*
+/* int stream_recv_fd(fd)
+/* int fd;
+/* DESCRIPTION
+/* stream_recv_fd() receives a file descriptor via the specified
+/* stream. The result value is the received descriptor.
+/*
+/* Arguments:
+/* .IP fd
+/* File descriptor.
+/* DIAGNOSTICS
+/* stream_recv_fd() returns -1 upon failure.
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h> /* includes <sys/types.h> */
+
+#ifdef STREAM_CONNECTIONS
+
+#include <sys/stat.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stropts.h>
+#include <fcntl.h>
+
+#endif
+
+/* Utility library. */
+
+#include <msg.h>
+#include <iostuff.h>
+
+/* stream_recv_fd - receive file descriptor */
+
+int stream_recv_fd(int fd)
+{
+#ifdef STREAM_CONNECTIONS
+ struct strrecvfd fdinfo;
+
+ /*
+ * This will return EAGAIN on a non-blocking stream when someone else
+ * snatched the connection from us.
+ */
+ if (ioctl(fd, I_RECVFD, &fdinfo) < 0)
+ return (-1);
+ return (fdinfo.fd);
+#else
+ msg_fatal("stream connections are not implemented");
+#endif
+}
+
+#ifdef TEST
+
+ /*
+ * Proof-of-concept program. Receive a descriptor (presumably from the
+ * stream_send_fd test program) and copy its content until EOF.
+ */
+#include <unistd.h>
+#include <split_at.h>
+#include <listen.h>
+
+int main(int argc, char **argv)
+{
+ char *transport;
+ char *endpoint;
+ int listen_sock;
+ int client_sock;
+ int client_fd;
+ ssize_t read_count;
+ char buf[1024];
+
+ if (argc != 2
+ || (endpoint = split_at(transport = argv[1], ':')) == 0
+ || *endpoint == 0 || *transport == 0)
+ msg_fatal("usage: %s transport:endpoint", argv[0]);
+
+ if (strcmp(transport, "stream") == 0) {
+ listen_sock = stream_listen(endpoint, BLOCKING, 0);
+ } else {
+ msg_fatal("invalid transport name: %s", transport);
+ }
+ if (listen_sock < 0)
+ msg_fatal("listen %s:%s: %m", transport, endpoint);
+
+ client_sock = stream_accept(listen_sock);
+ if (client_sock < 0)
+ msg_fatal("stream_accept: %m");
+
+ while ((client_fd = stream_recv_fd(client_sock)) >= 0) {
+ msg_info("client_fd = %d", client_fd);
+ while ((read_count = read(client_fd, buf, sizeof(buf))) > 0)
+ write(1, buf, read_count);
+ if (read_count < 0)
+ msg_fatal("read: %m");
+ if (close(client_fd) != 0)
+ msg_fatal("close(%d): %m", client_fd);
+ }
+ exit(0);
+}
+
+#endif
--- /dev/null
+/*++
+/* NAME
+/* stream_send_fd 3
+/* SUMMARY
+/* send file descriptor
+/* SYNOPSIS
+/* #include <iostuff.h>
+/*
+/* int stream_send_fd(fd, sendfd)
+/* int fd;
+/* int sendfd;
+/* DESCRIPTION
+/* stream_send_fd() sends a file descriptor over the specified
+/* stream.
+/*
+/* Arguments:
+/* .IP fd
+/* File descriptor.
+/* .IP sendfd
+/* Another file descriptor.
+/* DIAGNOSTICS
+/* stream_send_fd() returns -1 upon failure.
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h> /* includes <sys/types.h> */
+
+#ifdef STREAM_CONNECTIONS
+
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <stropts.h>
+
+#endif
+
+/* Utility library. */
+
+#include <msg.h>
+#include <iostuff.h>
+
+/* stream_send_fd - send file descriptor */
+
+int stream_send_fd(int fd, int sendfd)
+{
+ char *myname = "stream_send_fd";
+
+#ifdef STREAM_CONNECTIONS
+ if (ioctl(fd, I_SENDFD, sendfd) < 0)
+ msg_fatal("%s: send file descriptor: %m", myname);
+#else
+ msg_fatal("stream connections are not implemented");
+#endif
+}
+
+#ifdef TEST
+
+ /*
+ * Proof-of-concept program. Open a file and send the descriptor, presumably
+ * to the stream_recv_fd test program.
+ */
+#include <unistd.h>
+#include <fcntl.h>
+#include <split_at.h>
+#include <connect.h>
+
+int main(int argc, char **argv)
+{
+ char *transport;
+ char *endpoint;
+ char *path;
+ int server_sock;
+ int client_fd;
+
+ if (argc < 3
+ || (endpoint = split_at(transport = argv[1], ':')) == 0
+ || *endpoint == 0 || *transport == 0)
+ msg_fatal("usage: %s transport:endpoint file...", argv[0]);
+
+ if (strcmp(transport, "stream") == 0) {
+ server_sock = stream_connect(endpoint, BLOCKING, 0);
+ } else {
+ msg_fatal("invalid transport name: %s", transport);
+ }
+ if (server_sock < 0)
+ msg_fatal("connect %s:%s: %m", transport, endpoint);
+
+ argv += 2;
+ while ((path = *argv++) != 0) {
+ if ((client_fd = open(path, O_RDONLY, 0)) < 0)
+ msg_fatal("open %s: %m", path);
+ msg_info("path=%s client_fd=%d", path, client_fd);
+ if (stream_send_fd(server_sock, client_fd) < 0)
+ msg_fatal("send file descriptor: %m");
+ if (close(client_fd) != 0)
+ msg_fatal("close(%d): %m");
+ }
+ exit(0);
+}
+
+#endif
#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
#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) \
--- /dev/null
+/*++
+/* NAME
+/* unix_recv_fd 3
+/* SUMMARY
+/* receive file descriptor
+/* SYNOPSIS
+/* #include <iostuff.h>
+/*
+/* int unix_recv_fd(fd)
+/* int fd;
+/* DESCRIPTION
+/* unix_recv_fd() receives a file descriptor via the specified
+/* UNIX-domain socket. The result value is the received descriptor.
+/*
+/* Arguments:
+/* .IP fd
+/* File descriptor.
+/* DIAGNOSTICS
+/* unix_recv_fd() returns -1 upon failure.
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h> /* includes <sys/types.h> */
+#include <sys/socket.h>
+#include <sys/uio.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <iostuff.h>
+
+/* unix_recv_fd - receive file descriptor */
+
+int unix_recv_fd(int fd)
+{
+ char *myname = "unix_recv_fd";
+ struct msghdr msg;
+ int newfd;
+ struct iovec iov[1];
+ char buf[1];
+
+ /*
+ * Adapted from: W. Richard Stevens, UNIX Network Programming, Volume 1,
+ * Second edition.
+ */
+#if defined(CMSG_SPACE) && !defined(NO_MSGHDR_MSG_CONTROL)
+ union {
+ struct msghdr just_for_alignment;
+ char control[CMSG_SPACE(sizeof(newfd))];
+ } control_un;
+ struct cmsghdr *cmptr;
+
+ msg.msg_control = control_un.control;
+ msg.msg_controllen = sizeof(control_un.control);
+#else
+ msg.msg_accrights = (char *) &newfd;
+ msg.msg_accrightslen = sizeof(newfd);
+#endif
+
+ msg.msg_name = 0;
+ msg.msg_namelen = 0;
+
+ /*
+ * XXX We don't want to pass any data, just a file descriptor. However,
+ * setting msg.msg_iov = 0 and msg.msg_iovlen = 0 causes trouble: we need
+ * to read_wait() before we can receive the descriptor, and the code
+ * fails after the first descriptor when we attempt to receive a sequence
+ * of descriptors.
+ */
+ iov->iov_base = buf;
+ iov->iov_len = sizeof(buf);
+ msg.msg_iov = iov;
+ msg.msg_iovlen = 1;
+
+ if (recvmsg(fd, &msg, 0) < 0)
+ return (-1);
+
+#if defined(CMSG_SPACE) && !defined(NO_MSGHDR_MSG_CONTROL)
+ if ((cmptr = CMSG_FIRSTHDR(&msg)) != 0
+ && cmptr->cmsg_len == CMSG_LEN(sizeof(newfd))) {
+ if (cmptr->cmsg_level != SOL_SOCKET)
+ msg_fatal("%s: control level %d != SOL_SOCKET",
+ myname, cmptr->cmsg_level);
+ if (cmptr->cmsg_type != SCM_RIGHTS)
+ msg_fatal("%s: control type %d != SCM_RIGHTS",
+ myname, cmptr->cmsg_type);
+ return (*(int *) CMSG_DATA(cmptr));
+ } else
+ return (-1);
+#else
+ if (msg.msg_accrightslen == sizeof(newfd))
+ return (newfd);
+ else
+ return (-1);
+#endif
+}
+
+#ifdef TEST
+
+ /*
+ * Proof-of-concept program. Receive a descriptor (presumably from the
+ * unix_send_fd test program) and copy its content until EOF.
+ */
+#include <unistd.h>
+#include <split_at.h>
+#include <listen.h>
+
+int main(int argc, char **argv)
+{
+ char *transport;
+ char *endpoint;
+ int listen_sock;
+ int client_sock;
+ int client_fd;
+ ssize_t read_count;
+ char buf[1024];
+
+ if (argc != 2
+ || (endpoint = split_at(transport = argv[1], ':')) == 0
+ || *endpoint == 0 || *transport == 0)
+ msg_fatal("usage: %s transport:endpoint", argv[0]);
+
+ if (strcmp(transport, "unix") == 0) {
+ listen_sock = unix_listen(endpoint, 10, BLOCKING);
+ } else {
+ msg_fatal("invalid transport name: %s", transport);
+ }
+ if (listen_sock < 0)
+ msg_fatal("listen %s:%s: %m", transport, endpoint);
+
+ client_sock = accept(listen_sock, (struct sockaddr *) 0, (SOCKADDR_SIZE) 0);
+ if (client_sock < 0)
+ msg_fatal("accept: %m");
+
+ while ((client_fd = unix_recv_fd(client_sock)) >= 0) {
+ msg_info("client_fd = %d", client_fd);
+ while ((read_count = read(client_fd, buf, sizeof(buf))) > 0)
+ write(1, buf, read_count);
+ if (read_count < 0)
+ msg_fatal("read: %m");
+ close(client_fd);
+ }
+ exit(0);
+}
+
+#endif
--- /dev/null
+/*++
+/* NAME
+/* unix_send_fd 3
+/* SUMMARY
+/* send file descriptor
+/* SYNOPSIS
+/* #include <iostuff.h>
+/*
+/* int unix_send_fd(fd, sendfd)
+/* int fd;
+/* int sendfd;
+/* DESCRIPTION
+/* unix_send_fd() sends a file descriptor over the specified
+/* UNIX-domain socket.
+/*
+/* Arguments:
+/* .IP fd
+/* File descriptor.
+/* .IP sendfd
+/* Another file descriptor.
+/* DIAGNOSTICS
+/* unix_send_fd() returns -1 upon failure.
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h> /* includes <sys/types.h> */
+#include <sys/socket.h>
+#include <sys/uio.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <iostuff.h>
+
+/* unix_send_fd - send file descriptor */
+
+int unix_send_fd(int fd, int sendfd)
+{
+ struct msghdr msg;
+ struct iovec iov[1];
+
+ /*
+ * Adapted from: W. Richard Stevens, UNIX Network Programming, Volume 1,
+ * Second edition.
+ */
+#if defined(CMSG_SPACE) && !defined(NO_MSGHDR_MSG_CONTROL)
+ union {
+ struct msghdr just_for_alignment;
+ char control[CMSG_SPACE(sizeof(sendfd))];
+ } control_un;
+ struct cmsghdr *cmptr;
+
+ msg.msg_control = control_un.control;
+ msg.msg_controllen = sizeof(control_un.control);
+
+ cmptr = CMSG_FIRSTHDR(&msg);
+ cmptr->cmsg_len = CMSG_LEN(sizeof(sendfd));
+ cmptr->cmsg_level = SOL_SOCKET;
+ cmptr->cmsg_type = SCM_RIGHTS;
+ *(int *) CMSG_DATA(cmptr) = sendfd;
+#else
+ msg.msg_accrights = (char *) &sendfd;
+ msg.msg_accrightslen = sizeof(sendfd);
+#endif
+
+ msg.msg_name = 0;
+ msg.msg_namelen = 0;
+
+ /*
+ * XXX We don't want to pass any data, just a file descriptor. However,
+ * setting msg.msg_iov = 0 and msg.msg_iovlen = 0 causes trouble. See the
+ * comments in the unix_recv_fd() routine.
+ */
+ iov->iov_base = "";
+ iov->iov_len = 1;
+ msg.msg_iov = iov;
+ msg.msg_iovlen = 1;
+
+ return (sendmsg(fd, &msg, 0));
+}
+
+#ifdef TEST
+
+ /*
+ * Proof-of-concept program. Open a file and send the descriptor, presumably
+ * to the unix_recv_fd test program.
+ */
+#include <unistd.h>
+#include <fcntl.h>
+#include <split_at.h>
+#include <connect.h>
+
+int main(int argc, char **argv)
+{
+ char *transport;
+ char *endpoint;
+ char *path;
+ int server_sock;
+ int client_fd;
+
+ if (argc < 3
+ || (endpoint = split_at(transport = argv[1], ':')) == 0
+ || *endpoint == 0 || *transport == 0)
+ msg_fatal("usage: %s transport:endpoint file...", argv[0]);
+
+ if (strcmp(transport, "unix") == 0) {
+ server_sock = unix_connect(endpoint, BLOCKING, 0);
+ } else {
+ msg_fatal("invalid transport name: %s", transport);
+ }
+ if (server_sock < 0)
+ msg_fatal("connect %s:%s: %m", transport, endpoint);
+
+ argv += 2;
+ while ((path = *argv++) != 0) {
+ if ((client_fd = open(path, O_RDONLY, 0)) < 0)
+ msg_fatal("open %s: %m", path);
+ msg_info("path=%s fd=%d", path, client_fd);
+ if (unix_send_fd(server_sock, client_fd) < 0)
+ msg_fatal("send file descriptor: %m");
+ if (close(client_fd) != 0)
+ msg_fatal("close(%d): %m", client_fd);
+ }
+ exit(0);
+}
+
+#endif
/* int vstream_fclose(stream)
/* VSTREAM *stream;
/*
+/* int vstream_fdclose(stream)
+/* VSTREAM *stream;
+/*
/* VSTREAM *vstream_printf(format, ...)
/* char *format;
/*
/* vstream_fclose() closes the named buffered stream. The result
/* is 0 in case of success, VSTREAM_EOF in case of problems.
/*
+/* vstream_fdclose() leaves the file(s) open but is otherwise
+/* identical to vstream_fclose().
+/*
/* vstream_fprintf() formats its arguments according to the
/* \fIformat\fR argument and writes the result to the named stream.
/* The result is the stream argument. It understands the s, c, d, u,
*/
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);
}
*/
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))
{
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)
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,...)
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))
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
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