reading an entire command, instead of a time limit for
reading individual characters. File: postscreen/postscreen_smtpd.c.
+20101023
+
+ Cleanup: don't apply reject_rhsbl_helo to non-domain forms
+ such as network addresses. This would cause false positives
+ with dbl.spamhaus.org. File: smtpd/smtpd_check.c.
+
20101117
Bugfix: the "421" reply after Milter error was overruled
as permitted by RFC. Solution by Victor Duchovni. File:
smtpd/smtpd.c.
-20101023
-
- Cleanup: don't apply reject_rhsbl_helo to non-domain forms
- such as network addresses. This would cause false positives
- with dbl.spamhaus.org. File: smtpd/smtpd_check.c.
-
20101124-6
Feature: pattern matching for DNSWL/DNSBL responses. For
20110104
Feature: add contact information to each SMTP server reject
- message. For example, "smtpd_reject_footer =
- call 800-555-0101 for assistance", with macro expansion and
- with multi-line support. Files: global/mail_params.h,
- mantools/postlink, proto/postconf.proto, smtpd/smtpd.c,
- smtpd/smtpd_chat.c, smtpd/smtpd_expand.[hc], util/mac_expand.[hc].
+ message. For example, "smtpd_reject_footer = call 800-555-0101
+ for assistance", with macro expansion and with multi-line
+ support. Files: global/mail_params.h, mantools/postlink,
+ proto/postconf.proto, smtpd/smtpd.c, smtpd/smtpd_chat.c,
+ smtpd/smtpd_expand.[hc], util/mac_expand.[hc].
20110105
verify server certificates. Files: smtpd/smtpd.c,
smtp/smtp_proto.c.
-20110106:
+20110106
Non-production: cleaned up the tlsproxy support in the
Postfix SMTP server for stress testing of the tlsproxy
compatibility with smtpd_command_filter. Files:
postscreen/postscreen_dict.c, postscreen/postscreen_smtpd.c
+20110108
+
Cleanup: postscreen(8) now displays control characters in
PREGREET responses as C-style \letter escapes, instead of
"?". File: postscreen/postscreen_early.c.
+
+20110109
+
+ Cleanup: Solaris support for "pass" (file descriptor passing
+ based) services in master.cf. This was needed by postscreen(8).
+ Also, renamed upass_xxx.c to unix_pass_xxx.c. One-character
+ prefixes are too short. Removed upass_connect.c because it
+ was useless code. Files: util/stream_pass_connect.c,
+ util/unix_pass_listen.c, util/unix_pass_trigger.c.
+
+ Cleanup: on Solaris the Postfix event engine was deaf for
+ SIGHUP and SIGALRM (and probably more) signals after the
+ switch to /dev/poll. Symptoms were delayed "postfix reload"
+ response, and killed processes when the watchdog timeout
+ was less than max_idle. The fix is to set up SIGHUP and
+ SIGALRM handlers that write to a pipe, and to monitor that
+ pipe for read events via the Postfix event engine. Files:
+ master/master_sig.c, util/watchdog.c.
I\bIn\bnt\btr\bro\bod\bdu\buc\bct\bti\bio\bon\bn
The Postfix postscreen(8) server performs triage on multiple inbound SMTP
-connections in parallel. While a single postscreen(8) process keeps zombies
-away from Postfix SMTP server processes, more Postfix SMTP server processes
-remain available for legitimate clients.
+connections at the same time. While a single postscreen(8) process keeps
+zombies away from Postfix SMTP server processes, more Postfix SMTP server
+processes remain available for legitimate clients.
+
+postscreen(8) should not be used on SMTP ports that receive mail from end-user
+clients (MUAs). In a typical deployment, postscreen(8) is used on the "port 25"
+service, while MUA clients submit mail via the submission service.
postscreen(8) is the first layer in a multi-layer defense.
This feature supports macro expansion ($client_address, $localtime,
etc.), as documented in the postconf(5) manpage.
-This feature is also supported as postscreen_reject_footer
-using the same setting as smtpd_reject_footer by
-default.
+This feature is also supported as postscreen_reject_footer using
+the same setting as smtpd_reject_footer by default.
Incompatibility with snapshot 20110102
======================================
For backwards compatibility, specify:
/etc/postfix/main.cf
- To: undisclosed-recipients:;
+ undisclosed_recipients_header = To: undisclosed-recipients:;
Note: both the ":" and ";" are required.
Wish list:
- Remove this file from the stable release.
-
- Re-run "make depend" with all plugins enabled.
+ Things to do before the stable release:
- Make very clear in postscreen manpage and readme that this
- program is not to be used against your own mail clients.
-
- Test postscreen on Solaris.
+ Remove this file from the stable release.
- match_list support that does not recompile CIDR patterns
- on every call.
+ To speed up postscreen_whitelist/blacklist_networks lookups
+ invent match_list support that does not recompile CIDR
+ patterns on every call. Otherwise, large CIDR files will
+ make postscreen unacceptably slow.
+
+ Better, replace the postscreen_whitelist/blacklist_networks
+ mechanisms with one single postscreen_client_restrictions
+ mechanism that understands "permit_mynetworks" and (cidr)
+ access maps with entries of "pattern permit", "pattern
+ reject", or "pattern dunno". Tables that require locking
+ should not be used for performance reasons. The new mechanism
+ can reduce the number of table lookups when sites have both
+ whitelists and blacklists. We then use "permit_mynetworks"
+ to whitelist local networks by default. Unlike match_lists,
+ PCRE maps in access tables are compiled once, and even
+ Stan's very large tables resolve in a millisecond or so on
+ modern hardware.
+
+ Things to do after the stable release:
tlsproxy(8) should receive TLS preferences from postscreen(8)
and smtpd(8), instead of reading them from main.cf. This
means that many tlsproxy_ parameters become postscreen_
- parameters. This also means that tls_server_init() must not
- "validate" preferences that are supplied later at
- tls_server_start() time, which was dubious design anyway.
+ parameters, and that tls_server_init() parameters move to
+ to tls_server_start(). That is a significant API change.
anvil rate limit for sasl_username.
grep '^#*smtp.*postscreen' $config_directory/master.cf >/dev/null || {
echo Editing $config_directory/master.cf, adding missing entry for postscreen TCP service
cat >>$config_directory/master.cf <<EOF || exit 1
-#smtp inet n - n - 1 postscreen
+#smtp inet n - n - 1 postscreen
EOF
}
grep '^#*dnsblog.*dnsblog' $config_directory/master.cf >/dev/null || {
echo Editing $config_directory/master.cf, adding missing entry for dnsblog unix-domain service
cat >>$config_directory/master.cf <<EOF || exit 1
-#dnsblog unix - - n - 0 dnsblog
+#dnsblog unix - - n - 0 dnsblog
EOF
}
grep '^#*tlsproxy.*tlsproxy' $config_directory/master.cf >/dev/null || {
echo Editing $config_directory/master.cf, adding missing entry for tlsproxy unix-domain service
cat >>$config_directory/master.cf <<EOF || exit 1
-#tlsproxy unix - - n - 0 tlsproxy
+#tlsproxy unix - - n - 0 tlsproxy
EOF
}
<h2> <a name="intro">Introduction</a> </h2>
<p> The Postfix <a href="postscreen.8.html">postscreen(8)</a> server performs triage on multiple
-inbound SMTP connections in parallel. While a single <a href="postscreen.8.html">postscreen(8)</a>
-process keeps zombies away from Postfix SMTP server processes,
-more Postfix SMTP server processes remain available for legitimate
+inbound SMTP connections at the same time. While a single <a href="postscreen.8.html">postscreen(8)</a>
+process keeps zombies away from Postfix SMTP server processes, more
+Postfix SMTP server processes remain available for legitimate
clients. </p>
+<p> <a href="postscreen.8.html">postscreen(8)</a> should not be used on SMTP ports that receive
+mail from end-user clients (MUAs). In a typical deployment,
+<a href="postscreen.8.html">postscreen(8)</a> is used on the "port 25" service, while MUA clients
+submit mail via the submission service. </p>
+
<p> <a href="postscreen.8.html">postscreen(8)</a> is the first layer in a multi-layer defense. <p>
<ul>
<b>DESCRIPTION</b>
The Postfix <a href="postscreen.8.html"><b>postscreen</b>(8)</a> server performs triage on multi-
- ple inbound SMTP connections in parallel. While a single
- <a href="postscreen.8.html"><b>postscreen</b>(8)</a> process keeps spambots away from Postfix
- SMTP server processes, more Postfix SMTP server processes
- remain available for legitimate clients.
+ ple inbound SMTP connections at the same time. While a
+ single <a href="postscreen.8.html"><b>postscreen</b>(8)</a> process keeps spambots away from
+ Postfix SMTP server processes, more Postfix SMTP server
+ processes remain available for legitimate clients.
+
+ This program should not be used on SMTP ports that receive
+ mail from end-user clients (MUAs). In a typical deploy-
+ ment, <a href="postscreen.8.html"><b>postscreen</b>(8)</a> is used on the "port 25" service,
+ while MUA clients submit mail via the <b>submission</b> service.
<a href="postscreen.8.html"><b>postscreen</b>(8)</a> maintains a temporary whitelist for clients
that have passed a number of tests. When an SMTP client
.ad
.fi
The Postfix \fBpostscreen\fR(8) server performs triage on
-multiple inbound SMTP connections in parallel. While a
-single \fBpostscreen\fR(8) process keeps spambots away from
-Postfix SMTP server processes, more Postfix SMTP server
+multiple inbound SMTP connections at the same time. While
+a single \fBpostscreen\fR(8) process keeps spambots away
+from Postfix SMTP server processes, more Postfix SMTP server
processes remain available for legitimate clients.
+This program should not be used on SMTP ports that receive
+mail from end-user clients (MUAs). In a typical deployment,
+\fBpostscreen\fR(8) is used on the "port 25" service, while
+MUA clients submit mail via the \fBsubmission\fR service.
+
\fBpostscreen\fR(8) maintains a temporary whitelist for
clients that have passed a number of tests. When an SMTP
client IP address is whitelisted, \fBpostscreen\fR(8) hands
<h2> <a name="intro">Introduction</a> </h2>
<p> The Postfix postscreen(8) server performs triage on multiple
-inbound SMTP connections in parallel. While a single postscreen(8)
-process keeps zombies away from Postfix SMTP server processes,
-more Postfix SMTP server processes remain available for legitimate
+inbound SMTP connections at the same time. While a single postscreen(8)
+process keeps zombies away from Postfix SMTP server processes, more
+Postfix SMTP server processes remain available for legitimate
clients. </p>
+<p> postscreen(8) should not be used on SMTP ports that receive
+mail from end-user clients (MUAs). In a typical deployment,
+postscreen(8) is used on the "port 25" service, while MUA clients
+submit mail via the submission service. </p>
+
<p> postscreen(8) is the first layer in a multi-layer defense. <p>
<ul>
delivered_hdr.o: quote_flags.h
delivered_hdr.o: rec_type.h
delivered_hdr.o: record.h
+dict_ldap.o: ../../include/argv.h
+dict_ldap.o: ../../include/binhash.h
+dict_ldap.o: ../../include/dict.h
+dict_ldap.o: ../../include/match_list.h
+dict_ldap.o: ../../include/match_ops.h
+dict_ldap.o: ../../include/msg.h
+dict_ldap.o: ../../include/mymalloc.h
+dict_ldap.o: ../../include/name_code.h
+dict_ldap.o: ../../include/stringops.h
dict_ldap.o: ../../include/sys_defs.h
+dict_ldap.o: ../../include/vbuf.h
+dict_ldap.o: ../../include/vstream.h
+dict_ldap.o: ../../include/vstring.h
+dict_ldap.o: cfg_parser.h
+dict_ldap.o: db_common.h
dict_ldap.o: dict_ldap.c
+dict_ldap.o: dict_ldap.h
+dict_ldap.o: mail_conf.h
+dict_ldap.o: string_list.h
+dict_mysql.o: ../../include/argv.h
+dict_mysql.o: ../../include/dict.h
+dict_mysql.o: ../../include/events.h
+dict_mysql.o: ../../include/find_inet.h
+dict_mysql.o: ../../include/match_list.h
+dict_mysql.o: ../../include/match_ops.h
+dict_mysql.o: ../../include/msg.h
+dict_mysql.o: ../../include/mymalloc.h
+dict_mysql.o: ../../include/myrand.h
+dict_mysql.o: ../../include/split_at.h
+dict_mysql.o: ../../include/stringops.h
dict_mysql.o: ../../include/sys_defs.h
+dict_mysql.o: ../../include/vbuf.h
+dict_mysql.o: ../../include/vstream.h
+dict_mysql.o: ../../include/vstring.h
+dict_mysql.o: cfg_parser.h
+dict_mysql.o: db_common.h
dict_mysql.o: dict_mysql.c
+dict_mysql.o: dict_mysql.h
+dict_mysql.o: string_list.h
+dict_pgsql.o: ../../include/argv.h
+dict_pgsql.o: ../../include/dict.h
+dict_pgsql.o: ../../include/events.h
+dict_pgsql.o: ../../include/find_inet.h
+dict_pgsql.o: ../../include/match_list.h
+dict_pgsql.o: ../../include/match_ops.h
+dict_pgsql.o: ../../include/msg.h
+dict_pgsql.o: ../../include/mymalloc.h
+dict_pgsql.o: ../../include/myrand.h
+dict_pgsql.o: ../../include/split_at.h
+dict_pgsql.o: ../../include/stringops.h
dict_pgsql.o: ../../include/sys_defs.h
+dict_pgsql.o: ../../include/vbuf.h
+dict_pgsql.o: ../../include/vstream.h
+dict_pgsql.o: ../../include/vstring.h
+dict_pgsql.o: cfg_parser.h
+dict_pgsql.o: db_common.h
dict_pgsql.o: dict_pgsql.c
+dict_pgsql.o: dict_pgsql.h
+dict_pgsql.o: string_list.h
dict_proxy.o: ../../include/argv.h
dict_proxy.o: ../../include/attr.h
dict_proxy.o: ../../include/dict.h
* Patches change both the patchlevel and the release date. Snapshots have no
* patchlevel; they change the release date only.
*/
-#define MAIL_RELEASE_DATE "20110108"
+#define MAIL_RELEASE_DATE "20110109"
#define MAIL_VERSION_NUMBER "2.8"
#ifdef SNAPSHOT
#ifdef USE_SIG_RETURN
#include <sys/syscall.h>
-#endif
-
-#ifndef USE_SIG_RETURN
+#undef USE_SIG_PIPE
+#else
#define USE_SIG_PIPE
#endif
int master_gotsigchld;
int master_gotsighup;
+#ifdef USE_SIG_RETURN
+
/* master_sighup - register arrival of hangup signal */
static void master_sighup(int sig)
/* master_sigchld - register arrival of child death signal */
-#ifdef USE_SIG_RETURN
-
static void master_sigchld(int sig, int code, struct sigcontext * scp)
{
#else
-#ifdef USE_SIG_PIPE
+/* master_sighup - register arrival of hangup signal */
+
+static void master_sighup(int sig)
+{
+ int saved_errno = errno;
+
+ /*
+ * WARNING WARNING WARNING.
+ *
+ * This code runs at unpredictable moments, as a signal handler. Don't put
+ * any code here other than for setting a global flag, or code that is
+ * intended to be run within a signal handler. Restore errno in case we
+ * are interrupting the epilog of a failed system call.
+ */
+ master_gotsighup = sig;
+ if (write(SIG_PIPE_WRITE_FD, "", 1) != 1)
+ msg_warn("write to SIG_PIPE_WRITE_FD failed: %m");
+ errno = saved_errno;
+}
/* master_sigchld - force wakeup from select() */
* intended to be run within a signal handler. Restore errno in case we
* are interrupting the epilog of a failed system call.
*/
+ master_gotsigchld = 1;
if (write(SIG_PIPE_WRITE_FD, "", 1) != 1)
msg_warn("write to SIG_PIPE_WRITE_FD failed: %m");
errno = saved_errno;
while (read(SIG_PIPE_READ_FD, c, 1) > 0)
/* void */ ;
- master_gotsigchld = 1;
}
-#else
-
-static void master_sigchld(int sig)
-{
-
- /*
- * WARNING WARNING WARNING.
- *
- * This code runs at unpredictable moments, as a signal handler. Don't put
- * any code here other than for setting a global flag.
- */
- master_gotsigchld = sig;
-}
-
-#endif
#endif
/* master_sigdeath - die, women and children first */
/* \fBpostscreen\fR [generic Postfix daemon options]
/* DESCRIPTION
/* The Postfix \fBpostscreen\fR(8) server performs triage on
-/* multiple inbound SMTP connections in parallel. While a
-/* single \fBpostscreen\fR(8) process keeps spambots away from
-/* Postfix SMTP server processes, more Postfix SMTP server
+/* multiple inbound SMTP connections at the same time. While
+/* a single \fBpostscreen\fR(8) process keeps spambots away
+/* from Postfix SMTP server processes, more Postfix SMTP server
/* processes remain available for legitimate clients.
/*
+/* This program should not be used on SMTP ports that receive
+/* mail from end-user clients (MUAs). In a typical deployment,
+/* \fBpostscreen\fR(8) is used on the "port 25" service, while
+/* MUA clients submit mail via the \fBsubmission\fR service.
+/*
/* \fBpostscreen\fR(8) maintains a temporary whitelist for
/* clients that have passed a number of tests. When an SMTP
/* client IP address is whitelisted, \fBpostscreen\fR(8) hands
* Postfix-specific.
*/
if ((server_fd =
- LOCAL_CONNECT(psc_smtpd_service_name, NON_BLOCKING,
- PSC_SEND_SOCK_CONNECT_TIMEOUT)) < 0) {
+ PASS_CONNECT(psc_smtpd_service_name, NON_BLOCKING,
+ PSC_SEND_SOCK_CONNECT_TIMEOUT)) < 0) {
msg_warn("cannot connect to service %s: %m", psc_smtpd_service_name);
PSC_SEND_REPLY(state, "421 4.3.2 All server ports are busy\r\n");
psc_free_session_state(state);
username.c valid_hostname.c vbuf.c vbuf_print.c vstream.c \
vstream_popen.c vstring.c vstring_vstream.c watchdog.c writable.c \
write_buf.c write_wait.c sane_basename.c format_tv.c allspace.c \
- allascii.c load_file.c killme_after.c vstream_tweak.c upass_connect.c \
- upass_listen.c upass_trigger.c edit_file.c inet_windowsize.c \
+ allascii.c load_file.c killme_after.c vstream_tweak.c \
+ unix_pass_listen.c unix_pass_trigger.c edit_file.c inet_windowsize.c \
unix_pass_fd_fix.c dict_cache.c valid_utf_8.c dict_thash.c \
- ip_match.c nbbio.c
+ ip_match.c nbbio.c stream_pass_connect.c
OBJS = alldig.o allprint.o argv.o argv_split.o attr_clnt.o attr_print0.o \
attr_print64.o attr_print_plain.o attr_scan0.o attr_scan64.o \
attr_scan_plain.o auto_clnt.o base64_code.o basename.o binhash.o \
username.o valid_hostname.o vbuf.o vbuf_print.o vstream.o \
vstream_popen.o vstring.o vstring_vstream.o watchdog.o writable.o \
write_buf.o write_wait.o sane_basename.o format_tv.o allspace.o \
- allascii.o load_file.o killme_after.o vstream_tweak.o upass_connect.o \
- upass_listen.o upass_trigger.o edit_file.o inet_windowsize.o \
+ allascii.o load_file.o killme_after.o vstream_tweak.o \
+ unix_pass_listen.o unix_pass_trigger.o edit_file.o inet_windowsize.o \
unix_pass_fd_fix.o dict_cache.o valid_utf_8.o dict_thash.o \
- ip_match.o nbbio.o
+ ip_match.o nbbio.o stream_pass_connect.o
HDRS = argv.h attr.h attr_clnt.h auto_clnt.h base64_code.h binhash.h \
chroot_uid.h cidr_match.h clean_env.h connect.h ctable.h dict.h \
dict_cdb.h dict_cidr.h dict_db.h dict_dbm.h dict_env.h dict_ht.h \
stream_listen.o: msg.h
stream_listen.o: stream_listen.c
stream_listen.o: sys_defs.h
+stream_pass_connect.o: connect.h
+stream_pass_connect.o: iostuff.h
+stream_pass_connect.o: msg.h
+stream_pass_connect.o: stream_pass_connect.c
+stream_pass_connect.o: sys_defs.h
stream_recv_fd.o: iostuff.h
stream_recv_fd.o: msg.h
stream_recv_fd.o: stream_recv_fd.c
unix_pass_fd_fix.o: unix_pass_fd_fix.c
unix_pass_fd_fix.o: vbuf.h
unix_pass_fd_fix.o: vstring.h
+unix_pass_listen.o: iostuff.h
+unix_pass_listen.o: listen.h
+unix_pass_listen.o: msg.h
+unix_pass_listen.o: sane_accept.h
+unix_pass_listen.o: sys_defs.h
+unix_pass_listen.o: unix_pass_listen.c
+unix_pass_trigger.o: connect.h
+unix_pass_trigger.o: events.h
+unix_pass_trigger.o: iostuff.h
+unix_pass_trigger.o: msg.h
+unix_pass_trigger.o: mymalloc.h
+unix_pass_trigger.o: sys_defs.h
+unix_pass_trigger.o: trigger.h
+unix_pass_trigger.o: unix_pass_trigger.c
unix_recv_fd.o: iostuff.h
unix_recv_fd.o: msg.h
unix_recv_fd.o: sys_defs.h
unsafe.o: safe.h
unsafe.o: sys_defs.h
unsafe.o: unsafe.c
-upass_connect.o: connect.h
-upass_connect.o: events.h
-upass_connect.o: iostuff.h
-upass_connect.o: msg.h
-upass_connect.o: mymalloc.h
-upass_connect.o: sane_connect.h
-upass_connect.o: sane_socketpair.h
-upass_connect.o: sys_defs.h
-upass_connect.o: timed_connect.h
-upass_connect.o: upass_connect.c
-upass_listen.o: iostuff.h
-upass_listen.o: listen.h
-upass_listen.o: msg.h
-upass_listen.o: sane_accept.h
-upass_listen.o: sys_defs.h
-upass_listen.o: upass_listen.c
-upass_trigger.o: connect.h
-upass_trigger.o: events.h
-upass_trigger.o: iostuff.h
-upass_trigger.o: msg.h
-upass_trigger.o: mymalloc.h
-upass_trigger.o: sys_defs.h
-upass_trigger.o: trigger.h
-upass_trigger.o: upass_trigger.c
uppercase.o: stringops.h
uppercase.o: sys_defs.h
uppercase.o: uppercase.c
extern int unix_connect(const char *, int, int);
extern int inet_connect(const char *, int, int);
extern int stream_connect(const char *, int, int);
-extern int upass_connect(const char *, int, int);
+extern int stream_pass_connect(const char *, int, int);
+
+#define unix_pass_connect unix_connect
/* LICENSE
/* .ad
extern int fifo_listen(const char *, int, int);
extern int stream_listen(const char *, int, int);
-#define upass_listen(path, mode, log) unix_listen((path), (mode), (log))
+#define unix_pass_listen unix_listen
+#define stream_pass_listen stream_listen
extern int inet_accept(int);
extern int unix_accept(int);
extern int stream_accept(int);
-extern int upass_accept(int);
+extern int unix_pass_accept(int);
+
+#define stream_pass_accept stream_accept
/* LICENSE
/* .ad
* GID. Don't change the effective UID for doing this.
*/
if ((ret = stat(saved_path, &st)) < 0) {
- msg_warn("%s: stat saved_path: %m", myname);
+ msg_warn("%s: stat %s: %m", myname, saved_path);
break;
}
if (egid == -1)
egid = getegid();
if (st.st_gid != egid && (ret = chown(saved_path, -1, egid)) < 0) {
- msg_warn("%s: chgrp saved_path: %m", myname);
+ msg_warn("%s: chgrp %s: %m", myname, saved_path);
break;
}
}
--- /dev/null
+/*++
+/* NAME
+/* stream_pass_connect 3
+/* SUMMARY
+/* connect to stream-based descriptor listener
+/* SYNOPSIS
+/* #include <connect.h>
+/*
+/* int stream_pass_connect(path, block_mode, timeout)
+/* const char *path;
+/* int block_mode;
+/* int timeout;
+/* DESCRIPTION
+/* stream_pass_connect() connects to a stream-based descriptor
+/* listener for the specified pathname, and returns the resulting
+/* file descriptor. The next operation is to stream_send_fd()
+/* a file descriptor and then close() the connection once the
+/* server has received the file descriptor.
+/*
+/* Arguments:
+/* .IP path
+/* Null-terminated string with listener endpoint name.
+/* .IP block_mode
+/* Either NON_BLOCKING for a non-blocking stream, or BLOCKING for
+/* blocking mode. However, a stream connection succeeds or fails
+/* immediately.
+/* .IP timeout
+/* This argument is ignored; it is present for compatibility with
+/* other interfaces. Stream connections succeed or fail immediately.
+/* DIAGNOSTICS
+/* The result is -1 in case the connection could not be made.
+/* Fatal errors: other system call failures.
+/* 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>
+
+#ifdef STREAM_CONNECTIONS
+
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#endif
+
+/* Utility library. */
+
+#include <msg.h>
+#include <connect.h>
+
+/* stream_pass_connect - connect to stream-based descriptor listener */
+
+int stream_pass_connect(const char *path, int block_mode, int unused_timeout)
+{
+#ifdef STREAM_CONNECTIONS
+ const char *myname = "stream_pass_connect";
+ int fifo;
+
+ /*
+ * The requested file system object must exist, otherwise we can't reach
+ * the server.
+ */
+ if ((fifo = open(path, O_WRONLY | O_NONBLOCK, 0)) < 0)
+ return (-1);
+
+ /*
+ * This is for {unix,inet}_connect() compatibility.
+ */
+ non_blocking(fifo, block_mode);
+
+ return (fifo);
+#else
+ msg_fatal("stream connections are not implemented");
+#endif
+}
#define LOCAL_TRIGGER stream_trigger
#define LOCAL_SEND_FD stream_send_fd
#define LOCAL_RECV_FD stream_recv_fd
+#define PASS_CONNECT stream_pass_connect
+#define PASS_LISTEN stream_pass_listen
+#define PASS_ACCEPT stream_pass_accept
+#define PASS_TRIGGER stream_pass_trigger
#define HAS_VOLATILE_LOCKS
#define BROKEN_READ_SELECT_ON_TCP_SOCKET
#define CANT_WRITE_BEFORE_SENDING_FD
#define USE_SYSV_POLL
#ifndef NO_DEVPOLL
# define EVENTS_STYLE EVENTS_STYLE_DEVPOLL
+# define USE_WATCHDOG_PIPE
#endif
/*
#endif
#ifndef PASS_LISTEN
-#define PASS_LISTEN upass_listen
-#define PASS_ACCEPT upass_accept
-#define PASS_TRIGGER upass_trigger
+#define PASS_CONNECT unix_pass_connect
+#define PASS_LISTEN unix_pass_listen
+#define PASS_ACCEPT unix_pass_accept
+#define PASS_TRIGGER unix_pass_trigger
#endif
#if !defined (HAVE_SYS_NDIR_H) && !defined (HAVE_SYS_DIR_H) \
extern int inet_trigger(const char *, const char *, ssize_t, int);
extern int fifo_trigger(const char *, const char *, ssize_t, int);
extern int stream_trigger(const char *, const char *, ssize_t, int);
-extern int upass_trigger(const char *, const char *, ssize_t, int);
+extern int unix_pass_trigger(const char *, const char *, ssize_t, int);
+
+#define stream_pass_trigger stream_trigger
/* LICENSE
/* .ad
--- /dev/null
+/*++
+/* NAME
+/* unix_pass_listen 3
+/* SUMMARY
+/* start UNIX-domain file descriptor listener
+/* SYNOPSIS
+/* #include <listen.h>
+/*
+/* int unix_pass_listen(path, backlog, block_mode)
+/* const char *path;
+/* int backlog;
+/* int block_mode;
+/*
+/* int unix_pass_accept(fd)
+/* int fd;
+/* DESCRIPTION
+/* This module implements a listener that receives one file descriptor
+/* across each UNIX-domain connection that is made to it.
+/*
+/* unix_pass_listen() creates a listener endpoint with the specified
+/* permissions, and returns a file descriptor to be used for accepting
+/* descriptors.
+/*
+/* unix_pass_accept() accepts a descriptor.
+/*
+/* Arguments:
+/* .IP path
+/* Null-terminated string with connection destination.
+/* .IP backlog
+/* This argument exists for compatibility and is ignored.
+/* .IP block_mode
+/* Either NON_BLOCKING or BLOCKING. This does not affect the
+/* mode of accepted connections.
+/* .IP fd
+/* File descriptor returned by unix_pass_listen().
+/* DIAGNOSTICS
+/* Fatal errors: unix_pass_listen() aborts upon any system call failure.
+/* unix_pass_accept() leaves all error handling up to the caller.
+/* 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 <errno.h>
+#include <unistd.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <sane_accept.h>
+#include <listen.h>
+
+/* unix_pass_accept - accept descriptor */
+
+int unix_pass_accept(int listen_fd)
+{
+ const char *myname = "unix_pass_accept";
+ int accept_fd;
+ int recv_fd = -1;
+
+ accept_fd = sane_accept(listen_fd, (struct sockaddr *) 0, (SOCKADDR_SIZE *) 0);
+ if (accept_fd < 0) {
+ if (errno != EAGAIN)
+ msg_warn("%s: accept connection: %m", myname);
+ return (-1);
+ } else {
+ if (read_wait(accept_fd, 100) < 0)
+ msg_warn("%s: timeout receiving file descriptor: %m", myname);
+ else if ((recv_fd = unix_recv_fd(accept_fd)) < 0)
+ msg_warn("%s: cannot receive file descriptor: %m", myname);
+ if (close(accept_fd) < 0)
+ msg_warn("%s: close: %m", myname);
+ return (recv_fd);
+ }
+}
/*++
/* NAME
-/* upass_trigger 3
+/* unix_pass_trigger 3
/* SUMMARY
/* wakeup UNIX-domain file descriptor listener
/* SYNOPSIS
/* #include <trigger.h>
/*
-/* int upass_trigger(service, buf, len, timeout)
+/* int unix_pass_trigger(service, buf, len, timeout)
/* const char *service;
/* const char *buf;
/* ssize_t len;
/* int timeout;
/* DESCRIPTION
-/* upass_trigger() wakes up the named UNIX-domain server by sending
+/* unix_pass_trigger() wakes up the named UNIX-domain server by sending
/* a brief connection to it and writing the named buffer.
/*
/* The connection is closed by a background thread. Some kernels
/* DIAGNOSTICS
/* The result is zero in case of success, -1 in case of problems.
/* SEE ALSO
-/* upass_connect(3), UNIX-domain client
+/* unix_pass_connect(3), UNIX-domain client
/* LICENSE
/* .ad
/* .fi
#include <events.h>
#include <trigger.h>
-struct upass_trigger {
+struct unix_pass_trigger {
int fd;
char *service;
+ int *pair;
};
-/* upass_trigger_event - disconnect from peer */
+/* unix_pass_trigger_event - disconnect from peer */
-static void upass_trigger_event(int event, char *context)
+static void unix_pass_trigger_event(int event, char *context)
{
- struct upass_trigger *up = (struct upass_trigger *) context;
- static const char *myname = "upass_trigger_event";
+ struct unix_pass_trigger *up = (struct unix_pass_trigger *) context;
+ static const char *myname = "unix_pass_trigger_event";
/*
* Disconnect.
if (event == EVENT_TIME)
msg_warn("%s: read timeout for service %s", myname, up->service);
event_disable_readwrite(up->fd);
- event_cancel_timer(upass_trigger_event, context);
+ event_cancel_timer(unix_pass_trigger_event, context);
+ /* Don't combine multiple close() calls into one boolean expression. */
if (close(up->fd) < 0)
msg_warn("%s: close %s: %m", myname, up->service);
+ if (close(up->pair[0]) < 0)
+ msg_warn("%s: close pipe: %m", myname);
+ if (close(up->pair[1]) < 0)
+ msg_warn("%s: close pipe: %m", myname);
myfree(up->service);
myfree((char *) up);
}
-/* upass_trigger - wakeup UNIX-domain server */
+/* unix_pass_trigger - wakeup UNIX-domain server */
-int upass_trigger(const char *service, const char *buf, ssize_t len, int timeout)
+int unix_pass_trigger(const char *service, const char *buf, ssize_t len, int timeout)
{
- const char *myname = "upass_trigger";
- struct upass_trigger *up;
+ const char *myname = "unix_pass_trigger";
+ int pair[2];
+ struct unix_pass_trigger *up;
int fd;
if (msg_verbose > 1)
/*
* Connect...
*/
- if ((fd = upass_connect(service, BLOCKING, timeout)) < 0) {
+ if ((fd = unix_pass_connect(service, BLOCKING, timeout)) < 0) {
if (msg_verbose)
msg_warn("%s: connect to %s: %m", myname, service);
return (-1);
}
close_on_exec(fd, CLOSE_ON_EXEC);
+ /*
+ * Create a pipe, and send one pipe end to the server.
+ */
+ if (pipe(pair) < 0)
+ msg_fatal("%s: pipe: %m", myname);
+ if (unix_send_fd(fd, pair[0]) < 0)
+ msg_fatal("%s: send file descriptor: %m", myname);
+
/*
* Stash away context.
*/
- up = (struct upass_trigger *) mymalloc(sizeof(*up));
+ up = (struct unix_pass_trigger *) mymalloc(sizeof(*up));
up->fd = fd;
up->service = mystrdup(service);
+ up->pair = pair;
/*
* Write the request...
*/
- if (write_buf(fd, buf, len, timeout) < 0
- || write_buf(fd, "", 1, timeout) < 0)
+ if (write_buf(pair[1], buf, len, timeout) < 0
+ || write_buf(pair[1], "", 1, timeout) < 0)
if (msg_verbose)
msg_warn("%s: write to %s: %m", myname, service);
* Wakeup when the peer disconnects, or when we lose patience.
*/
if (timeout > 0)
- event_request_timer(upass_trigger_event, (char *) up, timeout + 100);
- event_enable_read(fd, upass_trigger_event, (char *) up);
+ event_request_timer(unix_pass_trigger_event, (char *) up, timeout + 100);
+ event_enable_read(fd, unix_pass_trigger_event, (char *) up);
return (0);
}
+++ /dev/null
-/*++
-/* NAME
-/* upass_connect 3
-/* SUMMARY
-/* connect to UNIX-domain file descriptor listener
-/* SYNOPSIS
-/* #include <connect.h>
-/*
-/* int upass_connect(addr, block_mode, timeout)
-/* const char *addr;
-/* int block_mode;
-/* int timeout;
-/* DESCRIPTION
-/* upass_connect() connects to a file descriptor listener in
-/* the UNIX domain at the specified address, sends one half
-/* of a socketpair to the listener, and returns the other half
-/* to the caller.
-/*
-/* The file descriptor transporting connection is closed by
-/* a background thread. Some kernels might otherwise discard
-/* the descriptor before the server has received it.
-/*
-/* Arguments:
-/* .IP addr
-/* Null-terminated string with connection destination.
-/* .IP block_mode
-/* Either NON_BLOCKING for a non-blocking socket, or BLOCKING for
-/* blocking mode. This setting has no effect on the connection
-/* establishment process.
-/* .IP timeout
-/* Bounds the number of seconds that the operation may take. Specify
-/* a value <= 0 to disable the time limit.
-/* DIAGNOSTICS
-/* The result is -1 in case the connection could not be made.
-/* Fatal errors: other system call failures.
-/* 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 interfaces. */
-
-#include <sys_defs.h>
-#include <sys/socket.h>
-#include <sys/un.h>
-#include <string.h>
-#include <unistd.h>
-#include <errno.h>
-
-/* Utility library. */
-
-#include <msg.h>
-#include <iostuff.h>
-#include <sane_connect.h>
-#include <connect.h>
-#include <timed_connect.h>
-#include <events.h>
-#include <mymalloc.h>
-#include <sane_socketpair.h>
-
- /*
- * Workaround for hostile kernels that don't support graceful shutdown.
- */
-struct upass_connect {
- int fd;
- char *service;
-};
-
-/* upass_connect_event - disconnect from peer */
-
-static void upass_connect_event(int event, char *context)
-{
- struct upass_connect *up = (struct upass_connect *) context;
- static const char *myname = "upass_connect_event";
-
- /*
- * Disconnect.
- */
- if (event == EVENT_TIME)
- msg_warn("%s: read timeout for service %s", myname, up->service);
- event_disable_readwrite(up->fd);
- event_cancel_timer(upass_connect_event, context);
- if (close(up->fd) < 0)
- msg_warn("%s: close %s: %m", myname, up->service);
- myfree(up->service);
- myfree((char *) up);
-}
-
-/* upass_connect - connect to UNIX-domain file descriptor listener */
-
-int upass_connect(const char *addr, int block_mode, int timeout)
-{
- struct upass_connect *up;
- int pair[2];
- int sock;
-
- /*
- * Connect.
- */
- if ((sock = unix_connect(addr, BLOCKING, timeout)) < 0)
- return (-1);
-
- /*
- * Send one socket pair half to the server.
- */
-#define OUR_HALF 0
-#define THEIR_HALF 1
-
- if (sane_socketpair(AF_UNIX, SOCK_STREAM, 0, pair) < 0) {
- close(sock);
- return (-1);
- }
- if (unix_send_fd(sock, pair[THEIR_HALF]) < 0) {
- close(pair[THEIR_HALF]);
- close(pair[OUR_HALF]);
- close(sock);
- return (-1);
- }
- close(pair[THEIR_HALF]);
-
- /*
- * Return the other socket pair half to the caller. Don't close the
- * control socket just yet, but wait until the receiver closes it first.
- * Otherwise, some hostile kernel might discard the socket that we just
- * sent.
- */
- up = (struct upass_connect *) mymalloc(sizeof(*up));
- up->fd = sock;
- up->service = mystrdup(addr);
- if (timeout > 0)
- event_request_timer(upass_connect_event, (char *) up, timeout + 100);
- event_enable_read(sock, upass_connect_event, (char *) up);
- non_blocking(pair[OUR_HALF], block_mode);
- return (pair[OUR_HALF]);
-}
+++ /dev/null
-/*++
-/* NAME
-/* upass_listen 3
-/* SUMMARY
-/* start UNIX-domain file descriptor listener
-/* SYNOPSIS
-/* #include <listen.h>
-/*
-/* int upass_listen(path, backlog, block_mode)
-/* const char *path;
-/* int backlog;
-/* int block_mode;
-/*
-/* int upass_accept(fd)
-/* int fd;
-/* DESCRIPTION
-/* This module implements a listener that receives one file descriptor
-/* across each UNIX-domain connection that is made to it.
-/*
-/* upass_listen() creates a listener endpoint with the specified
-/* permissions, and returns a file descriptor to be used for accepting
-/* descriptors.
-/*
-/* upass_accept() accepts a descriptor.
-/*
-/* Arguments:
-/* .IP path
-/* Null-terminated string with connection destination.
-/* .IP backlog
-/* This argument exists for compatibility and is ignored.
-/* .IP block_mode
-/* Either NON_BLOCKING or BLOCKING. This does not affect the
-/* mode of accepted connections.
-/* .IP fd
-/* File descriptor returned by upass_listen().
-/* DIAGNOSTICS
-/* Fatal errors: upass_listen() aborts upon any system call failure.
-/* upass_accept() leaves all error handling up to the caller.
-/* 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 <errno.h>
-#include <unistd.h>
-
-/* Utility library. */
-
-#include <msg.h>
-#include <sane_accept.h>
-#include <listen.h>
-
-/* upass_accept - accept descriptor */
-
-int upass_accept(int listen_fd)
-{
- const char *myname = "upass_accept";
- int accept_fd;
- int recv_fd = -1;
-
- accept_fd = sane_accept(listen_fd, (struct sockaddr *) 0, (SOCKADDR_SIZE *) 0);
- if (accept_fd < 0) {
- if (errno != EAGAIN)
- msg_warn("%s: accept connection: %m", myname);
- return (-1);
- } else {
- if (read_wait(accept_fd, 100) < 0)
- msg_warn("%s: timeout receiving file descriptor: %m", myname);
- else if ((recv_fd = unix_recv_fd(accept_fd)) < 0)
- msg_warn("%s: cannot receive file descriptor: %m", myname);
- if (close(accept_fd) < 0)
- msg_warn("%s: close: %m", myname);
- return (recv_fd);
- }
-}
-
-#if 0
-
-/* System library. */
-
-#include <sys_defs.h>
-
-/* Utility library. */
-
-#include <msg.h>
-#include <events.h>
-#include <sane_accept.h>
-#include <iostuff.h>
-#include <listen.h>
-
- /*
- * It would be nice if a client could make one UNIX-domain connection to a
- * Postfix master service, send multiple descriptors, and have each
- * descriptor handled by the first available child process.
- *
- * Possible solutions:
- *
- * - Either the master process accepts the UNIX-domain connection and forwards
- * each descriptor sent by the client to the first available child process.
- * That's what the code below does. Unfortunately, this approach is
- * inconsistent with the Postfix architecture which tries to eliminate the
- * master from connection management as much as possible.
- *
- * - Or one child processes accepts the UNIX-domain connection and sends a
- * shared socketpair half to the client. The other socketpair half is shared
- * with the master and all the child's siblings. The client then sends its
- * descriptors over the socketpair, and each descriptor is available to any
- * child process that is waiting for work.
- *
- * If the second solution did not use a shared socketpair, then all the
- * client's descriptors would be available only to the child process that
- * accepted the UNIX-domain connection. That results in poor performance.
- *
- * Unfortunately, having to receive a descriptor before being able to send one
- * or more descriptors is ugly from the client's point of view.
- */
-
-#define upass_accept(fd) unix_recv_fd(fd)
-
-/* upass_plumbing - operate the hidden descriptor passing machinery */
-
-static void upass_plumbing(int unused_event, char *context)
-{
- const char *myname = "upass_plumbing";
- UPASS_INFO *info = (UNIX_UPASS_INFO *) context;
- int fd;
-
- /*
- * Each time a client connects to the hidden UNIX-domain socket, call
- * unix_send_fd() to send one half of the hidden socketpair across a
- * short-lived UNIX-domain connection. Wait until the client closes the
- * UNIX-domain connection before closing the connection. This wait needs
- * to be time limited.
- */
- fd = sane_accept(info->unixsock, (struct sockaddr *) 0, (SOCKADDR_SIZE *) 0);
- if (fd < 0) {
- if (errno != EAGAIN)
- msg_fatal("%s: accept connection: %m", myname);
- } else {
- if (unix_send_fd(fd, info->halfpair) < 0)
- msg_warn("%s: cannot send file descriptor: %m", myname);
- if (read_wait(fd, 5) < 0)
- msg_warn("%s: read timeout", myname);
- if (close(fd) < 0)
- msg_warn("%s: close: %m", myname);
- }
-}
-
-/* upass_listen - set up hidden descriptor passing machinery */
-
-int upass_listen(const char *path, int backlog, int blocking, UPASS_INFO **ip)
-{
- int pair[2];
- UPASS_INFO *info;
-
- /*
- * Create a UNIX-domain socket with unix_listen() and create a
- * socketpair. One socketpair half is returned to the caller. The other
- * half is part of the hidden machinery, together with the UNIX-domain
- * socket.
- */
- if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) < 0)
- msg_fatal("socketpair: %m");
- info = (UPASS_INFO *) mymalloc(sizeof(*info));
- info->halfpair = pair[0];
- info->unixsock = unix_listen(path, backlog, blocking);
- event_request_read(info->unixsock, upass_plumbing, (char *) info);
- *ip = info;
- return (pair[1]);
-}
-
-/* upass_shutdown - tear down hidden descriptor passing machinery */
-
-void upass_shutdown(UPASS_INFO *info)
-{
- event_disable_readwrite(upass_info->unixsock)
- if (close(info->unixsock) < 0)
- msg_warn("%s: close unixsock: %m", myname);
- if (close(info->halfpair) < 0)
- msg_warn("%s: close halfpair: %m", myname);
- myfree((char *) info);
-}
-
-#endif
*/
static WATCHDOG *watchdog_curr;
+ /*
+ * Workaround for systems where the alarm signal does not wakeup the event
+ * machinery, and therefore does not restart the watchdog timer in the
+ * single_server etc. skeletons. The symptom is that programs abort when the
+ * watchdog timeout is less than the max_idle time.
+ */
+#ifdef USE_WATCHDOG_PIPE
+#include <iostuff.h>
+#include <events.h>
+
+static int watchdog_pipe[2];
+
+/* watchdog_read - read event pipe */
+
+static void watchdog_read(int unused_event, char *unused_context)
+{
+ char ch;
+
+ while (read(watchdog_pipe[0], &ch, 1) > 0)
+ /* void */ ;
+}
+
+#endif /* USE_WATCHDOG_PIPE */
+
/* watchdog_event - handle timeout event */
static void watchdog_event(int unused_sig)
if (msg_verbose > 1)
msg_info("%s: %p %d", myname, (void *) wp, wp->trip_run);
if (++(wp->trip_run) < WATCHDOG_STEPS) {
+#ifdef USE_WATCHDOG_PIPE
+ int saved_errno = errno;
+
+ /* Wake up the events(3) engine. */
+ if (write(watchdog_pipe[1], "", 1) != 1)
+ msg_warn("%s: write watchdog_pipe: %m", myname);
+ errno = saved_errno;
+#endif
alarm(wp->timeout);
} else {
if (wp->action)
msg_fatal("%s: sigaction(SIGALRM): %m", myname);
if (msg_verbose > 1)
msg_info("%s: %p %d", myname, (void *) wp, timeout);
+#ifdef USE_WATCHDOG_PIPE
+ if (watchdog_curr == 0) {
+ if (pipe(watchdog_pipe) < 0)
+ msg_fatal("%s: pipe: %m", myname);
+ non_blocking(watchdog_pipe[0], NON_BLOCKING);
+ non_blocking(watchdog_pipe[1], NON_BLOCKING);
+ event_enable_read(watchdog_pipe[0], watchdog_read, (char *) 0);
+ }
+#endif
return (watchdog_curr = wp);
}
if (wp->saved_time)
alarm(wp->saved_time);
myfree((char *) wp);
+#ifdef USE_WATCHDOG_PIPE
+ if (watchdog_curr == 0) {
+ event_disable_readwrite(watchdog_pipe[0]);
+ (void) close(watchdog_pipe[0]);
+ (void) close(watchdog_pipe[1]);
+ }
+#endif
if (msg_verbose > 1)
msg_info("%s: %p", myname, (void *) wp);
}