From: Wietse Venema
Date: Sat, 14 Jan 2012 05:00:00 +0000 (-0500)
Subject: postfix-2.9-20120114
X-Git-Tag: v2.9.0-RC1~3
X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=98d8efe9af1df2731204d344d7a63eab99977894;p=thirdparty%2Fpostfix.git
postfix-2.9-20120114
---
diff --git a/postfix/.indent.pro b/postfix/.indent.pro
index 9e679a2ca..e7009d5d4 100644
--- a/postfix/.indent.pro
+++ b/postfix/.indent.pro
@@ -100,6 +100,7 @@
-TDICT_SDBM
-TDICT_SQLITE
-TDICT_STACK
+-TDICT_SURROGATE
-TDICT_TCP
-TDICT_TEXT
-TDICT_THASH
diff --git a/postfix/HISTORY b/postfix/HISTORY
index 9f3659ac1..1d43a28cb 100644
--- a/postfix/HISTORY
+++ b/postfix/HISTORY
@@ -17502,7 +17502,7 @@ Apologies for any names omitted.
20120110
- Cleanup: added logging for failed table lookups and replaced
+ Cleanup: added logging for failed table lookups, and replaced
some "fatal" errors by warnings. Files: cleanup/cleanup_addr.c,
cleanup/cleanup_message.c, cleanup/cleanup_milter.c,
cleanup/cleanup_masquerade.c, global/header_body_checks.c,
@@ -17511,3 +17511,36 @@ Apologies for any names omitted.
smtp/smtp_proto.c, smtp/smtp_sasl_auth_cache.c,
smtp/smtp_sasl_glue.c, smtp/smtp_session.c, smtp/smtp_trouble.c,
smtpd/smtpd.c, smtpd/smtpd_check.c.
+
+20120114
+
+ Cleanup: gradual degradation after database file open errors.
+ Instead of terminating immediately with a "fatal" error, a
+ Postfix daemon logs an error and continues execution with
+ reduced functionality. In other words, features that don't
+ depend on the unavailable table will keep working. However,
+ for the sake of sanity, the number of such errors over the
+ life of a process is limited to 13. Files:
+ src/global/cfg_parser.c, src/util/dict_thash.c,
+ src/util/dict_cidr.c, src/util/dict_nis.c, src/util/dict_nisplus.c,
+ src/global/dict_ldap.c, src/global/dict_mysql.c,
+ src/global/dict_pgsql.c, src/global/dict_sqlite.c,
+ src/postconf/postconf_main.c, src/global/mail_conf.c,
+ src/util/dict.h, src/util/dict.c, src/global/dict_memcache.c,
+ src/util/dict_tcp.c, src/util/dict_unix.c, src/util/dict_pcre.c,
+ src/util/dict_regexp.c, src/master/trigger_server.c,
+ src/master/single_server.c, src/master/multi_server.c,
+ src/master/event_server.c, src/util/dict_test.c,
+ src/util/dict_surrogate.c, src/util/dict_alloc.c, src/util/msg.c,
+ src/util/dict_cdb.c, src/util/dict_dbm.c, src/util/msg.h,
+ src/util/dict_db.c.
+
+ Incompatibility: the Postfix SMTP server no longer reports
+ transcripts of sessions where a client command is rejected
+ because a table is unavailable. To receive such reports,
+ add the new "data" class to the notify_classes parameter
+ value. The reports will be sent to the error_notice_recipient
+ address as before. This class is also used by the Postfix
+ SMTP client to report about sessions that fail because a
+ table is unavailable. Files: global/mail_error.[hc],
+ smtpd/smtpd_check.c, smtp/smtp_trouble.c.
diff --git a/postfix/RELEASE_NOTES b/postfix/RELEASE_NOTES
index 4492293bd..dfb86dc67 100644
--- a/postfix/RELEASE_NOTES
+++ b/postfix/RELEASE_NOTES
@@ -14,16 +14,57 @@ specifies the release date of a stable release or snapshot release.
If you upgrade from Postfix 2.7 or earlier, read RELEASE_NOTES-2.8
before proceeding.
-Major changes with snapshot 20111208
+Incompatible changes with snapshot 20120114
+===========================================
+
+Instead of terminating immediately with a "fatal" message when a
+database file can't be opened, a Postfix daemon program now logs
+an "error" message, and continues execution with reduced functionality.
+
+Specify "daemon_table_open_error_is_fatal = yes" to get the
+historical behavior (immediate termination with "fatal" message).
+
+Logfile-based alerting systems may need to be updated to look for
+"error" messages in addition to "fatal" messages.
+
+By default the Postfix SMTP server no longer reports transcripts
+of sessions where a client command is rejected because a table is
+unavailable. To receive such reports, add the new "data" class to
+the notify_classes parameter value. The reports will be sent to the
+error_notice_recipient address as before. This class is also used
+by the Postfix SMTP client to report about sessions that fail because
+a table is unavailable
+
+Major changes with snapshot 20120114
+====================================
+
+Gradual degradation after database file open error. Instead of
+terminating immediately with a "fatal" message, a Postfix daemon
+program logs an "error" message, and continues execution with reduced
+functionality.
+
+Features that don't depend on the unavailable table will keep
+working; features that depend on the table will fail, and will be
+logged with a "warning" message. For the sake of sanity, the number
+of "error"s over the life of a process is limited to 13.
+
+Specify "daemon_table_open_error_is_fatal = yes" to get the
+historical behavior (immediate termination with "fatal" message).
+
+When the notify_classes parameter contains the new "data" class,
+the Postfix SMTP client and server will report transcripts of
+sessions with errors because a table is unavailable.
+
+Major changes with snapshot 20120108
====================================
The LDAP, *SQL and memcache clients now "catch" table lookup errors
in the "domain" feature, instead of terminating with a fatal error.
-Major changes with snapshot 20111202
+Major changes with snapshot 20120102
====================================
-Degrade gracefully when some or all network protocols specified
+Degrade gradually when some or all network protocols specified
with inet_protocols are unavailable, instead of terminating with a
fatal error. This eliminates build errors on non-standard systems
where opening an IPv4 socket results in an error, and on non-standard
diff --git a/postfix/WISHLIST b/postfix/WISHLIST
index 394eea47c..c201dae89 100644
--- a/postfix/WISHLIST
+++ b/postfix/WISHLIST
@@ -4,9 +4,6 @@ Wish list:
Remove this file from the stable release.
- In daemons, open surrogate map when real map is unavailable,
- effectively redirecting to fail:.
-
Things to do after the stable release:
Before proxymap can be exposed to the network to share,
diff --git a/postfix/html/memcache_table.5.html b/postfix/html/memcache_table.5.html
index 1dd8ba15f..11ab0ff7e 100644
--- a/postfix/html/memcache_table.5.html
+++ b/postfix/html/memcache_table.5.html
@@ -162,12 +162,13 @@ MEMCACHE_TABLE(5) MEMCACHE_TABLE(5)
domain (default: no domain list)
This feature can significantly reduce database
server load. Specify 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 or update: bare 'user' lookups, bare domain
- lookups and "@domain" lookups are silently skipped
- (updates are skipped with a warning). Example:
+ to files, or "type:table" databases. When speci-
+ fied, only fully qualified search keys with a *non-
+ empty* localpart and a matching domain are eligible
+ for lookup or update: bare 'user' lookups, bare
+ domain lookups and "@domain" lookups are silently
+ skipped (updates are skipped with a warning).
+ Example:
domain = example.com, hash:/etc/postfix/searchdomains
@@ -179,31 +180,31 @@ MEMCACHE_TABLE(5) MEMCACHE_TABLE(5)
The maximal memcache reply line length in bytes.
max_try (default: 2)
- The number of times to try a memcache command
+ The number of times to try a memcache command
before giving up.
retry_pause (default: 1)
- The time in seconds to wait after a memcache com-
+ The time in seconds to wait after a memcache com-
mand fails.
timeout (default: 2)
- The time limit for sending a memcache command and
+ The time limit for sending a memcache command and
for receiving a memcache reply.
BUGS
- The Postfix memcache client cannot be used for security-
- sensitive tables such as alias_maps (these may contain
- "|command and "/file/name" destinations), or vir-
- tual_uid_maps, virtual_gid_maps and virtual_mailbox_maps
- (these specify UNIX process privileges or "/file/name"
- destinations). In a typical deployment a memcache data-
- base is writable by any process that can talk to the mem-
- cache server; in contrast, security-sensitive tables must
+ The Postfix memcache client cannot be used for security-
+ sensitive tables such as alias_maps (these may contain
+ "|command and "/file/name" destinations), or vir-
+ tual_uid_maps, virtual_gid_maps and virtual_mailbox_maps
+ (these specify UNIX process privileges or "/file/name"
+ destinations). In a typical deployment a memcache data-
+ base is writable by any process that can talk to the mem-
+ cache server; in contrast, security-sensitive tables must
not be writable by the unprivileged Postfix user.
The Postfix memcache client requires additional configura-
- tion when used as postscreen(8) or verify(8) cache. For
- details see the backup and ttl parameter discussions in
+ tion when used as postscreen(8) or verify(8) cache. For
+ details see the backup and ttl parameter discussions in
the MEMCACHE MAIN PARAMETERS section above.
SEE ALSO
@@ -215,13 +216,13 @@ MEMCACHE_TABLE(5) MEMCACHE_TABLE(5)
MEMCACHE_README, Postfix memcache client guide
LICENSE
- The Secure Mailer license must be distributed with this
+ The Secure Mailer license must be distributed with this
software.
HISTORY
- The first memcache client for Postfix was written by Omar
- Kilani, and was based on libmemcache. The Postfix imple-
- mentation does not use libmemcache, and bears no resem-
+ The first memcache client for Postfix was written by Omar
+ Kilani, and was based on libmemcache. The Postfix imple-
+ mentation does not use libmemcache, and bears no resem-
blance to earlier work.
AUTHOR(S)
diff --git a/postfix/html/postconf.5.html b/postfix/html/postconf.5.html
index 5ef3c7bd0..ec54b00c4 100644
--- a/postfix/html/postconf.5.html
+++ b/postfix/html/postconf.5.html
@@ -1618,6 +1618,39 @@ be owned by root.
+
+
+daemon_table_open_error_is_fatal
+(default: no)
+
+ How a Postfix daemon process handles errors while opening lookup
+tables: gradual degradation or immediate termination.
+
+
+
+- no (default)
-
Gradual degradation: a
+daemon process logs a message of type "error" and continues execution
+with reduced functionality. Features that do not depend on the
+unavailable table will work normally, while features that depend
+on the table will result in a type "warning" message.
When
+the notify_classes parameter value contains the "data" class, the
+Postfix SMTP server and client will report transcripts of sessions
+with an error because a table is unavailable.
+
+- yes (historical behavior)
-
Immediate
+termination: a daemon process logs a type "fatal" message and
+terminates immediately. This option reduces the number of possible
+code paths through Postfix, and may therefore be slightly more
+secure than the default.
+
+
+
+ For the sake of sanity, the number of type "error" messages is
+limited to 13 over the lifetime of a daemon process.
+
+ This feature is available in Postfix 2.9 and later.
+
+
daemon_timeout
@@ -6653,6 +6686,14 @@ notification is sent to the address specified with the
is sent to the address specified with the 2bounce_notice_recipient
configuration parameter (default: postmaster).
+data
+
+Send the postmaster a transcript of the SMTP session with an
+error because a critical data file was unavailable. The notification
+is sent to the address specified with the error_notice_recipient
+configuration parameter (default: postmaster).
This feature
+is available in Postfix 2.9 and later.
+
delay
Send the postmaster copies of the headers of delayed mail. The
diff --git a/postfix/man/man5/memcache_table.5 b/postfix/man/man5/memcache_table.5
index 5ceb73d1d..002766859 100644
--- a/postfix/man/man5/memcache_table.5
+++ b/postfix/man/man5/memcache_table.5
@@ -160,7 +160,8 @@ is skipped with a warning).
.RE
.IP "\fBdomain (default: no domain list)\fR"
This feature can significantly reduce database server load.
-Specify a list of domain names, paths to files, or dictionaries.
+Specify a list of domain names, paths to files, or "type:table"
+databases.
When specified, only fully qualified search keys with a
*non-empty* localpart and a matching domain are eligible
for lookup or update: bare 'user' lookups, bare domain
diff --git a/postfix/man/man5/postconf.5 b/postfix/man/man5/postconf.5
index d5b5e17f1..09506eae2 100644
--- a/postfix/man/man5/postconf.5
+++ b/postfix/man/man5/postconf.5
@@ -906,6 +906,31 @@ with Cyrus SASL 2.1.22 or later.
The directory with Postfix support programs and daemon programs.
These should not be invoked directly by humans. The directory must
be owned by root.
+.SH daemon_table_open_error_is_fatal (default: no)
+How a Postfix daemon process handles errors while opening lookup
+tables: gradual degradation or immediate termination.
+.IP "\fB no \fR (default)"
+Gradual degradation: a
+daemon process logs a message of type "error" and continues execution
+with reduced functionality. Features that do not depend on the
+unavailable table will work normally, while features that depend
+on the table will result in a type "warning" message.
+.br
+When
+the notify_classes parameter value contains the "data" class, the
+Postfix SMTP server and client will report transcripts of sessions
+with an error because a table is unavailable.
+.IP "\fB yes \fR (historical behavior)"
+Immediate
+termination: a daemon process logs a type "fatal" message and
+terminates immediately. This option reduces the number of possible
+code paths through Postfix, and may therefore be slightly more
+secure than the default.
+.PP
+For the sake of sanity, the number of type "error" messages is
+limited to 13 over the lifetime of a daemon process.
+.PP
+This feature is available in Postfix 2.9 and later.
.SH daemon_timeout (default: 18000s)
How much time a Postfix daemon process may take to handle a
request before it is terminated by a built-in watchdog timer.
@@ -3792,6 +3817,14 @@ bounce_notice_recipient configuration parameter (default: postmaster).
Send undeliverable bounced mail to the postmaster. The notification
is sent to the address specified with the 2bounce_notice_recipient
configuration parameter (default: postmaster).
+.IP "\fBdata\fR"
+Send the postmaster a transcript of the SMTP session with an
+error because a critical data file was unavailable. The notification
+is sent to the address specified with the error_notice_recipient
+configuration parameter (default: postmaster).
+.br
+This feature
+is available in Postfix 2.9 and later.
.IP "\fBdelay\fR"
Send the postmaster copies of the headers of delayed mail. The
notification is sent to the address specified with the
diff --git a/postfix/proto/memcache_table b/postfix/proto/memcache_table
index 45dbe2942..fc5827c13 100644
--- a/postfix/proto/memcache_table
+++ b/postfix/proto/memcache_table
@@ -150,7 +150,8 @@
# .RE
# .IP "\fBdomain (default: no domain list)\fR"
# This feature can significantly reduce database server load.
-# Specify a list of domain names, paths to files, or dictionaries.
+# Specify a list of domain names, paths to files, or "type:table"
+# databases.
# When specified, only fully qualified search keys with a
# *non-empty* localpart and a matching domain are eligible
# for lookup or update: bare 'user' lookups, bare domain
diff --git a/postfix/proto/postconf.proto b/postfix/proto/postconf.proto
index 638e81205..8e5e47a36 100644
--- a/postfix/proto/postconf.proto
+++ b/postfix/proto/postconf.proto
@@ -3045,6 +3045,14 @@ bounce_notice_recipient configuration parameter (default: postmaster).
is sent to the address specified with the 2bounce_notice_recipient
configuration parameter (default: postmaster).
+data
+
+Send the postmaster a transcript of the SMTP session with an
+error because a critical data file was unavailable. The notification
+is sent to the address specified with the error_notice_recipient
+configuration parameter (default: postmaster).
This feature
+is available in Postfix 2.9 and later.
+
delay
Send the postmaster copies of the headers of delayed mail. The
@@ -14455,3 +14463,32 @@ units are: s (seconds), m (minutes), h (hours), d (days), w (weeks).
This feature is available in Postfix 2.9 and later.
+
+%PARAM daemon_table_open_error_is_fatal no
+
+ How a Postfix daemon process handles errors while opening lookup
+tables: gradual degradation or immediate termination.
+
+
+
+- no (default)
-
Gradual degradation: a
+daemon process logs a message of type "error" and continues execution
+with reduced functionality. Features that do not depend on the
+unavailable table will work normally, while features that depend
+on the table will result in a type "warning" message.
When
+the notify_classes parameter value contains the "data" class, the
+Postfix SMTP server and client will report transcripts of sessions
+with an error because a table is unavailable.
+
+- yes (historical behavior)
-
Immediate
+termination: a daemon process logs a type "fatal" message and
+terminates immediately. This option reduces the number of possible
+code paths through Postfix, and may therefore be slightly more
+secure than the default.
+
+
+
+ For the sake of sanity, the number of type "error" messages is
+limited to 13 over the lifetime of a daemon process.
+
+ This feature is available in Postfix 2.9 and later.
diff --git a/postfix/src/cleanup/cleanup_milter.c b/postfix/src/cleanup/cleanup_milter.c
index cc63313f1..18957fe1d 100644
--- a/postfix/src/cleanup/cleanup_milter.c
+++ b/postfix/src/cleanup/cleanup_milter.c
@@ -2155,7 +2155,7 @@ char *var_milt_head_checks = "";
/* Dummies to satisfy unused external references. */
-int cleanup_masquerade_internal(VSTRING *addr, ARGV *masq_domains)
+int cleanup_masquerade_internal(CLEANUP_STATE *state, VSTRING *addr, ARGV *masq_domains)
{
msg_panic("cleanup_masquerade_internal dummy");
}
diff --git a/postfix/src/global/cfg_parser.c b/postfix/src/global/cfg_parser.c
index 73c81d025..89ebab5d3 100644
--- a/postfix/src/global/cfg_parser.c
+++ b/postfix/src/global/cfg_parser.c
@@ -40,7 +40,8 @@
/* \fBvalue\fR" in main.cf (the old LDAP style). It unifies the
/* two styles and provides support for range checking.
/*
-/* \fIcfg_parser_alloc\fR initializes the parser.
+/* \fIcfg_parser_alloc\fR initializes the parser. The result
+/* is NULL if a configuration file could not be opened.
/*
/* \fIcfg_parser_free\fR releases the parser.
/*
@@ -234,7 +235,11 @@ CFG_PARSER *cfg_parser_alloc(const char *pname)
parser = (CFG_PARSER *) mymalloc(sizeof(*parser));
parser->name = mystrdup(pname);
if (*parser->name == '/' || *parser->name == '.') {
- dict_load_file(parser->name, parser->name);
+ if (dict_load_file_xt(parser->name, parser->name) == 0) {
+ myfree(parser->name);
+ myfree((char *) parser);
+ return (0);
+ }
parser->get_str = get_dict_str;
parser->get_int = get_dict_int;
parser->get_bool = get_dict_bool;
diff --git a/postfix/src/global/dict_ldap.c b/postfix/src/global/dict_ldap.c
index 6398dcc89..3d6ddeaaa 100644
--- a/postfix/src/global/dict_ldap.c
+++ b/postfix/src/global/dict_ldap.c
@@ -1606,7 +1606,7 @@ static void dict_ldap_close(DICT *dict)
/* dict_ldap_open - create association with data base */
-DICT *dict_ldap_open(const char *ldapsource, int dummy, int dict_flags)
+DICT *dict_ldap_open(const char *ldapsource, int open_flags, int dict_flags)
{
const char *myname = "dict_ldap_open";
DICT_LDAP *dict_ldap;
@@ -1619,10 +1619,26 @@ DICT *dict_ldap_open(const char *ldapsource, int dummy, int dict_flags)
char *bindopt;
int tmp;
int vendor_version = dict_ldap_vendor_version();
+ CFG_PARSER *parser;
if (msg_verbose)
msg_info("%s: Using LDAP source %s", myname, ldapsource);
+ /*
+ * Sanity check.
+ */
+ if (open_flags != O_RDONLY)
+ return (dict_surrogate(DICT_TYPE_LDAP, ldapsource, open_flags, dict_flags,
+ "%s:%s map requires O_RDONLY access mode",
+ DICT_TYPE_LDAP, ldapsource));
+
+ /*
+ * Open the configuration file.
+ */
+ if ((parser = cfg_parser_alloc(ldapsource)) == 0)
+ return (dict_surrogate(DICT_TYPE_LDAP, ldapsource, open_flags, dict_flags,
+ "open %s: %m", ldapsource));
+
dict_ldap = (DICT_LDAP *) dict_alloc(DICT_TYPE_LDAP, ldapsource,
sizeof(*dict_ldap));
dict_ldap->dict.lookup = dict_ldap_lookup;
@@ -1630,7 +1646,7 @@ DICT *dict_ldap_open(const char *ldapsource, int dummy, int dict_flags)
dict_ldap->dict.flags = dict_flags;
dict_ldap->ld = NULL;
- dict_ldap->parser = cfg_parser_alloc(ldapsource);
+ dict_ldap->parser = parser;
server_host = cfg_get_str(dict_ldap->parser, "server_host",
"localhost", 1, 0);
diff --git a/postfix/src/global/dict_memcache.c b/postfix/src/global/dict_memcache.c
index d6df42996..e3ce92538 100644
--- a/postfix/src/global/dict_memcache.c
+++ b/postfix/src/global/dict_memcache.c
@@ -500,17 +500,27 @@ DICT *dict_memcache_open(const char *name, int open_flags, int dict_flags)
{
DICT_MC *dict_mc;
char *backup;
+ CFG_PARSER *parser;
/*
* Sanity checks.
*/
if (dict_flags & DICT_FLAG_NO_UNAUTH)
- msg_fatal("%s:%s map is not allowed for security-sensitive data",
- DICT_TYPE_MEMCACHE, name);
+ return (dict_surrogate(DICT_TYPE_MEMCACHE, name, open_flags, dict_flags,
+ "%s:%s map is not allowed for security-sensitive data",
+ DICT_TYPE_MEMCACHE, name));
open_flags &= (O_RDONLY | O_RDWR | O_WRONLY | O_APPEND);
if (open_flags != O_RDONLY && open_flags != O_RDWR)
- msg_fatal("%s:%s map requires O_RDONLY or O_RDWR access mode",
- DICT_TYPE_MEMCACHE, name);
+ return (dict_surrogate(DICT_TYPE_MEMCACHE, name, open_flags, dict_flags,
+ "%s:%s map requires O_RDONLY or O_RDWR access mode",
+ DICT_TYPE_MEMCACHE, name));
+
+ /*
+ * Open the configuration file.
+ */
+ if ((parser = cfg_parser_alloc(name)) == 0)
+ return (dict_surrogate(DICT_TYPE_MEMCACHE, name, open_flags, dict_flags,
+ "open %s: %m", name));
/*
* Create the dictionary object.
@@ -531,7 +541,7 @@ DICT *dict_memcache_open(const char *name, int open_flags, int dict_flags)
/*
* Parse the configuration file.
*/
- dict_mc->parser = cfg_parser_alloc(name);
+ dict_mc->parser = parser;
dict_mc->key_format = cfg_get_str(dict_mc->parser, DICT_MC_NAME_KEY_FMT,
DICT_MC_DEF_KEY_FMT, 0, 0);
dict_mc->timeout = cfg_get_int(dict_mc->parser, DICT_MC_NAME_MC_TIMEOUT,
diff --git a/postfix/src/global/dict_mysql.c b/postfix/src/global/dict_mysql.c
index 8f7285234..a3e231a4e 100644
--- a/postfix/src/global/dict_mysql.c
+++ b/postfix/src/global/dict_mysql.c
@@ -583,11 +583,10 @@ static void plmysql_down_host(HOST *host)
static void mysql_parse_config(DICT_MYSQL *dict_mysql, const char *mysqlcf)
{
const char *myname = "mysqlname_parse";
- CFG_PARSER *p;
+ CFG_PARSER *p = dict_mysql->parser;
VSTRING *buf;
char *hosts;
- p = dict_mysql->parser = cfg_parser_alloc(mysqlcf);
dict_mysql->username = cfg_get_str(p, "user", "", 0, 0);
dict_mysql->password = cfg_get_str(p, "password", "", 0, 0);
dict_mysql->dbname = cfg_get_str(p, "dbname", "", 1, 0);
@@ -649,19 +648,29 @@ static void mysql_parse_config(DICT_MYSQL *dict_mysql, const char *mysqlcf)
DICT *dict_mysql_open(const char *name, int open_flags, int dict_flags)
{
DICT_MYSQL *dict_mysql;
+ CFG_PARSER *parser;
/*
* Sanity checks.
*/
if (open_flags != O_RDONLY)
- msg_fatal("%s:%s map requires O_RDONLY access mode",
- DICT_TYPE_MYSQL, name);
+ return (dict_surrogate(DICT_TYPE_MYSQL, name, open_flags, dict_flags,
+ "%s:%s map requires O_RDONLY access mode",
+ DICT_TYPE_MYSQL, name));
+
+ /*
+ * Open the configuration file.
+ */
+ if ((parser = cfg_parser_alloc(name)) == 0)
+ return (dict_surrogate(DICT_TYPE_MYSQL, name, open_flags, dict_flags,
+ "open %s: %m", name));
dict_mysql = (DICT_MYSQL *) dict_alloc(DICT_TYPE_MYSQL, name,
sizeof(DICT_MYSQL));
dict_mysql->dict.lookup = dict_mysql_lookup;
dict_mysql->dict.close = dict_mysql_close;
dict_mysql->dict.flags = dict_flags;
+ dict_mysql->parser = parser;
mysql_parse_config(dict_mysql, name);
#if defined(MYSQL_VERSION_ID) && MYSQL_VERSION_ID >= 40000
dict_mysql->active_host = 0;
diff --git a/postfix/src/global/dict_pgsql.c b/postfix/src/global/dict_pgsql.c
index a98a0c979..b96a81fe3 100644
--- a/postfix/src/global/dict_pgsql.c
+++ b/postfix/src/global/dict_pgsql.c
@@ -680,12 +680,11 @@ static void plpgsql_down_host(HOST *host)
static void pgsql_parse_config(DICT_PGSQL *dict_pgsql, const char *pgsqlcf)
{
const char *myname = "pgsql_parse_config";
- CFG_PARSER *p;
+ CFG_PARSER *p = dict_pgsql->parser;
char *hosts;
VSTRING *query;
char *select_function;
- p = dict_pgsql->parser = cfg_parser_alloc(pgsqlcf);
dict_pgsql->username = cfg_get_str(p, "user", "", 0, 0);
dict_pgsql->password = cfg_get_str(p, "password", "", 0, 0);
dict_pgsql->dbname = cfg_get_str(p, "dbname", "", 1, 0);
@@ -752,16 +751,29 @@ static void pgsql_parse_config(DICT_PGSQL *dict_pgsql, const char *pgsqlcf)
DICT *dict_pgsql_open(const char *name, int open_flags, int dict_flags)
{
DICT_PGSQL *dict_pgsql;
+ CFG_PARSER *parser;
+ /*
+ * Sanity check.
+ */
if (open_flags != O_RDONLY)
- msg_fatal("%s:%s map requires O_RDONLY access mode",
- DICT_TYPE_PGSQL, name);
+ return (dict_surrogate(DICT_TYPE_PGSQL, name, open_flags, dict_flags,
+ "%s:%s map requires O_RDONLY access mode",
+ DICT_TYPE_PGSQL, name));
+
+ /*
+ * Open the configuration file.
+ */
+ if ((parser = cfg_parser_alloc(name)) == 0)
+ return (dict_surrogate(DICT_TYPE_PGSQL, name, open_flags, dict_flags,
+ "open %s: %m", name));
dict_pgsql = (DICT_PGSQL *) dict_alloc(DICT_TYPE_PGSQL, name,
sizeof(DICT_PGSQL));
dict_pgsql->dict.lookup = dict_pgsql_lookup;
dict_pgsql->dict.close = dict_pgsql_close;
dict_pgsql->dict.flags = dict_flags;
+ dict_pgsql->parser = parser;
pgsql_parse_config(dict_pgsql, name);
dict_pgsql->active_host = 0;
dict_pgsql->pldb = plpgsql_init(dict_pgsql->hosts);
diff --git a/postfix/src/global/dict_sqlite.c b/postfix/src/global/dict_sqlite.c
index acfcd4084..7f56cdf30 100644
--- a/postfix/src/global/dict_sqlite.c
+++ b/postfix/src/global/dict_sqlite.c
@@ -268,7 +268,6 @@ static void sqlite_parse_config(DICT_SQLITE *dict_sqlite, const char *sqlitecf)
* query interface if necessary. This simplifies migration from one SQL
* database type to another.
*/
- dict_sqlite->parser = cfg_parser_alloc(sqlitecf);
dict_sqlite->dbpath = cfg_get_str(dict_sqlite->parser, "dbpath", "", 1, 0);
dict_sqlite->query = cfg_get_str(dict_sqlite->parser, "query", NULL, 0, 0);
if (dict_sqlite->query == 0) {
@@ -305,13 +304,22 @@ static void sqlite_parse_config(DICT_SQLITE *dict_sqlite, const char *sqlitecf)
DICT *dict_sqlite_open(const char *name, int open_flags, int dict_flags)
{
DICT_SQLITE *dict_sqlite;
+ CFG_PARSER *parser;
/*
* Sanity checks.
*/
if (open_flags != O_RDONLY)
- msg_fatal("%s:%s map requires O_RDONLY access mode",
- DICT_TYPE_SQLITE, name);
+ return (dict_surrogate(DICT_TYPE_SQLITE, name, open_flags, dict_flags,
+ "%s:%s map requires O_RDONLY access mode",
+ DICT_TYPE_SQLITE, name));
+
+ /*
+ * Open the configuration file.
+ */
+ if ((parser = cfg_parser_alloc(name)) == 0)
+ return (dict_surrogate(DICT_TYPE_SQLITE, name, open_flags, dict_flags,
+ "open %s: %m", name));
dict_sqlite = (DICT_SQLITE *) dict_alloc(DICT_TYPE_SQLITE, name,
sizeof(DICT_SQLITE));
@@ -319,6 +327,7 @@ DICT *dict_sqlite_open(const char *name, int open_flags, int dict_flags)
dict_sqlite->dict.close = dict_sqlite_close;
dict_sqlite->dict.flags = dict_flags;
+ dict_sqlite->parser = parser;
sqlite_parse_config(dict_sqlite, name);
if (sqlite3_open(dict_sqlite->dbpath, &dict_sqlite->db))
diff --git a/postfix/src/global/mail_conf.c b/postfix/src/global/mail_conf.c
index ed1b336d1..ae0ae9886 100644
--- a/postfix/src/global/mail_conf.c
+++ b/postfix/src/global/mail_conf.c
@@ -187,7 +187,8 @@ void mail_conf_suck(void)
&& geteuid() != 0) /* untrusted */
mail_conf_checkdir(var_config_dir);
path = concatenate(var_config_dir, "/", "main.cf", (char *) 0);
- dict_load_file(CONFIG_DICT, path);
+ if (dict_load_file_xt(CONFIG_DICT, path) == 0)
+ msg_fatal("open %s: %m", path);
myfree(path);
}
diff --git a/postfix/src/global/mail_error.c b/postfix/src/global/mail_error.c
index 8e9d40936..0a239b816 100644
--- a/postfix/src/global/mail_error.c
+++ b/postfix/src/global/mail_error.c
@@ -21,6 +21,9 @@
/* does not exist, and so on.
/* .IP "2bounce (MAIL_ERROR_2BOUNCE)"
/* A bounce message could not be delivered.
+/* .IP "dat (MAIL_ERROR_DATA)"
+/* A message could not be delivered because a critical data
+/* file was unavailable.
/* .IP "policy (MAIL_ERROR_POLICY)"
/* Policy violation. This depends on what restrictions have
/* been configured locally.
@@ -67,6 +70,7 @@
const NAME_MASK mail_error_masks[] = {
"bounce", MAIL_ERROR_BOUNCE,
"2bounce", MAIL_ERROR_2BOUNCE,
+ "data", MAIL_ERROR_DATA,
"delay", MAIL_ERROR_DELAY,
"policy", MAIL_ERROR_POLICY,
"protocol", MAIL_ERROR_PROTOCOL,
diff --git a/postfix/src/global/mail_error.h b/postfix/src/global/mail_error.h
index 89aee07e4..f90c0ef1c 100644
--- a/postfix/src/global/mail_error.h
+++ b/postfix/src/global/mail_error.h
@@ -26,6 +26,7 @@
#define MAIL_ERROR_RESOURCE (1<<4)
#define MAIL_ERROR_2BOUNCE (1<<5)
#define MAIL_ERROR_DELAY (1<<6)
+#define MAIL_ERROR_DATA (1<<7)
extern const NAME_MASK mail_error_masks[];
diff --git a/postfix/src/global/mail_params.c b/postfix/src/global/mail_params.c
index dff3b72b8..e312f56e5 100644
--- a/postfix/src/global/mail_params.c
+++ b/postfix/src/global/mail_params.c
@@ -118,6 +118,7 @@
/* char *var_multi_name;
/* bool var_multi_enable;
/* bool var_long_queue_ids;
+/* bool var_daemon_open_fatal;
/*
/* void mail_params_init()
/*
@@ -304,6 +305,7 @@ char *var_multi_group;
char *var_multi_name;
bool var_multi_enable;
bool var_long_queue_ids;
+bool var_daemon_open_fatal;
const char null_format_string[1] = "";
@@ -517,6 +519,11 @@ void mail_params_init()
VAR_MULTI_NAME, DEF_MULTI_NAME, &var_multi_name, 0, 0,
0,
};
+ static const CONFIG_BOOL_TABLE first_bool_defaults[] = {
+ /* read and process the following before opening tables. */
+ VAR_DAEMON_OPEN_FATAL, DEF_DAEMON_OPEN_FATAL, &var_daemon_open_fatal,
+ 0,
+ };
static const CONFIG_STR_FN_TABLE function_str_defaults[] = {
VAR_MYHOSTNAME, check_myhostname, &var_myhostname, 1, 0,
VAR_MYDOMAIN, check_mydomainname, &var_mydomain, 1, 0,
@@ -646,6 +653,14 @@ void mail_params_init()
var_config_dir, MAIN_CONF_FILE,
VAR_SYSLOG_FACILITY, var_syslog_facility);
+ /*
+ * Should daemons terminate after table open error, or should they
+ * continue execution with reduced functionality?
+ */
+ get_mail_conf_bool_table(first_bool_defaults);
+ if (var_daemon_open_fatal)
+ dict_allow_surrogate = 0;
+
/*
* What protocols should we attempt to support? The result is stored in
* the global inet_proto_table variable.
diff --git a/postfix/src/global/mail_params.h b/postfix/src/global/mail_params.h
index 936835e70..912c012c8 100644
--- a/postfix/src/global/mail_params.h
+++ b/postfix/src/global/mail_params.h
@@ -3623,6 +3623,13 @@ extern bool var_smtp_rec_deadline;
#define DEF_SM_FIX_EOL SM_FIX_EOL_ALWAYS
extern char *var_sm_fix_eol;
+ /*
+ * Gradual degradation, or fatal exit after table open error?
+ */
+#define VAR_DAEMON_OPEN_FATAL "daemon_table_open_error_is_fatal"
+#define DEF_DAEMON_OPEN_FATAL 0
+extern bool var_daemon_open_fatal;
+
/* LICENSE
/* .ad
/* .fi
diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h
index a69bbad89..cbb946c71 100644
--- a/postfix/src/global/mail_version.h
+++ b/postfix/src/global/mail_version.h
@@ -20,7 +20,7 @@
* Patches change both the patchlevel and the release date. Snapshots have no
* patchlevel; they change the release date only.
*/
-#define MAIL_RELEASE_DATE "20120110"
+#define MAIL_RELEASE_DATE "20120114"
#define MAIL_VERSION_NUMBER "2.9"
#ifdef SNAPSHOT
diff --git a/postfix/src/global/maps.ref b/postfix/src/global/maps.ref
index 8ae750a25..84033c7d8 100644
--- a/postfix/src/global/maps.ref
+++ b/postfix/src/global/maps.ref
@@ -1,7 +1,7 @@
unknown: dict_open: fail:1maps
unknown: dict_register: fail:1maps(0,lock) 1
"": not found
-unknown: warning: fail:1maps lookup of foobar failed
+unknown: warning: fail:1maps lookup error for "foobar"
unknown: maps_find: whatever: foobar: search aborted
"foobar": lookup error
unknown: maps_free: fail:1maps(0,lock)
diff --git a/postfix/src/global/namadr_list.c b/postfix/src/global/namadr_list.c
index 66a1fb934..fc7f681ed 100644
--- a/postfix/src/global/namadr_list.c
+++ b/postfix/src/global/namadr_list.c
@@ -94,6 +94,7 @@
#include
#include
#include
+#include
static void usage(char *progname)
{
@@ -120,6 +121,7 @@ int main(int argc, char **argv)
}
if (argc != optind + 3)
usage(argv[0]);
+ dict_allow_surrogate = 1;
list = namadr_list_init(MATCH_FLAG_PARENT | MATCH_FLAG_RETURN, argv[optind]);
host = argv[optind + 1];
addr = argv[optind + 2];
diff --git a/postfix/src/global/namadr_list.in b/postfix/src/global/namadr_list.in
index c1cd8f2c4..dfe6ed20c 100644
--- a/postfix/src/global/namadr_list.in
+++ b/postfix/src/global/namadr_list.in
@@ -39,3 +39,4 @@ env foo=x ./namadr_list !!environ:junk foo 168.100.189.3
env foo=x ./namadr_list !!environ:junk bar 168.100.189.3
./namadr_list fail:1 bar 168.100.189.3
./namadr_list !fail:1 bar 168.100.189.3
+./namadr_list `pwd`/nosuchfile bar 168.100.189.3
diff --git a/postfix/src/global/namadr_list.ref b/postfix/src/global/namadr_list.ref
index 0515ecea5..1f62aa216 100644
--- a/postfix/src/global/namadr_list.ref
+++ b/postfix/src/global/namadr_list.ref
@@ -44,3 +44,7 @@ bar/168.100.189.3: NO
bar/168.100.189.3: ERROR
./namadr_list: warning: fail:1: table lookup problem
bar/168.100.189.3: ERROR
+./namadr_list: error: open file /home/wietse/postfix-2.9-20120114/src/global/nosuchfile: No such file or directory
+./namadr_list: warning: non-existent:/home/wietse/postfix-2.9-20120114/src/global/nosuchfile is unavailable. open file /home/wietse/postfix-2.9-20120114/src/global/nosuchfile: No such file or directory
+./namadr_list: warning: non-existent:/home/wietse/postfix-2.9-20120114/src/global/nosuchfile: table lookup problem
+bar/168.100.189.3: ERROR
diff --git a/postfix/src/global/smtp_stream.c b/postfix/src/global/smtp_stream.c
index fb68e5057..4893f4082 100644
--- a/postfix/src/global/smtp_stream.c
+++ b/postfix/src/global/smtp_stream.c
@@ -122,8 +122,8 @@
/* the application.
/* This error is never generated by the smtp_stream(3) module, but
/* is defined for application-specific use.
-/* .IP SMTP_ERR_APPL
-/* Application error - the program cannot proceed with this
+/* .IP SMTP_ERR_DATA
+/* Application data error - the program cannot proceed with this
/* SMTP session.
/* .IP SMTP_ERR_NONE
/* A non-error code that makes setjmp()/longjmp() convenient
diff --git a/postfix/src/global/smtp_stream.h b/postfix/src/global/smtp_stream.h
index 599bcfa29..bdb143675 100644
--- a/postfix/src/global/smtp_stream.h
+++ b/postfix/src/global/smtp_stream.h
@@ -31,7 +31,7 @@
#define SMTP_ERR_TIME 2 /* time out */
#define SMTP_ERR_QUIET 3 /* silent cleanup (application) */
#define SMTP_ERR_NONE 4 /* non-error case */
-#define SMTP_ERR_APPL 5 /* application error - can't proceed */
+#define SMTP_ERR_DATA 5 /* application data error */
extern void smtp_stream_setup(VSTREAM *, int, int);
extern void PRINTFLIKE(2, 3) smtp_printf(VSTREAM *, const char *,...);
diff --git a/postfix/src/master/event_server.c b/postfix/src/master/event_server.c
index 37d7e0970..b58d22578 100644
--- a/postfix/src/master/event_server.c
+++ b/postfix/src/master/event_server.c
@@ -594,6 +594,12 @@ NORETURN event_server_main(int argc, char **argv, MULTI_SERVER_FN service,...)
*/
mail_dict_init();
+ /*
+ * After database open error, continue execution with reduced
+ * functionality.
+ */
+ dict_allow_surrogate = 1;
+
/*
* Pick up policy settings from master process. Shut up error messages to
* stderr, because no-one is going to see them.
diff --git a/postfix/src/master/multi_server.c b/postfix/src/master/multi_server.c
index 0b5e678c6..c16118db3 100644
--- a/postfix/src/master/multi_server.c
+++ b/postfix/src/master/multi_server.c
@@ -589,6 +589,12 @@ NORETURN multi_server_main(int argc, char **argv, MULTI_SERVER_FN service,...)
* Register dictionaries that use higher-level interfaces and protocols.
*/
mail_dict_init();
+
+ /*
+ * After database open error, continue execution with reduced
+ * functionality.
+ */
+ dict_allow_surrogate = 1;
/*
* Pick up policy settings from master process. Shut up error messages to
diff --git a/postfix/src/master/single_server.c b/postfix/src/master/single_server.c
index aba479cd5..7b6d8bfd7 100644
--- a/postfix/src/master/single_server.c
+++ b/postfix/src/master/single_server.c
@@ -472,6 +472,12 @@ NORETURN single_server_main(int argc, char **argv, SINGLE_SERVER_FN service,...)
* Register dictionaries that use higher-level interfaces and protocols.
*/
mail_dict_init();
+
+ /*
+ * After database open error, continue execution with reduced
+ * functionality.
+ */
+ dict_allow_surrogate = 1;
/*
* Pick up policy settings from master process. Shut up error messages to
diff --git a/postfix/src/master/trigger_server.c b/postfix/src/master/trigger_server.c
index df3bf2054..bc4a16cea 100644
--- a/postfix/src/master/trigger_server.c
+++ b/postfix/src/master/trigger_server.c
@@ -483,6 +483,12 @@ NORETURN trigger_server_main(int argc, char **argv, TRIGGER_SERVER_FN service,..
* Register dictionaries that use higher-level interfaces and protocols.
*/
mail_dict_init();
+
+ /*
+ * After database open error, continue execution with reduced
+ * functionality.
+ */
+ dict_allow_surrogate = 1;
/*
* Pick up policy settings from master process. Shut up error messages to
diff --git a/postfix/src/postconf/postconf_main.c b/postfix/src/postconf/postconf_main.c
index 9411d7dc4..ad96c9afb 100644
--- a/postfix/src/postconf/postconf_main.c
+++ b/postfix/src/postconf/postconf_main.c
@@ -107,7 +107,8 @@ void read_parameters(void)
*/
set_config_dir();
path = concatenate(var_config_dir, "/", MAIN_CONF_FILE, (char *) 0);
- dict_load_file(CONFIG_DICT, path);
+ if (dict_load_file_xt(CONFIG_DICT, path) == 0)
+ msg_fatal("open %s: %m", path);
myfree(path);
}
diff --git a/postfix/src/smtp/smtp.c b/postfix/src/smtp/smtp.c
index 831cfc7bf..a13cd2eef 100644
--- a/postfix/src/smtp/smtp.c
+++ b/postfix/src/smtp/smtp.c
@@ -1103,7 +1103,7 @@ static void pre_init(char *unused_name, char **unused_argv)
* Session cache domain list.
*/
if (*var_smtp_cache_dest)
- smtp_cache_dest = string_list_init(MATCH_FLAG_NONE, var_smtp_cache_dest);
+ smtp_cache_dest = string_list_init(MATCH_FLAG_RETURN, var_smtp_cache_dest);
/*
* EHLO keyword filter.
diff --git a/postfix/src/smtp/smtp_chat.c b/postfix/src/smtp/smtp_chat.c
index 1aefaf412..cad15c4bf 100644
--- a/postfix/src/smtp/smtp_chat.c
+++ b/postfix/src/smtp/smtp_chat.c
@@ -306,7 +306,7 @@ SMTP_RESP *smtp_chat_resp(SMTP_SESSION *session)
smtp_chat_resp_filter->type,
smtp_chat_resp_filter->name,
printable(STR(session->buffer), '?'));
- vstream_longjmp(session->stream, SMTP_ERR_APPL);
+ vstream_longjmp(session->stream, SMTP_ERR_DATA);
}
}
if (chat_append_flag) {
diff --git a/postfix/src/smtp/smtp_proto.c b/postfix/src/smtp/smtp_proto.c
index e348da80f..e1775b1cf 100644
--- a/postfix/src/smtp/smtp_proto.c
+++ b/postfix/src/smtp/smtp_proto.c
@@ -456,7 +456,7 @@ int smtp_helo(SMTP_STATE *state)
msg_warn("%s: %s map lookup error for %s",
session->state->request->queue_id,
smtp_ehlo_dis_maps->title, state->session->addr);
- vstream_longjmp(session->stream, SMTP_ERR_APPL);
+ vstream_longjmp(session->stream, SMTP_ERR_DATA);
}
discard_mask = ehlo_mask(ehlo_words);
if (discard_mask && !(discard_mask & EHLO_MASK_SILENT))
@@ -996,7 +996,7 @@ static void smtp_header_rewrite(void *context, int header_class,
if (result == HBC_CHECKS_STAT_ERROR) {
msg_warn("%s: smtp header checks lookup error",
state->request->queue_id);
- vstream_longjmp(state->session->stream, SMTP_ERR_APPL);
+ vstream_longjmp(state->session->stream, SMTP_ERR_DATA);
}
if (result != STR(buf)) {
vstring_strcpy(buf, result);
@@ -1098,7 +1098,7 @@ static void smtp_body_rewrite(void *context, int type,
} else if (result == HBC_CHECKS_STAT_ERROR) {
msg_warn("%s: smtp body checks lookup error",
state->request->queue_id);
- vstream_longjmp(state->session->stream, SMTP_ERR_APPL);
+ vstream_longjmp(state->session->stream, SMTP_ERR_DATA);
} else if (result != 0) {
smtp_text_out(state, type, result, strlen(result), offset);
myfree(result);
diff --git a/postfix/src/smtp/smtp_sasl_glue.c b/postfix/src/smtp/smtp_sasl_glue.c
index 16fc940f4..7ba70bb1d 100644
--- a/postfix/src/smtp/smtp_sasl_glue.c
+++ b/postfix/src/smtp/smtp_sasl_glue.c
@@ -206,7 +206,7 @@ int smtp_sasl_passwd_lookup(SMTP_SESSION *session)
} else if (smtp_sasl_passwd_map->error) {
msg_warn("%s: %s lookup error",
state->request->queue_id, smtp_sasl_passwd_map->title);
- vstream_longjmp(session->stream, SMTP_ERR_APPL);
+ vstream_longjmp(session->stream, SMTP_ERR_DATA);
} else {
if (msg_verbose)
msg_info("%s: no auth info found (sender=`%s', host=`%s')",
diff --git a/postfix/src/smtp/smtp_session.c b/postfix/src/smtp/smtp_session.c
index 4ee8e872a..e98695450 100644
--- a/postfix/src/smtp/smtp_session.c
+++ b/postfix/src/smtp/smtp_session.c
@@ -227,7 +227,7 @@ static int tls_policy_lookup_one(SMTP_SESSION *session, int *site_level,
msg_warn("%s: %s lookup error for %s",
session->state->request->queue_id,
tls_policy->title, site_name);
- vstream_longjmp(session->stream, SMTP_ERR_APPL);
+ vstream_longjmp(session->stream, SMTP_ERR_DATA);
}
if (cbuf == 0)
cbuf = vstring_alloc(10);
diff --git a/postfix/src/smtp/smtp_trouble.c b/postfix/src/smtp/smtp_trouble.c
index 706bc5d4d..0e7fafd46 100644
--- a/postfix/src/smtp/smtp_trouble.c
+++ b/postfix/src/smtp/smtp_trouble.c
@@ -431,7 +431,8 @@ int smtp_stream_except(SMTP_STATE *state, int code, const char *description)
dsb_simple(why, "4.4.2", "conversation with %s timed out while %s",
session->namaddr, description);
break;
- case SMTP_ERR_APPL:
+ case SMTP_ERR_DATA:
+ session->error_mask |= MAIL_ERROR_DATA;
dsb_simple(why, "4.3.0", "local data error while talking to %s",
session->namaddr);
}
diff --git a/postfix/src/smtpd/smtpd.c b/postfix/src/smtpd/smtpd.c
index d4b30a6d2..b2bfd231d 100644
--- a/postfix/src/smtpd/smtpd.c
+++ b/postfix/src/smtpd/smtpd.c
@@ -1390,8 +1390,9 @@ static int sasl_client_exception(SMTPD_STATE *state)
if (sasl_exceptions_networks == 0)
return (0);
- match = namadr_list_match(sasl_exceptions_networks,
- state->name, state->addr);
+ if ((match = namadr_list_match(sasl_exceptions_networks,
+ state->name, state->addr)) == 0)
+ match = sasl_exceptions_networks->error;
if (msg_verbose)
msg_info("sasl_exceptions: %s, match=%d",
@@ -1554,13 +1555,31 @@ static int helo_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
return (0);
}
+/* cant_announce_feature - explain and terminate this session */
+
+static NORETURN cant_announce_feature(SMTPD_STATE *state, const char *feature)
+{
+ msg_warn("don't know if feature %s should be announced to %s",
+ feature, state->namaddr);
+ vstream_longjmp(state->client, SMTP_ERR_DATA);
+}
+
+/* cant_permit_command - explain and terminate this session */
+
+static NORETURN cant_permit_command(SMTPD_STATE *state, const char *command)
+{
+ msg_warn("don't know if command %s should be allowed from %s",
+ command, state->namaddr);
+ vstream_longjmp(state->client, SMTP_ERR_DATA);
+}
+
/* ehlo_cmd - process EHLO command */
static int ehlo_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
{
const char *err;
int discard_mask;
- VSTRING *reply_buf;
+ char **cpp;
/*
* XXX 2821 new feature: Section 4.1.4 specifies that a server must clear
@@ -1627,23 +1646,21 @@ static int ehlo_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
}
/*
- * Build the EHLO response, suppressing features as requested. We store
- * each output line in a one-element output queue, where it sits until we
- * know if we need to prepend "250-" or "250 " to it. Each time we
- * enqueue a reply line we flush the one that sits in the queue. We use a
- * couple ugly macros to avoid making mistakes in code that repeats a
- * lot.
+ * Build the EHLO response, producing no output until we know what to
+ * send - this simplifies exception handling. The CRLF record boundaries
+ * don't exist at this level in the code, so we represent multi-line
+ * output as an array of single-line responses.
*/
-#define ENQUEUE_FIX_REPLY(state, reply_buf, cmd) \
+#define EHLO_APPEND(state, cmd) \
do { \
- smtpd_chat_reply((state), "250-%s", STR(reply_buf)); \
- vstring_strcpy((reply_buf), (cmd)); \
+ vstring_sprintf((state)->ehlo_buf, (cmd)); \
+ argv_add((state)->ehlo_argv, STR((state)->ehlo_buf), (char *) 0); \
} while (0)
-#define ENQUEUE_FMT_REPLY(state, reply_buf, fmt, arg) \
+#define EHLO_APPEND1(state, cmd, arg) \
do { \
- smtpd_chat_reply((state), "250-%s", STR(reply_buf)); \
- vstring_sprintf((reply_buf), (fmt), (arg)); \
+ vstring_sprintf((state)->ehlo_buf, (cmd), (arg)); \
+ argv_add((state)->ehlo_argv, STR((state)->ehlo_buf), (char *) 0); \
} while (0)
/*
@@ -1659,72 +1676,103 @@ static int ehlo_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
if ((discard_mask & EHLO_MASK_ENHANCEDSTATUSCODES) == 0)
if (discard_mask && !(discard_mask & EHLO_MASK_SILENT))
msg_info("discarding EHLO keywords: %s", str_ehlo_mask(discard_mask));
+ if (ehlo_discard_maps && ehlo_discard_maps->error) {
+ msg_warn("don't know what features to announce in EHLO");
+ vstream_longjmp(state->client, SMTP_ERR_DATA);
+ }
+
+ /*
+ * These may still exist after a prior exception.
+ */
+ if (state->ehlo_argv == 0) {
+ state->ehlo_argv = argv_alloc(10);
+ state->ehlo_buf = vstring_alloc(10);
+ } else
+ argv_truncate(state->ehlo_argv, 0);
- reply_buf = vstring_alloc(10);
- vstring_strcpy(reply_buf, var_myhostname);
+ EHLO_APPEND1(state, "%s", var_myhostname);
if ((discard_mask & EHLO_MASK_PIPELINING) == 0)
- ENQUEUE_FIX_REPLY(state, reply_buf, "PIPELINING");
+ EHLO_APPEND(state, "PIPELINING");
if ((discard_mask & EHLO_MASK_SIZE) == 0) {
if (var_message_limit)
- ENQUEUE_FMT_REPLY(state, reply_buf, "SIZE %lu",
- (unsigned long) var_message_limit); /* XXX */
+ EHLO_APPEND1(state, "SIZE %lu",
+ (unsigned long) var_message_limit); /* XXX */
else
- ENQUEUE_FIX_REPLY(state, reply_buf, "SIZE");
+ EHLO_APPEND(state, "SIZE");
}
if ((discard_mask & EHLO_MASK_VRFY) == 0)
if (var_disable_vrfy_cmd == 0)
- ENQUEUE_FIX_REPLY(state, reply_buf, SMTPD_CMD_VRFY);
+ EHLO_APPEND(state, SMTPD_CMD_VRFY);
if ((discard_mask & EHLO_MASK_ETRN) == 0)
- ENQUEUE_FIX_REPLY(state, reply_buf, SMTPD_CMD_ETRN);
+ EHLO_APPEND(state, SMTPD_CMD_ETRN);
#ifdef USE_TLS
if ((discard_mask & EHLO_MASK_STARTTLS) == 0)
if (var_smtpd_use_tls && (!state->tls_context))
- ENQUEUE_FIX_REPLY(state, reply_buf, SMTPD_CMD_STARTTLS);
+ EHLO_APPEND(state, SMTPD_CMD_STARTTLS);
#endif
#ifdef USE_SASL_AUTH
+#ifndef AUTH_CMD
+#define AUTH_CMD "AUTH"
+#endif
if ((discard_mask & EHLO_MASK_AUTH) == 0) {
if (smtpd_sasl_is_active(state) && !sasl_client_exception(state)) {
- ENQUEUE_FMT_REPLY(state, reply_buf, "AUTH %s",
- state->sasl_mechanism_list);
+ EHLO_APPEND1(state, "AUTH %s", state->sasl_mechanism_list);
if (var_broken_auth_clients)
- ENQUEUE_FMT_REPLY(state, reply_buf, "AUTH=%s",
- state->sasl_mechanism_list);
- }
+ EHLO_APPEND1(state, "AUTH=%s", state->sasl_mechanism_list);
+ } else if (sasl_exceptions_networks && sasl_exceptions_networks->error)
+ cant_announce_feature(state, AUTH_CMD);
}
#define XCLIENT_LOGIN_KLUDGE " " XCLIENT_LOGIN
#else
#define XCLIENT_LOGIN_KLUDGE ""
#endif
- if ((discard_mask & EHLO_MASK_VERP) == 0)
+ if ((discard_mask & EHLO_MASK_VERP) == 0) {
if (namadr_list_match(verp_clients, state->name, state->addr))
- ENQUEUE_FIX_REPLY(state, reply_buf, VERP_CMD);
+ EHLO_APPEND(state, VERP_CMD);
+ else if (verp_clients && verp_clients->error)
+ cant_announce_feature(state, VERP_CMD);
+ }
/* XCLIENT must not override its own access control. */
- if ((discard_mask & EHLO_MASK_XCLIENT) == 0)
+ if ((discard_mask & EHLO_MASK_XCLIENT) == 0) {
if (xclient_allowed)
- ENQUEUE_FIX_REPLY(state, reply_buf, XCLIENT_CMD
- " " XCLIENT_NAME " " XCLIENT_ADDR
- " " XCLIENT_PROTO " " XCLIENT_HELO
- " " XCLIENT_REVERSE_NAME " " XCLIENT_PORT
- XCLIENT_LOGIN_KLUDGE);
- if ((discard_mask & EHLO_MASK_XFORWARD) == 0)
+ EHLO_APPEND(state, XCLIENT_CMD
+ " " XCLIENT_NAME " " XCLIENT_ADDR
+ " " XCLIENT_PROTO " " XCLIENT_HELO
+ " " XCLIENT_REVERSE_NAME " " XCLIENT_PORT
+ XCLIENT_LOGIN_KLUDGE);
+ else if (xclient_hosts && xclient_hosts->error)
+ cant_announce_feature(state, XCLIENT_CMD);
+ }
+ if ((discard_mask & EHLO_MASK_XFORWARD) == 0) {
if (xforward_allowed)
- ENQUEUE_FIX_REPLY(state, reply_buf, XFORWARD_CMD
- " " XFORWARD_NAME " " XFORWARD_ADDR
- " " XFORWARD_PROTO " " XFORWARD_HELO
- " " XFORWARD_DOMAIN " " XFORWARD_PORT
- " " XFORWARD_IDENT);
+ EHLO_APPEND(state, XFORWARD_CMD
+ " " XFORWARD_NAME " " XFORWARD_ADDR
+ " " XFORWARD_PROTO " " XFORWARD_HELO
+ " " XFORWARD_DOMAIN " " XFORWARD_PORT
+ " " XFORWARD_IDENT);
+ else if (xforward_hosts && xforward_hosts->error)
+ cant_announce_feature(state, XFORWARD_CMD);
+ }
if ((discard_mask & EHLO_MASK_ENHANCEDSTATUSCODES) == 0)
- ENQUEUE_FIX_REPLY(state, reply_buf, "ENHANCEDSTATUSCODES");
+ EHLO_APPEND(state, "ENHANCEDSTATUSCODES");
if ((discard_mask & EHLO_MASK_8BITMIME) == 0)
- ENQUEUE_FIX_REPLY(state, reply_buf, "8BITMIME");
+ EHLO_APPEND(state, "8BITMIME");
if ((discard_mask & EHLO_MASK_DSN) == 0)
- ENQUEUE_FIX_REPLY(state, reply_buf, "DSN");
- smtpd_chat_reply(state, "250 %s", STR(reply_buf));
+ EHLO_APPEND(state, "DSN");
+
+ /*
+ * Send the reply.
+ */
+ for (cpp = state->ehlo_argv->argv; *cpp; cpp++)
+ smtpd_chat_reply(state, "250%c%s", cpp[1] ? '-' : ' ', *cpp);
/*
* Clean up.
*/
- vstring_free(reply_buf);
+ argv_free(state->ehlo_argv);
+ state->ehlo_argv = 0;
+ vstring_free(state->ehlo_buf);
+ state->ehlo_buf = 0;
return (0);
}
@@ -1739,6 +1787,14 @@ static void helo_reset(SMTPD_STATE *state)
if (SMTPD_STAND_ALONE(state) == 0 && smtpd_milters != 0)
milter_abort(smtpd_milters);
}
+ if (state->ehlo_argv) {
+ argv_free(state->ehlo_argv);
+ state->ehlo_argv = 0;
+ }
+ if (state->ehlo_buf) {
+ vstring_free(state->ehlo_buf);
+ state->ehlo_buf = 0;
+ }
}
/* mail_open_stream - open mail queue file or IPC stream */
@@ -3486,6 +3542,8 @@ static int xclient_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
XCLIENT_CMD);
return (-1);
}
+ if (xclient_hosts && xclient_hosts->error)
+ cant_permit_command(state, XCLIENT_CMD);
if (!xclient_allowed) {
state->error_mask |= MAIL_ERROR_POLICY;
smtpd_chat_reply(state, "550 5.7.0 Error: insufficient authorization");
@@ -3780,6 +3838,8 @@ static int xforward_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
XFORWARD_CMD);
return (-1);
}
+ if (xforward_hosts && xforward_hosts->error)
+ cant_permit_command(state, XFORWARD_CMD);
if (!xforward_allowed) {
state->error_mask |= MAIL_ERROR_POLICY;
smtpd_chat_reply(state, "550 5.7.0 Error: insufficient authorization");
@@ -4429,11 +4489,12 @@ static void smtpd_proto(SMTPD_STATE *state)
case SMTP_ERR_QUIET:
break;
- case SMTP_ERR_APPL:
+ case SMTP_ERR_DATA:
msg_info("%s: reject: %s from %s: "
"421 4.3.0 %s Server configuration error",
(state->queue_id ? state->queue_id : "NOQUEUE"),
state->where, state->namaddr, var_myhostname);
+ state->error_mask |= MAIL_ERROR_DATA;
if (vstream_setjmp(state->client) == 0)
smtpd_chat_reply(state, "421 4.3.0 %s Server configuration error",
var_myhostname);
@@ -4538,8 +4599,6 @@ static void smtpd_proto(SMTPD_STATE *state)
if (ehlo_discard_maps == 0
|| (ehlo_words = maps_find(ehlo_discard_maps, state->addr, 0)) == 0)
ehlo_words = var_smtpd_ehlo_dis_words;
- if (ehlo_discard_maps && ehlo_discard_maps->error)
- vstream_longjmp(state->client, SMTP_ERR_APPL);
state->ehlo_discard_mask = ehlo_mask(ehlo_words);
/* XXX We use the real client for connect access control. */
@@ -4638,7 +4697,7 @@ static void smtpd_proto(SMTPD_STATE *state)
msg_warn("%s:%s lookup error for \"%.100s\"",
smtpd_cmd_filter->type, smtpd_cmd_filter->name,
printable(STR(state->buffer), '?'));
- vstream_longjmp(state->client, SMTP_ERR_APPL);
+ vstream_longjmp(state->client, SMTP_ERR_DATA);
}
}
if ((argc = smtpd_token(vstring_str(state->buffer), &argv)) == 0) {
@@ -4647,6 +4706,7 @@ static void smtpd_proto(SMTPD_STATE *state)
state->error_count++;
continue;
}
+ /* Ignore smtpd_noop_cmds lookup errors. Non-critical feature. */
if (*var_smtpd_noop_cmds
&& string_list_match(smtpd_noop_cmds, argv[0].strval)) {
smtpd_chat_reply(state, "250 2.0.0 Ok");
@@ -4657,6 +4717,7 @@ static void smtpd_proto(SMTPD_STATE *state)
for (cmdp = smtpd_cmd_table; cmdp->name != 0; cmdp++)
if (strcasecmp(argv[0].strval, cmdp->name) == 0)
break;
+ /* Ignore smtpd_forbid_cmds lookup errors. Non-critical feature. */
if (cmdp->name == 0) {
state->where = SMTPD_CMD_UNKNOWN;
if (is_header(argv[0].strval)
@@ -4886,11 +4947,11 @@ static void pre_jail_init(char *unused_name, char **unused_argv)
* Initialize blacklist/etc. patterns before entering the chroot jail, in
* case they specify a filename pattern.
*/
- smtpd_noop_cmds = string_list_init(MATCH_FLAG_NONE, var_smtpd_noop_cmds);
- smtpd_forbid_cmds = string_list_init(MATCH_FLAG_NONE, var_smtpd_forbid_cmds);
- verp_clients = namadr_list_init(MATCH_FLAG_NONE, var_verp_clients);
- xclient_hosts = namadr_list_init(MATCH_FLAG_NONE, var_xclient_hosts);
- xforward_hosts = namadr_list_init(MATCH_FLAG_NONE, var_xforward_hosts);
+ smtpd_noop_cmds = string_list_init(MATCH_FLAG_RETURN, var_smtpd_noop_cmds);
+ smtpd_forbid_cmds = string_list_init(MATCH_FLAG_RETURN, var_smtpd_forbid_cmds);
+ verp_clients = namadr_list_init(MATCH_FLAG_RETURN, var_verp_clients);
+ xclient_hosts = namadr_list_init(MATCH_FLAG_RETURN, var_xclient_hosts);
+ xforward_hosts = namadr_list_init(MATCH_FLAG_RETURN, var_xforward_hosts);
hogger_list = namadr_list_init(MATCH_FLAG_RETURN, var_smtpd_hoggers);
/*
@@ -4914,7 +4975,7 @@ static void pre_jail_init(char *unused_name, char **unused_argv)
if (*var_smtpd_sasl_exceptions_networks)
sasl_exceptions_networks =
- namadr_list_init(MATCH_FLAG_NONE,
+ namadr_list_init(MATCH_FLAG_RETURN,
var_smtpd_sasl_exceptions_networks);
#else
msg_warn("%s is true, but SASL support is not compiled in",
diff --git a/postfix/src/smtpd/smtpd.h b/postfix/src/smtpd/smtpd.h
index 4001a8145..cc4590682 100644
--- a/postfix/src/smtpd/smtpd.h
+++ b/postfix/src/smtpd/smtpd.h
@@ -177,6 +177,12 @@ typedef struct {
const char **milter_argv; /* SMTP command vector */
ssize_t milter_argc; /* SMTP command vector */
const char *milter_reject_text; /* input to call-back from Milter */
+
+ /*
+ * EHLO temporary space.
+ */
+ VSTRING *ehlo_buf;
+ ARGV *ehlo_argv;
} SMTPD_STATE;
#define SMTPD_FLAG_HANGUP (1<<0) /* 421/521 disconnect */
diff --git a/postfix/src/smtpd/smtpd_chat.c b/postfix/src/smtpd/smtpd_chat.c
index 9cc30385e..a1016cd9c 100644
--- a/postfix/src/smtpd/smtpd_chat.c
+++ b/postfix/src/smtpd/smtpd_chat.c
@@ -182,7 +182,10 @@ void smtpd_chat_reply(SMTPD_STATE *state, const char *format,...)
/* This is why we use strlen() above instead of VSTRING_LEN(). */
if ((next = strstr(cp, "\r\n")) != 0) {
*next = 0;
- cp[3] = '-'; /* contact footer kludge */
+ if (next[2] != 0)
+ cp[3] = '-'; /* contact footer kludge */
+ else
+ next = end; /* strip trailing \r\n */
} else {
next = end;
}
diff --git a/postfix/src/smtpd/smtpd_check.c b/postfix/src/smtpd/smtpd_check.c
index 5bfaf7357..341ae3d42 100644
--- a/postfix/src/smtpd/smtpd_check.c
+++ b/postfix/src/smtpd/smtpd_check.c
@@ -897,7 +897,7 @@ static int defer_if(SMTPD_DEFER *defer, int error_class,
static NORETURN reject_dict_retry(SMTPD_STATE *state, const char *reply_name)
{
- longjmp(smtpd_check_buf, smtpd_check_reject(state, MAIL_ERROR_RESOURCE,
+ longjmp(smtpd_check_buf, smtpd_check_reject(state, MAIL_ERROR_DATA,
451, "4.3.0",
"<%s>: Temporary lookup failure",
reply_name));
diff --git a/postfix/src/smtpd/smtpd_error.ref b/postfix/src/smtpd/smtpd_error.ref
index b30f7c300..44675cb2e 100644
--- a/postfix/src/smtpd/smtpd_error.ref
+++ b/postfix/src/smtpd/smtpd_error.ref
@@ -70,8 +70,8 @@ OK
>>> #
>>> rcpt reject@dunno.domain
./smtpd_check: warning: non-null host address bits in "168.100.189.1/27", perhaps you should use "168.100.189.0/27" instead
-./smtpd_check: : reject: RCPT from foo.dunno.com[131.155.210.17]: 451 4.3.5 Server configuration error; from= to= proto=SMTP helo=
-451 4.3.5 Server configuration error
+./smtpd_check: : reject: RCPT from foo.dunno.com[131.155.210.17]: 451 4.3.0 : Temporary lookup failure; from= to= proto=SMTP helo=
+451 4.3.0 : Temporary lookup failure
>>> #
>>> # check_sender_access specific
>>> #
@@ -91,7 +91,7 @@ OK
>>> recipient_restrictions permit_tls_clientcerts
OK
>>> rcpt reject@dunno.domain
-./smtpd_check: warning: fail:1_certs lookup of abcdef failed
+./smtpd_check: warning: fail:1_certs lookup error for "abcdef"
./smtpd_check: warning: relay_clientcerts: lookup error for fingerprint 'abcdef', pkey fingerprint abcdef
./smtpd_check: : reject: RCPT from foo.dunno.com[131.155.210.17]: 451 4.3.0 : Temporary lookup failure; from=<> to= proto=SMTP helo=
451 4.3.0 : Temporary lookup failure
@@ -130,6 +130,6 @@ OK
>>> virtual_alias_maps fail:1_virtual
OK
>>> rcpt user@example.com
-./smtpd_check: warning: fail:1_virtual lookup of user@example.com failed
+./smtpd_check: warning: fail:1_virtual lookup error for "user@example.com"
./smtpd_check: : reject: RCPT from foo.dunno.com[131.155.210.17]: 451 4.3.0 : Temporary lookup failure; from=<> to= proto=SMTP helo=
451 4.3.0 : Temporary lookup failure
diff --git a/postfix/src/smtpd/smtpd_state.c b/postfix/src/smtpd/smtpd_state.c
index 43baa74bc..c03dfbef8 100644
--- a/postfix/src/smtpd/smtpd_state.c
+++ b/postfix/src/smtpd/smtpd_state.c
@@ -174,6 +174,9 @@ void smtpd_state_init(SMTPD_STATE *state, VSTREAM *stream,
* Initialize the conversation history.
*/
smtpd_chat_reset(state);
+
+ state->ehlo_argv = 0;
+ state->ehlo_buf = 0;
}
/* smtpd_state_reset - cleanup after disconnect */
diff --git a/postfix/src/util/Makefile.in b/postfix/src/util/Makefile.in
index 368a1cea8..cff7d8a05 100644
--- a/postfix/src/util/Makefile.in
+++ b/postfix/src/util/Makefile.in
@@ -34,7 +34,7 @@ SRCS = alldig.c allprint.c argv.c argv_split.c attr_clnt.c attr_print0.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 stream_pass_connect.c base32_code.c dict_test.c \
- dict_fail.c msg_rate_delay.c
+ dict_fail.c msg_rate_delay.c dict_surrogate.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 \
@@ -70,7 +70,7 @@ OBJS = alldig.o allprint.o argv.o argv_split.o attr_clnt.o attr_print0.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 stream_pass_connect.o base32_code.o dict_test.o \
- dict_fail.o msg_rate_delay.o
+ dict_fail.o msg_rate_delay.o dict_surrogate.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 \
@@ -444,7 +444,7 @@ tests: valid_hostname_test mac_expand_test dict_test unescape_test \
attr_scan64_test attr_scan0_test dict_pcre_test host_port_test \
dict_cidr_test attr_scan_plain_test htable_test hex_code_test \
myaddrinfo_test format_tv_test ip_match_test name_mask_tests \
- base32_code_test
+ base32_code_test dict_thash_test
root_tests:
@@ -628,6 +628,11 @@ name_mask_test9: name_mask name_mask.in name_mask.ref9
base32_code_test: base32_code
./base32_code
+dict_thash_test: ../postmap/postmap dict_thash.map
+ ../postmap/postmap -s texthash:dict_thash.map >dict_thash.tmp 2>&1
+ sort dict_thash.tmp | diff -b dict_thash.map -
+ rm -f dict_thash.tmp
+
depend: $(MAKES)
(sed '1,/^# do not edit/!d' Makefile.in; \
set -e; for i in [a-z][a-z0-9]*.c; do \
@@ -1042,6 +1047,15 @@ dict_static.o: sys_defs.h
dict_static.o: vbuf.h
dict_static.o: vstream.h
dict_static.o: vstring.h
+dict_surrogate.o: argv.h
+dict_surrogate.o: dict.h
+dict_surrogate.o: dict_surrogate.c
+dict_surrogate.o: msg.h
+dict_surrogate.o: mymalloc.h
+dict_surrogate.o: sys_defs.h
+dict_surrogate.o: vbuf.h
+dict_surrogate.o: vstream.h
+dict_surrogate.o: vstring.h
dict_tcp.o: argv.h
dict_tcp.o: connect.h
dict_tcp.o: dict.h
diff --git a/postfix/src/util/dict.c b/postfix/src/util/dict.c
index baeff9d20..875c5c513 100644
--- a/postfix/src/util/dict.c
+++ b/postfix/src/util/dict.c
@@ -49,7 +49,7 @@
/*
/* const char *dict_changed_name()
/* AUXILIARY FUNCTIONS
-/* void dict_load_file(dict_name, path)
+/* int dict_load_file_xt(dict_name, path)
/* const char *dict_name;
/* const char *path;
/*
@@ -141,13 +141,14 @@
/* be re-opened because it has changed or because it was unlinked.
/* A non-zero result is the name of a changed dictionary.
/*
-/* dict_load_file() reads name-value entries from the named file.
+/* dict_load_file_xt() reads name-value entries from the named file.
/* Lines that begin with whitespace are concatenated to the preceding
/* line (the newline is deleted).
/* Each entry is stored in the dictionary named by \fIdict_name\fR.
+/* The result is zero if the file could not be opened.
/*
/* dict_load_fp() reads name-value entries from an open stream.
-/* It has the same semantics as the dict_load_file() function.
+/* It has the same semantics as the dict_load_file_xt() function.
/*
/* dict_flags_str() returns a printable representation of the
/* specified dictionary flags. The result is overwritten upon
@@ -383,9 +384,9 @@ int dict_error(const char *dict_name)
return (dict ? dict->error : DICT_ERR_NONE);
}
-/* dict_load_file - read entries from text file */
+/* dict_load_file_xt - read entries from text file */
-void dict_load_file(const char *dict_name, const char *path)
+int dict_load_file_xt(const char *dict_name, const char *path)
{
VSTREAM *fp;
struct stat st;
@@ -398,7 +399,7 @@ void dict_load_file(const char *dict_name, const char *path)
*/
for (before = time((time_t *) 0); /* see below */ ; before = after) {
if ((fp = vstream_fopen(path, O_RDONLY, 0)) == 0)
- msg_fatal("open %s: %m", path);
+ return (0);
dict_load_fp(dict_name, fp);
if (fstat(vstream_fileno(fp), &st) < 0)
msg_fatal("fstat %s: %m", path);
@@ -411,6 +412,7 @@ void dict_load_file(const char *dict_name, const char *path)
msg_info("pausing to let %s cool down", path);
doze(300000);
}
+ return (1);
}
/* dict_load_fp - read entries from open stream */
diff --git a/postfix/src/util/dict.h b/postfix/src/util/dict.h
index 90536a226..f8e91a405 100644
--- a/postfix/src/util/dict.h
+++ b/postfix/src/util/dict.h
@@ -96,7 +96,8 @@ extern DICT *dict_debug(DICT *);
* map implementation itself upon open, lookup etc. requests.
*
* DICT_FLAG_RQST_MASK - all requestor flags, including paranoid flags, that
- * the requestor may change between open, lookup etc. requests.
+ * the requestor may change between open, lookup etc. requests. These
+ * specify requestor properties, not map properties.
*
* DICT_FLAG_INST_MASK - none of the above flags. The requestor may not change
* these flags between open, lookup, etc. requests (although a map may make
@@ -154,7 +155,7 @@ extern int dict_update(const char *, const char *, const char *);
extern const char *dict_lookup(const char *, const char *);
extern int dict_delete(const char *, const char *);
extern int dict_sequence(const char *, const int, const char **, const char **);
-extern void dict_load_file(const char *, const char *);
+extern int dict_load_file_xt(const char *, const char *);
extern void dict_load_fp(const char *, VSTREAM *);
extern const char *dict_eval(const char *, const char *, int);
extern int dict_error(const char *);
@@ -182,6 +183,18 @@ extern const char *dict_flags_str(int);
*/
void dict_test(int, char **);
+ /*
+ * Behind-the-scenes support to continue execution with reduced
+ * functionality.
+ */
+extern int dict_allow_surrogate;
+extern DICT *dict_surrogate(const char *, const char *, int, int, const char *,...);
+
+ /*
+ * This name is reserved for matchlist error handling.
+ */
+#define DICT_TYPE_NOFILE "non-existent"
+
/* LICENSE
/* .ad
/* .fi
diff --git a/postfix/src/util/dict_alloc.c b/postfix/src/util/dict_alloc.c
index b68421021..03321f1da 100644
--- a/postfix/src/util/dict_alloc.c
+++ b/postfix/src/util/dict_alloc.c
@@ -65,7 +65,7 @@
static const char *dict_default_lookup(DICT *dict, const char *unused_key)
{
- msg_fatal("%s table %s: lookup operation is not supported",
+ msg_fatal("table %s:%s: lookup operation is not supported",
dict->type, dict->name);
}
@@ -74,7 +74,7 @@ static const char *dict_default_lookup(DICT *dict, const char *unused_key)
static int dict_default_update(DICT *dict, const char *unused_key,
const char *unused_value)
{
- msg_fatal("%s table %s: update operation is not supported",
+ msg_fatal("table %s:%s: update operation is not supported",
dict->type, dict->name);
}
@@ -82,7 +82,7 @@ static int dict_default_update(DICT *dict, const char *unused_key,
static int dict_default_delete(DICT *dict, const char *unused_key)
{
- msg_fatal("%s table %s: delete operation is not supported",
+ msg_fatal("table %s:%s: delete operation is not supported",
dict->type, dict->name);
}
@@ -91,7 +91,7 @@ static int dict_default_delete(DICT *dict, const char *unused_key)
static int dict_default_sequence(DICT *dict, int unused_function,
const char **unused_key, const char **unused_value)
{
- msg_fatal("%s table %s: sequence operation is not supported",
+ msg_fatal("table %s:%s: sequence operation is not supported",
dict->type, dict->name);
}
@@ -99,7 +99,7 @@ static int dict_default_sequence(DICT *dict, int unused_function,
static void dict_default_close(DICT *dict)
{
- msg_fatal("%s table %s: close operation is not supported",
+ msg_fatal("table %s:%s: close operation is not supported",
dict->type, dict->name);
}
diff --git a/postfix/src/util/dict_cdb.c b/postfix/src/util/dict_cdb.c
index 099ecc99d..9de10fdf0 100644
--- a/postfix/src/util/dict_cdb.c
+++ b/postfix/src/util/dict_cdb.c
@@ -184,7 +184,8 @@ static DICT *dict_cdbq_open(const char *path, int dict_flags)
cdb_path = concatenate(path, CDB_SUFFIX, (char *) 0);
if ((fd = open(cdb_path, O_RDONLY)) < 0)
- msg_fatal("open database %s: %m", cdb_path);
+ return (dict_surrogate(DICT_TYPE_CDB, path, O_RDONLY, dict_flags,
+ "open database %s: %m", cdb_path));
dict_cdbq = (DICT_CDBQ *) dict_alloc(DICT_TYPE_CDB,
cdb_path, sizeof(*dict_cdbq));
@@ -339,9 +340,11 @@ static DICT *dict_cdbm_open(const char *path, int dict_flags)
* isn't creating it at the same time.
*/
for (;;) {
- if ((fd = open(tmp_path, O_RDWR | O_CREAT, 0644)) < 0
- || fstat(fd, &st0) < 0)
- msg_fatal("open database %s: %m", tmp_path);
+ if ((fd = open(tmp_path, O_RDWR | O_CREAT, 0644)) < 0)
+ return (dict_surrogate(DICT_TYPE_CDB, path, O_RDWR, dict_flags,
+ "open database %s: %m", tmp_path));
+ if (fstat(fd, &st0) < 0)
+ msg_fatal("fstat(%s): %m", tmp_path);
/*
* Get an exclusive lock - we're going to change the database so we
diff --git a/postfix/src/util/dict_cidr.c b/postfix/src/util/dict_cidr.c
index 97f4867db..5731e58d4 100644
--- a/postfix/src/util/dict_cidr.c
+++ b/postfix/src/util/dict_cidr.c
@@ -176,8 +176,18 @@ DICT *dict_cidr_open(const char *mapname, int open_flags, int dict_flags)
* Sanity checks.
*/
if (open_flags != O_RDONLY)
- msg_fatal("%s:%s map requires O_RDONLY access mode",
- DICT_TYPE_CIDR, mapname);
+ return (dict_surrogate(DICT_TYPE_CIDR, mapname, open_flags, dict_flags,
+ "%s:%s map requires O_RDONLY access mode",
+ DICT_TYPE_CIDR, mapname));
+
+ /*
+ * Open the configuration file.
+ */
+ if ((map_fp = vstream_fopen(mapname, O_RDONLY, 0)) == 0)
+ return (dict_surrogate(DICT_TYPE_CIDR, mapname, open_flags, dict_flags,
+ "open %s: %m", mapname));
+ if (fstat(vstream_fileno(map_fp), &st) < 0)
+ msg_fatal("fstat %s: %m", mapname);
/*
* XXX Eliminate unnecessary queries by setting a flag that says "this
@@ -190,10 +200,6 @@ DICT *dict_cidr_open(const char *mapname, int open_flags, int dict_flags)
dict_cidr->dict.flags = dict_flags | DICT_FLAG_PATTERN;
dict_cidr->head = 0;
- if ((map_fp = vstream_fopen(mapname, O_RDONLY, 0)) == 0)
- msg_fatal("open %s: %m", mapname);
- if (fstat(vstream_fileno(map_fp), &st) < 0)
- msg_fatal("fstat %s: %m", mapname);
dict_cidr->dict.owner.uid = st.st_uid;
dict_cidr->dict.owner.status = (st.st_uid != 0);
diff --git a/postfix/src/util/dict_db.c b/postfix/src/util/dict_db.c
index 898dab53a..29e4cda09 100644
--- a/postfix/src/util/dict_db.c
+++ b/postfix/src/util/dict_db.c
@@ -594,10 +594,11 @@ static DICT *dict_db_open(const char *class, const char *path, int open_flags,
(void) db_version(&major_version, &minor_version, &patch_version);
if (major_version != DB_VERSION_MAJOR || minor_version != DB_VERSION_MINOR)
- msg_fatal("incorrect version of Berkeley DB: "
+ return (dict_surrogate(class, path, open_flags, dict_flags,
+ "incorrect version of Berkeley DB: "
"compiled against %d.%d.%d, run-time linked against %d.%d.%d",
- DB_VERSION_MAJOR, DB_VERSION_MINOR, DB_VERSION_PATCH,
- major_version, minor_version, patch_version);
+ DB_VERSION_MAJOR, DB_VERSION_MINOR, DB_VERSION_PATCH,
+ major_version, minor_version, patch_version));
if (msg_verbose) {
msg_info("Compiled against Berkeley DB: %d.%d.%d\n",
DB_VERSION_MAJOR, DB_VERSION_MINOR, DB_VERSION_PATCH);
@@ -627,7 +628,8 @@ static DICT *dict_db_open(const char *class, const char *path, int open_flags,
if (dict_flags & DICT_FLAG_LOCK) {
if ((lock_fd = open(db_path, LOCK_OPEN_FLAGS(open_flags), 0644)) < 0) {
if (errno != ENOENT)
- msg_fatal("open database %s: %m", db_path);
+ return (dict_surrogate(class, path, open_flags, dict_flags,
+ "open database %s: %m", db_path));
} else {
if (myflock(lock_fd, INTERNAL_LOCK, MYFLOCK_OP_SHARED) < 0)
msg_fatal("shared-lock database %s for open: %m", db_path);
@@ -642,7 +644,8 @@ static DICT *dict_db_open(const char *class, const char *path, int open_flags,
*/
#if DB_VERSION_MAJOR < 2
if ((db = dbopen(db_path, open_flags, 0644, type, tweak)) == 0)
- msg_fatal("open database %s: %m", db_path);
+ return (dict_surrogate(class, path, open_flags, dict_flags,
+ "open database %s: %m", db_path));
dbfd = db->fd(db);
#endif
@@ -658,7 +661,8 @@ static DICT *dict_db_open(const char *class, const char *path, int open_flags,
if (open_flags & O_TRUNC)
db_flags |= DB_TRUNCATE;
if ((errno = db_open(db_path, type, db_flags, 0644, 0, tweak, &db)) != 0)
- msg_fatal("open database %s: %m", db_path);
+ return (dict_surrogate(class, path, open_flags, dict_flags,
+ "open database %s: %m", db_path));
if (db == 0)
msg_panic("db_open null result");
if ((errno = db->fd(db, &dbfd)) != 0)
@@ -686,10 +690,12 @@ static DICT *dict_db_open(const char *class, const char *path, int open_flags,
msg_fatal("set DB hash element count %d: %m", DICT_DB_NELM);
#if DB_VERSION_MAJOR == 5 || (DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR > 0)
if ((errno = db->open(db, 0, db_path, 0, type, db_flags, 0644)) != 0)
- msg_fatal("open database %s: %m", db_path);
+ return (dict_surrogate(class, path, open_flags, dict_flags,
+ "open database %s: %m", db_path));
#elif (DB_VERSION_MAJOR == 3 || DB_VERSION_MAJOR == 4)
if ((errno = db->open(db, db_path, 0, type, db_flags, 0644)) != 0)
- msg_fatal("open database %s: %m", db_path);
+ return (dict_surrogate(class, path, open_flags, dict_flags,
+ "open database %s: %m", db_path));
#else
#error "Unsupported Berkeley DB version"
#endif
diff --git a/postfix/src/util/dict_dbm.c b/postfix/src/util/dict_dbm.c
index bdc4c3888..c0627d87b 100644
--- a/postfix/src/util/dict_dbm.c
+++ b/postfix/src/util/dict_dbm.c
@@ -426,7 +426,8 @@ DICT *dict_dbm_open(const char *path, int open_flags, int dict_flags)
if (dict_flags & DICT_FLAG_LOCK) {
dbm_path = concatenate(path, ".dir", (char *) 0);
if ((lock_fd = open(dbm_path, open_flags, 0644)) < 0)
- msg_fatal("open database %s: %m", dbm_path);
+ return (dict_surrogate(DICT_TYPE_DBM, path, open_flags, dict_flags,
+ "open database %s: %m", dbm_path));
if (myflock(lock_fd, INTERNAL_LOCK, MYFLOCK_OP_SHARED) < 0)
msg_fatal("shared-lock database %s for open: %m", dbm_path);
}
@@ -435,7 +436,8 @@ DICT *dict_dbm_open(const char *path, int open_flags, int dict_flags)
* XXX SunOS 5.x has no const in dbm_open() prototype.
*/
if ((dbm = dbm_open((char *) path, open_flags, 0644)) == 0)
- msg_fatal("open database %s.{dir,pag}: %m", path);
+ return (dict_surrogate(DICT_TYPE_DBM, path, open_flags, dict_flags,
+ "open database %s.{dir,pag}: %m", path));
if (dict_flags & DICT_FLAG_LOCK) {
if (myflock(lock_fd, INTERNAL_LOCK, MYFLOCK_OP_NONE) < 0)
diff --git a/postfix/src/util/dict_fail.c b/postfix/src/util/dict_fail.c
index c4f8f54d5..06c10f50c 100644
--- a/postfix/src/util/dict_fail.c
+++ b/postfix/src/util/dict_fail.c
@@ -6,9 +6,9 @@
/* SYNOPSIS
/* #include
/*
-/* DICT *dict_fail_open(name, name, dict_flags)
+/* DICT *dict_fail_open(name, open_flags, dict_flags)
/* const char *name;
-/* int dummy;
+/* int open_flags;
/* int dict_flags;
/* DESCRIPTION
/* dict_fail_open() implements a dummy dictionary that fails
diff --git a/postfix/src/util/dict_nis.c b/postfix/src/util/dict_nis.c
index a06e5f359..357011f80 100644
--- a/postfix/src/util/dict_nis.c
+++ b/postfix/src/util/dict_nis.c
@@ -226,8 +226,9 @@ DICT *dict_nis_open(const char *map, int open_flags, int dict_flags)
DICT_NIS *dict_nis;
if (open_flags != O_RDONLY)
- msg_fatal("%s:%s map requires O_RDONLY access mode",
- DICT_TYPE_NIS, map);
+ return (dict_surrogate(DICT_TYPE_NIS, map, open_flags, dict_flags,
+ "%s:%s map requires O_RDONLY access mode",
+ DICT_TYPE_NIS, map));
dict_nis = (DICT_NIS *) dict_alloc(DICT_TYPE_NIS, map, sizeof(*dict_nis));
dict_nis->dict.lookup = dict_nis_lookup;
diff --git a/postfix/src/util/dict_nisplus.c b/postfix/src/util/dict_nisplus.c
index 819bf0750..1f5e1266a 100644
--- a/postfix/src/util/dict_nisplus.c
+++ b/postfix/src/util/dict_nisplus.c
@@ -257,8 +257,9 @@ DICT *dict_nisplus_open(const char *map, int open_flags, int dict_flags)
* Sanity check.
*/
if (open_flags != O_RDONLY)
- msg_fatal("%s:%s map requires O_RDONLY access mode",
- DICT_TYPE_NISPLUS, map);
+ return (dict_surrogate(DICT_TYPE_NISPLUS, map, open_flags, dict_flags,
+ "%s:%s map requires O_RDONLY access mode",
+ DICT_TYPE_NISPLUS, map));
/*
* Initialize. This is a read-only map with fixed strings, not with
diff --git a/postfix/src/util/dict_pcre.c b/postfix/src/util/dict_pcre.c
index 067416442..d53d4b6c8 100644
--- a/postfix/src/util/dict_pcre.c
+++ b/postfix/src/util/dict_pcre.c
@@ -794,7 +794,7 @@ static DICT_PCRE_RULE *dict_pcre_parse_rule(const char *mapname, int lineno,
/* dict_pcre_open - load and compile a file containing regular expressions */
-DICT *dict_pcre_open(const char *mapname, int unused_flags, int dict_flags)
+DICT *dict_pcre_open(const char *mapname, int open_flags, int dict_flags)
{
DICT_PCRE *dict_pcre;
VSTREAM *map_fp;
@@ -806,6 +806,23 @@ DICT *dict_pcre_open(const char *mapname, int unused_flags, int dict_flags)
int nesting = 0;
char *p;
+ /*
+ * Sanity checks.
+ */
+ if (open_flags != O_RDONLY)
+ return (dict_surrogate(DICT_TYPE_PCRE, mapname, open_flags, dict_flags,
+ "%s:%s map requires O_RDONLY access mode",
+ DICT_TYPE_PCRE, mapname));
+
+ /*
+ * Open the configuration file.
+ */
+ if ((map_fp = vstream_fopen(mapname, O_RDONLY, 0)) == 0)
+ return (dict_surrogate(DICT_TYPE_PCRE, mapname, open_flags, dict_flags,
+ "open %s: %m", mapname));
+ if (fstat(vstream_fileno(map_fp), &st) < 0)
+ msg_fatal("fstat %s: %m", mapname);
+
line_buffer = vstring_alloc(100);
dict_pcre = (DICT_PCRE *) dict_alloc(DICT_TYPE_PCRE, mapname,
@@ -823,17 +840,12 @@ DICT *dict_pcre_open(const char *mapname, int unused_flags, int dict_flags)
pcre_free = (void (*) (void *)) myfree;
dict_pcre_init = 1;
}
+ dict_pcre->dict.owner.uid = st.st_uid;
+ dict_pcre->dict.owner.status = (st.st_uid != 0);
/*
* Parse the pcre table.
*/
- if ((map_fp = vstream_fopen(mapname, O_RDONLY, 0)) == 0)
- msg_fatal("open %s: %m", mapname);
- if (fstat(vstream_fileno(map_fp), &st) < 0)
- msg_fatal("fstat %s: %m", mapname);
- dict_pcre->dict.owner.uid = st.st_uid;
- dict_pcre->dict.owner.status = (st.st_uid != 0);
-
while (readlline(line_buffer, map_fp, &lineno)) {
p = vstring_str(line_buffer);
trimblanks(p, 0)[0] = 0; /* Trim space at end */
diff --git a/postfix/src/util/dict_regexp.c b/postfix/src/util/dict_regexp.c
index b7b08c62f..2cab3b4e0 100644
--- a/postfix/src/util/dict_regexp.c
+++ b/postfix/src/util/dict_regexp.c
@@ -734,7 +734,7 @@ static DICT_REGEXP_RULE *dict_regexp_parseline(const char *mapname, int lineno,
/* dict_regexp_open - load and compile a file containing regular expressions */
-DICT *dict_regexp_open(const char *mapname, int unused_flags, int dict_flags)
+DICT *dict_regexp_open(const char *mapname, int open_flags, int dict_flags)
{
DICT_REGEXP *dict_regexp;
VSTREAM *map_fp;
@@ -747,6 +747,23 @@ DICT *dict_regexp_open(const char *mapname, int unused_flags, int dict_flags)
int nesting = 0;
char *p;
+ /*
+ * Sanity checks.
+ */
+ if (open_flags != O_RDONLY)
+ return (dict_surrogate(DICT_TYPE_REGEXP, mapname, open_flags, dict_flags,
+ "%s:%s map requires O_RDONLY access mode",
+ DICT_TYPE_REGEXP, mapname));
+
+ /*
+ * Open the configuration file.
+ */
+ if ((map_fp = vstream_fopen(mapname, O_RDONLY, 0)) == 0)
+ return (dict_surrogate(DICT_TYPE_REGEXP, mapname, open_flags, dict_flags,
+ "open %s: %m", mapname));
+ if (fstat(vstream_fileno(map_fp), &st) < 0)
+ msg_fatal("fstat %s: %m", mapname);
+
line_buffer = vstring_alloc(100);
dict_regexp = (DICT_REGEXP *) dict_alloc(DICT_TYPE_REGEXP, mapname,
@@ -759,17 +776,12 @@ DICT *dict_regexp_open(const char *mapname, int unused_flags, int dict_flags)
dict_regexp->head = 0;
dict_regexp->pmatch = 0;
dict_regexp->expansion_buf = 0;
+ dict_regexp->dict.owner.uid = st.st_uid;
+ dict_regexp->dict.owner.status = (st.st_uid != 0);
/*
* Parse the regexp table.
*/
- if ((map_fp = vstream_fopen(mapname, O_RDONLY, 0)) == 0)
- msg_fatal("open %s: %m", mapname);
- if (fstat(vstream_fileno(map_fp), &st) < 0)
- msg_fatal("fstat %s: %m", mapname);
- dict_regexp->dict.owner.uid = st.st_uid;
- dict_regexp->dict.owner.status = (st.st_uid != 0);
-
while (readlline(line_buffer, map_fp, &lineno)) {
p = vstring_str(line_buffer);
trimblanks(p, 0)[0] = 0;
diff --git a/postfix/src/util/dict_surrogate.c b/postfix/src/util/dict_surrogate.c
new file mode 100644
index 000000000..42dbe8fad
--- /dev/null
+++ b/postfix/src/util/dict_surrogate.c
@@ -0,0 +1,169 @@
+/*++
+/* NAME
+/* dict_surrogate 3
+/* SUMMARY
+/* surrogate table for graceful "open" failure
+/* SYNOPSIS
+/* #include
+/*
+/* DICT *dict_surrogate(dict_type, dict_name,
+/* open_flags, dict_flags,
+/* format, ...)
+/* const char *dict_type;
+/* const char *dict_name;
+/* int open_flags;
+/* int dict_flags;
+/* const char *format;
+/*
+/* int dict_allow_surrogate;
+/* DESCRIPTION
+/* dict_surrogate() either terminates the program with a fatal
+/* error, or provides a dummy dictionary that fails all
+/* operations with an error message, allowing the program to
+/* continue with reduced functionality.
+/*
+/* The global dict_allow_surrogate variable controls the choice
+/* between fatal error or reduced functionality. The default
+/* value is zero (fatal error).
+/*
+/* Arguments:
+/* .IP dict_type
+/* .IP dict_name
+/* .IP open_flags
+/* .IP dict_flags
+/* The parameters to the failed dictionary open() request.
+/* .IP format, ...
+/* The reason why the table could not be opened. This text is
+/* logged immediately, and upon every attempt to access the
+/* surrogate dictionary, before returning a "failed" completion
+/* status.
+/* SEE ALSO
+/* dict(3) generic dictionary manager
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* System library. */
+
+#include
+
+/* Utility library. */
+
+#include
+#include
+#include
+
+/* Application-specific. */
+
+typedef struct {
+ DICT dict; /* generic members */
+ char *reason; /* open failure reason */
+} DICT_SURROGATE;
+
+/* dict_surrogate_sequence - fail lookup */
+
+static int dict_surrogate_sequence(DICT *dict, int unused_func,
+ const char **key, const char **value)
+{
+ DICT_SURROGATE *dp = (DICT_SURROGATE *) dict;
+
+ msg_warn("%s:%s is unavailable. %s",
+ dict->type, dict->name, dp->reason);
+ DICT_ERR_VAL_RETURN(dict, DICT_ERR_RETRY, DICT_STAT_ERROR);
+}
+
+/* dict_surrogate_update - fail lookup */
+
+static int dict_surrogate_update(DICT *dict, const char *unused_name,
+ const char *unused_value)
+{
+ DICT_SURROGATE *dp = (DICT_SURROGATE *) dict;
+
+ msg_warn("%s:%s is unavailable. %s",
+ dict->type, dict->name, dp->reason);
+ DICT_ERR_VAL_RETURN(dict, DICT_ERR_RETRY, DICT_STAT_ERROR);
+}
+
+/* dict_surrogate_lookup - fail lookup */
+
+static const char *dict_surrogate_lookup(DICT *dict, const char *unused_name)
+{
+ DICT_SURROGATE *dp = (DICT_SURROGATE *) dict;
+
+ msg_warn("%s:%s is unavailable. %s",
+ dict->type, dict->name, dp->reason);
+ DICT_ERR_VAL_RETURN(dict, DICT_ERR_RETRY, (char *) 0);
+}
+
+/* dict_surrogate_delete - fail delete */
+
+static int dict_surrogate_delete(DICT *dict, const char *unused_name)
+{
+ DICT_SURROGATE *dp = (DICT_SURROGATE *) dict;
+
+ msg_warn("%s:%s is unavailable. %s",
+ dict->type, dict->name, dp->reason);
+ DICT_ERR_VAL_RETURN(dict, DICT_ERR_RETRY, DICT_STAT_ERROR);
+}
+
+/* dict_surrogate_close - close fail dictionary */
+
+static void dict_surrogate_close(DICT *dict)
+{
+ DICT_SURROGATE *dp = (DICT_SURROGATE *) dict;
+
+ myfree((char *) dp->reason);
+ dict_free(dict);
+}
+
+int dict_allow_surrogate = 0;
+
+/* dict_surrogate - terminate or provide surrogate dictionary */
+
+DICT *dict_surrogate(const char *dict_type, const char *dict_name,
+ int open_flags, int dict_flags,
+ const char *fmt,...)
+{
+ va_list ap;
+ DICT_SURROGATE *dp;
+ VSTRING *buf;
+ void (*log_fn) (const char *, va_list);
+
+ /*
+ * Log the problem immediately when it is detected. The table may not be
+ * accessed in every program execution (that is the whole point of
+ * continuing with reduced functionality) but we don't want the problem
+ * to remain unnoticed until long after a configuration mistake is made.
+ */
+ log_fn = dict_allow_surrogate ? vmsg_error : vmsg_fatal;
+ va_start(ap, fmt);
+ log_fn(fmt, ap);
+ va_end(ap);
+
+ /*
+ * Log the problem upon each access.
+ */
+ dp = (DICT_SURROGATE *) dict_alloc(dict_type, dict_name, sizeof(*dp));
+ dp->dict.lookup = dict_surrogate_lookup;
+ if (open_flags & O_RDWR) {
+ dp->dict.update = dict_surrogate_update;
+ dp->dict.delete = dict_surrogate_delete;
+ }
+ dp->dict.sequence = dict_surrogate_sequence;
+ dp->dict.close = dict_surrogate_close;
+ dp->dict.flags = dict_flags | DICT_FLAG_PATTERN;
+ dp->dict.owner.status = DICT_OWNER_TRUSTED;
+ buf = vstring_alloc(10);
+ va_start(ap, fmt);
+ vstring_vsprintf(buf, fmt, ap);
+ va_end(ap);
+ dp->reason = vstring_export(buf);
+ return (DICT_DEBUG (&dp->dict));
+}
diff --git a/postfix/src/util/dict_tcp.c b/postfix/src/util/dict_tcp.c
index 1f85b5f10..a20956538 100644
--- a/postfix/src/util/dict_tcp.c
+++ b/postfix/src/util/dict_tcp.c
@@ -290,11 +290,13 @@ DICT *dict_tcp_open(const char *map, int open_flags, int dict_flags)
* Sanity checks.
*/
if (dict_flags & DICT_FLAG_NO_UNAUTH)
- msg_fatal("%s:%s map is not allowed for security sensitive data",
- DICT_TYPE_TCP, map);
+ return (dict_surrogate(DICT_TYPE_TCP, map, open_flags, dict_flags,
+ "%s:%s map is not allowed for security sensitive data",
+ DICT_TYPE_TCP, map));
if (open_flags != O_RDONLY)
- msg_fatal("%s:%s map requires O_RDONLY access mode",
- DICT_TYPE_TCP, map);
+ return (dict_surrogate(DICT_TYPE_TCP, map, open_flags, dict_flags,
+ "%s:%s map requires O_RDONLY access mode",
+ DICT_TYPE_TCP, map));
/*
* Create the dictionary handle. Do not open the connection until the
diff --git a/postfix/src/util/dict_test.c b/postfix/src/util/dict_test.c
index 68165e2fb..3d60b7a92 100644
--- a/postfix/src/util/dict_test.c
+++ b/postfix/src/util/dict_test.c
@@ -77,6 +77,7 @@ void dict_test(int argc, char **argv)
usage(argv[0]);
}
dict_name = argv[optind];
+ dict_allow_surrogate = 1;
dict = dict_open(dict_name, open_flags, dict_flags);
dict_register(dict_name, dict);
while (vstring_fgets_nonl(inbuf, VSTREAM_IN)) {
diff --git a/postfix/src/util/dict_thash.c b/postfix/src/util/dict_thash.c
index a03dc3f4b..2c35db9a3 100644
--- a/postfix/src/util/dict_thash.c
+++ b/postfix/src/util/dict_thash.c
@@ -152,37 +152,28 @@ DICT *dict_thash_open(const char *path, int open_flags, int dict_flags)
int lineno;
char *key;
char *value;
+ HTABLE *table;
HTABLE_INFO *ht;
/*
* Sanity checks.
*/
if (open_flags != O_RDONLY)
- msg_fatal("%s:%s map requires O_RDONLY access mode",
- DICT_TYPE_THASH, path);
-
- /*
- * Create the in-memory table.
- */
- dict_thash = (DICT_THASH *)
- dict_alloc(DICT_TYPE_THASH, path, sizeof(*dict_thash));
- dict_thash->dict.lookup = dict_thash_lookup;
- dict_thash->dict.sequence = dict_thash_sequence;
- dict_thash->dict.close = dict_thash_close;
- dict_thash->dict.flags = dict_flags | DICT_FLAG_DUP_WARN | DICT_FLAG_FIXED;
- if (dict_flags & DICT_FLAG_FOLD_FIX)
- dict_thash->dict.fold_buf = vstring_alloc(10);
- dict_thash->info = 0;
+ return (dict_surrogate(DICT_TYPE_THASH, path, open_flags, dict_flags,
+ "%s:%s map requires O_RDONLY access mode",
+ DICT_TYPE_THASH, path));
/*
* Read the flat text file into in-memory hash. Read the file again if it
* may have changed while we were reading.
*/
for (before = time((time_t *) 0); /* see below */ ; before = after) {
- if ((fp = vstream_fopen(path, open_flags, 0644)) == 0)
- msg_fatal("open database %s: %m", path);
+ if ((fp = vstream_fopen(path, open_flags, 0644)) == 0) {
+ return (dict_surrogate(DICT_TYPE_THASH, path, open_flags, dict_flags,
+ "open database %s: %m", path));
+ }
lineno = 0;
- dict_thash->table = htable_create(13);
+ table = htable_create(13);
while (readlline(line_buffer, fp, &lineno)) {
/*
@@ -214,20 +205,20 @@ DICT *dict_thash_open(const char *path, int open_flags, int dict_flags)
/*
* Optionally fold the key.
*/
- if (dict_thash->dict.flags & DICT_FLAG_FOLD_FIX)
+ if (dict_flags & DICT_FLAG_FOLD_FIX)
lowercase(key);
/*
* Store the value under the key. Handle duplicates
* appropriately.
*/
- if ((ht = htable_locate(dict_thash->table, key)) != 0) {
- if (dict_thash->dict.flags & DICT_FLAG_DUP_IGNORE) {
+ if ((ht = htable_locate(table, key)) != 0) {
+ if (dict_flags & DICT_FLAG_DUP_IGNORE) {
/* void */ ;
- } else if (dict_thash->dict.flags & DICT_FLAG_DUP_REPLACE) {
+ } else if (dict_flags & DICT_FLAG_DUP_REPLACE) {
myfree(ht->value);
ht->value = mystrdup(value);
- } else if (dict_thash->dict.flags & DICT_FLAG_DUP_WARN) {
+ } else if (dict_flags & DICT_FLAG_DUP_WARN) {
msg_warn("%s, line %d: duplicate entry: \"%s\"",
path, lineno, key);
} else {
@@ -235,7 +226,7 @@ DICT *dict_thash_open(const char *path, int open_flags, int dict_flags)
path, lineno, key);
}
} else {
- htable_enter(dict_thash->table, key, mystrdup(value));
+ htable_enter(table, key, mystrdup(value));
}
}
@@ -253,12 +244,26 @@ DICT *dict_thash_open(const char *path, int open_flags, int dict_flags)
/*
* Yes, it is hot. Discard the result and read the file again.
*/
- htable_free(dict_thash->table, myfree);
+ htable_free(table, myfree);
if (msg_verbose > 1)
msg_info("pausing to let file %s cool down", path);
doze(300000);
}
vstring_free(line_buffer);
+
+ /*
+ * Create the in-memory table.
+ */
+ dict_thash = (DICT_THASH *)
+ dict_alloc(DICT_TYPE_THASH, path, sizeof(*dict_thash));
+ dict_thash->dict.lookup = dict_thash_lookup;
+ dict_thash->dict.sequence = dict_thash_sequence;
+ dict_thash->dict.close = dict_thash_close;
+ dict_thash->dict.flags = dict_flags | DICT_FLAG_DUP_WARN | DICT_FLAG_FIXED;
+ if (dict_flags & DICT_FLAG_FOLD_FIX)
+ dict_thash->dict.fold_buf = vstring_alloc(10);
+ dict_thash->info = 0;
+ dict_thash->table = table;
dict_thash->dict.owner.uid = st.st_uid;
dict_thash->dict.owner.status = (st.st_uid != 0);
diff --git a/postfix/src/util/dict_thash.map b/postfix/src/util/dict_thash.map
new file mode 100644
index 000000000..67e75784f
--- /dev/null
+++ b/postfix/src/util/dict_thash.map
@@ -0,0 +1,14 @@
+allascii.c 915
+alldig.c 928
+allprint.c 943
+allspace.c 931
+argv.c 5271
+argv_split.c 2780
+attr_clnt.c 5813
+attr_print0.c 7243
+attr_print64.c 8104
+attr_print_plain.c 7086
+attr_scan0.c 15454
+attr_scan64.c 17256
+attr_scan_plain.c 16924
+auto_clnt.c 9819
diff --git a/postfix/src/util/dict_unix.c b/postfix/src/util/dict_unix.c
index cd103cb40..463534430 100644
--- a/postfix/src/util/dict_unix.c
+++ b/postfix/src/util/dict_unix.c
@@ -159,7 +159,7 @@ static void dict_unix_close(DICT *dict)
/* dict_unix_open - open UNIX map */
-DICT *dict_unix_open(const char *map, int unused_flags, int dict_flags)
+DICT *dict_unix_open(const char *map, int open_flags, int dict_flags)
{
DICT_UNIX *dict_unix;
struct dict_unix_lookup {
@@ -173,14 +173,26 @@ DICT *dict_unix_open(const char *map, int unused_flags, int dict_flags)
};
struct dict_unix_lookup *lp;
- dict_unix = (DICT_UNIX *) dict_alloc(DICT_TYPE_UNIX, map,
- sizeof(*dict_unix));
+ /*
+ * Sanity checks.
+ */
+ if (open_flags != O_RDONLY)
+ return (dict_surrogate(DICT_TYPE_UNIX, map, open_flags, dict_flags,
+ "%s:%s map requires O_RDONLY access mode",
+ DICT_TYPE_UNIX, map));
+
+ /*
+ * "Open" the database.
+ */
for (lp = dict_unix_lookup; /* void */ ; lp++) {
if (lp->name == 0)
- msg_fatal("dict_unix_open: unknown map name: %s", map);
+ return (dict_surrogate(DICT_TYPE_UNIX, map, open_flags, dict_flags,
+ "unknown table: %s:%s", DICT_TYPE_UNIX, map));
if (strcmp(map, lp->name) == 0)
break;
}
+ dict_unix = (DICT_UNIX *) dict_alloc(DICT_TYPE_UNIX, map,
+ sizeof(*dict_unix));
dict_unix->dict.lookup = lp->lookup;
dict_unix->dict.close = dict_unix_close;
dict_unix->dict.flags = dict_flags | DICT_FLAG_FIXED;
diff --git a/postfix/src/util/match_list.c b/postfix/src/util/match_list.c
index f6f7fc34d..ca7091efb 100644
--- a/postfix/src/util/match_list.c
+++ b/postfix/src/util/match_list.c
@@ -106,6 +106,10 @@ static ARGV *match_list_parse(ARGV *list, char *string, int init_match)
char *map_type_name_flags;
int match;
+#define OPEN_FLAGS O_RDONLY
+#define DICT_FLAGS (DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX)
+#define STR(x) vstring_str(x)
+
/*
* /filename contents are expanded in-line. To support !/filename we
* prepend the negation operator to each item from the file.
@@ -121,17 +125,23 @@ static ARGV *match_list_parse(ARGV *list, char *string, int init_match)
if (*item == 0)
msg_fatal("%s: no pattern after '!'", myname);
if (*item == '/') { /* /file/name */
- if ((fp = vstream_fopen(item, O_RDONLY, 0)) == 0)
- msg_fatal("%s: open file %s: %m", myname, item);
- while (vstring_fgets(buf, fp))
- if (vstring_str(buf)[0] != '#')
- list = match_list_parse(list, vstring_str(buf), match);
- if (vstream_fclose(fp))
- msg_fatal("%s: read file %s: %m", myname, item);
+ if ((fp = vstream_fopen(item, O_RDONLY, 0)) == 0) {
+ vstring_sprintf(buf, "%s:%s", DICT_TYPE_NOFILE, item);
+ /* XXX Should increment existing map refcount. */
+ if (dict_handle(STR(buf)) == 0)
+ dict_register(STR(buf),
+ dict_surrogate(DICT_TYPE_NOFILE, item,
+ OPEN_FLAGS, DICT_FLAGS,
+ "open file %s: %m", item));
+ argv_add(list, STR(buf), (char *) 0);
+ } else {
+ while (vstring_fgets(buf, fp))
+ if (vstring_str(buf)[0] != '#')
+ list = match_list_parse(list, vstring_str(buf), match);
+ if (vstream_fclose(fp))
+ msg_fatal("%s: read file %s: %m", myname, item);
+ }
} else if (MATCH_DICTIONARY(item)) { /* type:table */
-#define OPEN_FLAGS O_RDONLY
-#define DICT_FLAGS (DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX)
-#define STR(x) vstring_str(x)
vstring_sprintf(buf, "%s%s(%o,%s)", match ? "" : "!",
item, OPEN_FLAGS, dict_flags_str(DICT_FLAGS));
map_type_name_flags = STR(buf) + (match == 0);
diff --git a/postfix/src/util/match_ops.c b/postfix/src/util/match_ops.c
index d138d0579..f978d2629 100644
--- a/postfix/src/util/match_ops.c
+++ b/postfix/src/util/match_ops.c
@@ -297,7 +297,7 @@ int match_hostaddr(MATCH_LIST *list, const char *addr, const char *pattern)
err = cidr_match_parse(&match_info, saved_patt, (VSTRING *) 0);
myfree(saved_patt);
if (err != 0) {
- list->error = DICT_ERR_CONFIG;
+ list->error = DICT_ERR_RETRY;
rc = match_error(list, "%s", vstring_str(err));
vstring_free(err);
return (rc);
diff --git a/postfix/src/util/msg.c b/postfix/src/util/msg.c
index ae7798017..70c6eab08 100644
--- a/postfix/src/util/msg.c
+++ b/postfix/src/util/msg.c
@@ -11,22 +11,47 @@
/* void msg_info(format, ...)
/* const char *format;
/*
+/* void vmsg_info(format, ap)
+/* const char *format;
+/* va_list ap;
+/*
/* void msg_warn(format, ...)
/* const char *format;
/*
+/* void vmsg_warn(format, ap)
+/* const char *format;
+/* va_list ap;
+/*
/* void msg_error(format, ...)
/* const char *format;
/*
+/* void vmsg_error(format, ap)
+/* const char *format;
+/* va_list ap;
+/*
/* NORETURN msg_fatal(format, ...)
/* const char *format;
/*
+/* NORETURN vmsg_fatal(format, ap)
+/* const char *format;
+/* va_list ap;
+/*
/* NORETURN msg_fatal_status(status, format, ...)
/* int status;
/* const char *format;
/*
+/* NORETURN vmsg_fatal_status(status, format, ap)
+/* int status;
+/* const char *format;
+/* va_list ap;
+/*
/* NORETURN msg_panic(format, ...)
/* const char *format;
/*
+/* NORETURN vmsg_panic(format, ap)
+/* const char *format;
+/* va_list ap;
+/*
/* MSG_CLEANUP_FN msg_cleanup(cleanup)
/* void (*cleanup)(void);
/* AUXILIARY FUNCTIONS
@@ -176,10 +201,15 @@ void msg_info(const char *fmt,...)
va_list ap;
va_start(ap, fmt);
- msg_vprintf(MSG_INFO, fmt, ap);
+ vmsg_info(fmt, ap);
va_end(ap);
}
+void vmsg_info(const char *fmt, va_list ap)
+{
+ msg_vprintf(MSG_INFO, fmt, ap);
+}
+
/* msg_warn - report warning message */
void msg_warn(const char *fmt,...)
@@ -187,10 +217,15 @@ void msg_warn(const char *fmt,...)
va_list ap;
va_start(ap, fmt);
- msg_vprintf(MSG_WARN, fmt, ap);
+ vmsg_warn(fmt, ap);
va_end(ap);
}
+void vmsg_warn(const char *fmt, va_list ap)
+{
+ msg_vprintf(MSG_WARN, fmt, ap);
+}
+
/* msg_error - report recoverable error */
void msg_error(const char *fmt,...)
@@ -198,8 +233,13 @@ void msg_error(const char *fmt,...)
va_list ap;
va_start(ap, fmt);
- msg_vprintf(MSG_ERROR, fmt, ap);
+ vmsg_error(fmt, ap);
va_end(ap);
+}
+
+void vmsg_error(const char *fmt, va_list ap)
+{
+ msg_vprintf(MSG_ERROR, fmt, ap);
if (++msg_error_count >= msg_error_bound)
msg_fatal("too many errors - program terminated");
}
@@ -210,10 +250,15 @@ NORETURN msg_fatal(const char *fmt,...)
{
va_list ap;
+ va_start(ap, fmt);
+ vmsg_fatal(fmt, ap);
+ /* NOTREACHED */
+}
+
+NORETURN vmsg_fatal(const char *fmt, va_list ap)
+{
if (msg_exiting++ == 0) {
- va_start(ap, fmt);
msg_vprintf(MSG_FATAL, fmt, ap);
- va_end(ap);
if (msg_cleanup_fn)
msg_cleanup_fn();
}
@@ -228,10 +273,15 @@ NORETURN msg_fatal_status(int status, const char *fmt,...)
{
va_list ap;
+ va_start(ap, fmt);
+ vmsg_fatal_status(status, fmt, ap);
+ /* NOTREACHED */
+}
+
+NORETURN vmsg_fatal_status(int status, const char *fmt, va_list ap)
+{
if (msg_exiting++ == 0) {
- va_start(ap, fmt);
msg_vprintf(MSG_FATAL, fmt, ap);
- va_end(ap);
if (msg_cleanup_fn)
msg_cleanup_fn();
}
@@ -246,10 +296,15 @@ NORETURN msg_panic(const char *fmt,...)
{
va_list ap;
+ va_start(ap, fmt);
+ vmsg_panic(fmt, ap);
+ /* NOTREACHED */
+}
+
+NORETURN vmsg_panic(const char *fmt, va_list ap)
+{
if (msg_exiting++ == 0) {
- va_start(ap, fmt);
msg_vprintf(MSG_PANIC, fmt, ap);
- va_end(ap);
}
sleep(1);
abort(); /* Die! */
diff --git a/postfix/src/util/msg.h b/postfix/src/util/msg.h
index 4b1c6d0bf..599c69a57 100644
--- a/postfix/src/util/msg.h
+++ b/postfix/src/util/msg.h
@@ -14,6 +14,7 @@
/*
* System library.
*/
+#include
#include
/*
@@ -30,6 +31,13 @@ extern NORETURN PRINTFLIKE(1, 2) msg_fatal(const char *,...);
extern NORETURN PRINTFLIKE(2, 3) msg_fatal_status(int, const char *,...);
extern NORETURN PRINTFLIKE(1, 2) msg_panic(const char *,...);
+extern void vmsg_info(const char *, va_list);
+extern void vmsg_warn(const char *, va_list);
+extern void vmsg_error(const char *, va_list);
+extern NORETURN vmsg_fatal(const char *, va_list);
+extern NORETURN vmsg_fatal_status(int, const char *, va_list);
+extern NORETURN vmsg_panic(const char *, va_list);
+
extern int msg_error_limit(int);
extern void msg_error_clear(void);
extern MSG_CLEANUP_FN msg_cleanup(MSG_CLEANUP_FN);