-TDICT_STACK
-TDICT_TCP
-TDICT_TEXT
+-TDICT_THASH
-TDICT_UNIX
-TDNS_FIXED
-TDNS_REPLY
-Tsfsistat
-Tsize_t
-Tssize_t
+-Ttime_t
namadr_list.c, trivial-rewrite/resolve.c, smtpd/smtpd.c,
smtpd/smtpd_check.c, global/flush_clnt.c, flush/flush.c.
-20111226
+20111224
+
+ Cleanup: eliminated the global dict_errno variable that
+ made error reporting convenient but not necessarily precise.
+ This was a straightforward change except in the few modules
+ that propagate errors from one dictionary API to another:
+ dict_cache.c, dict_debug.c, maps.c, dict_memcache.c. Files:
+ src/cleanup/cleanup_map11.c, src/cleanup/cleanup_map1n.c,
+ src/global/addr_match_list.c, src/global/dict_ldap.c,
+ src/global/dict_memcache.c, src/global/dict_mysql.c,
+ src/global/dict_pgsql.c, src/global/dict_proxy.c,
+ src/global/dict_sqlite.c, src/global/domain_list.c,
+ src/global/flush_clnt.c, src/global/mail_addr_find.c,
+ src/global/mail_addr_map.c, src/global/maps.c, src/global/maps.h,
+ src/global/match_parent_style.h, src/global/namadr_list.c,
+ src/global/resolve_local.c, src/global/resolve_local.h,
+ src/global/server_acl.c, src/global/string_list.c,
+ src/local/alias.c, src/local/bounce_workaround.c,
+ src/local/mailbox.c, src/local/unknown.c, src/proxymap/proxymap.c,
+ src/qmqpd/qmqpd.c, src/smtp/smtp_map11.c, src/smtpd/smtpd_check.c,
+ src/trivial-rewrite/resolve.c, src/trivial-rewrite/transport.c,
+ src/util/dict.h, src/util/dict_alloc.c, src/util/dict_cache.c,
+ src/util/dict_cidr.c, src/util/dict_db.c, src/util/dict_debug.c,
+ src/util/dict_env.c, src/util/dict_fail.c, src/util/dict_ht.c,
+ src/util/dict_pcre.c, src/util/dict_regexp.c,
+ src/util/dict_static.c, src/util/dict_tcp.c, src/util/dict_test.c,
+ src/util/dict_thash.c, src/util/dict_unix.c, src/util/match_list.c,
+ src/util/match_list.h, src/util/match_ops.c, src/virtual/mailbox.c.
- Cleanup: reset the global dict_errno flag before table
- lookup, to avoid false alarms about table lookup errors.
- Files: util/dict_thash.c, global/dict_sqlite.c, global.maps.c.
+20111226
Bugfix (introduced 20110426): after lookup error with
mailbox_transport_maps, mailbox_command_maps or
master/master_ent.c, master/master_vars.c, postscreen/postscreen.c,
qmqpd/qmqpd.c, smtp/smtp_connect.c, smtpd/smtpd.c,
util/inet_proto.c.
+
+20120107
+
+ Workaround: degrade gracefully when the "domain" feature
+ of LDAP, *SQL and memcache databases has a table lookup
+ problem. Files: global/db_common.c, global/dict_ldap.c,
+ global/dict*sql*.c, global/dict_memcache.c.
+
+ Cleanup: fixed memcache client error handling for things
+ that never happen. global/dict_memcache.c.
+
+ Future proofing: prepare postmap/postalias error logging
+ for future changes to database code. Files: postalias/postalias.c,
+ postmap/postmap.c.
+
+20120108
+
+ Cleanup: the postscreen(8) and verify(8) cache managers log
+ warnings at a reduced rate of one per second per cache
+ operation, to avoid logging large numbers of warnings about
+ a problem with low-value information. File: util/msg_rate_delay.c,
+ util/dict_cache.c.
The UNIX process environment array. The lookup key is the variable
name. The lookup table name in "environ:table" is ignored.
f\bfa\bai\bil\bl (read-write)
- A table that reliably fails all requests. The lookup table name
- provides the internal error result code. This table exists to simplify
- Postfix error tests.
+ A table that reliably fails all requests. The lookup table name is used
+ for logging only. This table exists to simplify Postfix error tests.
h\bha\bas\bsh\bh
An indexed file type based on hashing. This is available only on
systems with support for Berkeley DB databases. Database files are
Remove this file from the stable release.
- limits on attribute string length in IPC protocols. 10-20KB
- seems OK. We could start with limits enabled only in proxymap.
+ Things to do after the stable release:
+
+ Before proxymap can be exposed to the network to share,
+ e.g., postscreen or verify caches, need to enforce limits
+ on attribute string length in IPC protocols. 10-20KB seems
+ OK. The VSTREAM library already supports read/write deadlines.
move flush_init() etc. from defer service clients to the
bounce daemon?
- Is it practical to move errors from global dict_errno
- to lookuphandle->dict_errno?
-
- Either make all void dict_* operations return an error code,
- or require that they reset dict_errno on entry, either exit
- with a fatal error or set dict_errno on error.
-
multi_connect() function that takes a list of inet:host:port
and/or unix:pathname specs, with an explicit "inet" prefix
argument to handle applications that use host:port only.
client, dovecot client, and other.
dict_memcache: treat "bad" key as cache miss, i.e. read/write
- the database as if the cache did not exist. This does not
- work because most Postfix maps (virtual, canonical, access,
- transport, ...) also don't support spaces in keys.
+ the backup database as if the cache did not exist. This
+ does not help because most Postfix maps (virtual, canonical,
+ access, transport, ...) also don't support spaces in keys.
postscreen: keep the cache open after "postfix reload" when
it is remote (type memcache: or proxy:). This does not work
because memcache can use a non-proxied file as backup).
- Things to do after the stable release:
-
What is the feasibility of adding an mta_name (personality)
attribute that is propagated via queue files and delivery
agent requests? It would default to myhostname.
into doubles (converting only some leads to a documentation
nightmare).
- postscreen: wait for DNS completion after early HANGUP and
- log DNSBL results. If the client was still waiting for the
- PREGREET timer, just flag the PREGREET test as (done, not
- passed). If the client was not waiting for the PREGREET
- timer, just wait until DNSBL lookup (if any) completes.
-
Address verify cache: allow a negative cache "refresh"
result to purge a "positive" cache entry in some safe manner.
Currently, the negative cache "refresh" result is discarded,
<dt> <b>fail</b> (read-write) </dt>
<dd> A table that reliably fails all requests. The lookup table
-name provides the internal error result code. This table exists to
-simplify Postfix error tests. </dd>
+name is used for logging only. This table exists to simplify Postfix
+error tests. </dd>
<dt> <b>hash</b> </dt>
<i>file</i><b>_</b><i>name</i><b>.db</b>. This is available on systems
with support for <b>db</b> databases.
- <b>sdbm</b> The output consists of two files, named
- <i>file</i><b>_</b><i>name</i><b>.pag</b> and <i>file</i><b>_</b><i>name</i><b>.dir</b>. This is
- available on systems with support for <b>sdbm</b>
+ <b>fail</b> A table that reliably fails all requests.
+ The lookup table name is used for logging
+ only. This table exists to simplify Postfix
+ error tests.
+
+ <b>sdbm</b> The output consists of two files, named
+ <i>file</i><b>_</b><i>name</i><b>.pag</b> and <i>file</i><b>_</b><i>name</i><b>.dir</b>. This is
+ available on systems with support for <b>sdbm</b>
databases.
- When no <i>file</i><b>_</b><i>type</i> is specified, the software uses
- the database type specified via the <b><a href="postconf.5.html#default_database_type">default_data</a>-</b>
+ When no <i>file</i><b>_</b><i>type</i> is specified, the software uses
+ the database type specified via the <b><a href="postconf.5.html#default_database_type">default_data</a>-</b>
<b><a href="postconf.5.html#default_database_type">base_type</a></b> configuration parameter. The default
- value for this parameter depends on the host envi-
+ value for this parameter depends on the host envi-
ronment.
<i>file</i><b>_</b><i>name</i>
- The name of the alias database source file when
+ The name of the alias database source file when
creating a database.
<b>DIAGNOSTICS</b>
- Problems are logged to the standard error stream and to
- <b>syslogd</b>(8). No output means that no problems were
- detected. Duplicate entries are skipped and are flagged
+ Problems are logged to the standard error stream and to
+ <b>syslogd</b>(8). No output means that no problems were
+ detected. Duplicate entries are skipped and are flagged
with a warning.
- <a href="postalias.1.html"><b>postalias</b>(1)</a> terminates with zero exit status in case of
- success (including successful "<b>postalias -q</b>" lookup) and
+ <a href="postalias.1.html"><b>postalias</b>(1)</a> terminates with zero exit status in case of
+ success (including successful "<b>postalias -q</b>" lookup) and
terminates with non-zero exit status in case of failure.
<b>ENVIRONMENT</b>
Enable verbose logging for debugging purposes.
<b>CONFIGURATION PARAMETERS</b>
- The following <a href="postconf.5.html"><b>main.cf</b></a> parameters are especially relevant
+ The following <a href="postconf.5.html"><b>main.cf</b></a> parameters are especially relevant
to this program.
- The text below provides only a parameter summary. See
+ The text below provides only a parameter summary. See
<a href="postconf.5.html"><b>postconf</b>(5)</a> for more details including examples.
<b><a href="postconf.5.html#alias_database">alias_database</a> (see 'postconf -d' output)</b>
- The alias databases for <a href="local.8.html"><b>local</b>(8)</a> delivery that are
+ The alias databases for <a href="local.8.html"><b>local</b>(8)</a> delivery that are
updated with "<b>newaliases</b>" or with "<b>sendmail -bi</b>".
<b><a href="postconf.5.html#config_directory">config_directory</a> (see 'postconf -d' output)</b>
- The default location of the Postfix <a href="postconf.5.html">main.cf</a> and
+ The default location of the Postfix <a href="postconf.5.html">main.cf</a> and
<a href="master.5.html">master.cf</a> configuration files.
<b><a href="postconf.5.html#berkeley_db_create_buffer_size">berkeley_db_create_buffer_size</a> (16777216)</b>
- The per-table I/O buffer size for programs that
+ The per-table I/O buffer size for programs that
create Berkeley DB hash or btree tables.
<b><a href="postconf.5.html#berkeley_db_read_buffer_size">berkeley_db_read_buffer_size</a> (131072)</b>
- The per-table I/O buffer size for programs that
+ The per-table I/O buffer size for programs that
read Berkeley DB hash or btree tables.
<b><a href="postconf.5.html#default_database_type">default_database_type</a> (see 'postconf -d' output)</b>
The syslog facility of Postfix logging.
<b><a href="postconf.5.html#syslog_name">syslog_name</a> (see 'postconf -d' output)</b>
- The mail system name that is prepended to the
- process name in syslog records, so that "smtpd"
+ The mail system name that is prepended to the
+ process name in syslog records, so that "smtpd"
becomes, for example, "postfix/smtpd".
<b>STANDARDS</b>
<a href="DATABASE_README.html">DATABASE_README</a>, Postfix lookup table overview
<b>LICENSE</b>
- The Secure Mailer license must be distributed with this
+ The Secure Mailer license must be distributed with this
software.
<b>AUTHOR(S)</b>
this useful someday.
<b>fail</b> A table that reliably fails all requests.
- The lookup table name provides the internal
- error result code. This table exists to sim-
- plify Postfix error tests.
+ The lookup table name is used for logging.
+ This table exists to simplify Postfix error
+ tests.
<b>hash</b> An indexed file type based on hashing. This
is available on systems with support for
<i>file</i><b>_</b><i>name</i><b>.db</b>. This is available on systems
with support for <b>db</b> databases.
- <b>sdbm</b> The output consists of two files, named
- <i>file</i><b>_</b><i>name</i><b>.pag</b> and <i>file</i><b>_</b><i>name</i><b>.dir</b>. This is
- available on systems with support for <b>sdbm</b>
+ <b>fail</b> A table that reliably fails all requests.
+ The lookup table name is used for logging
+ only. This table exists to simplify Postfix
+ error tests.
+
+ <b>sdbm</b> The output consists of two files, named
+ <i>file</i><b>_</b><i>name</i><b>.pag</b> and <i>file</i><b>_</b><i>name</i><b>.dir</b>. This is
+ available on systems with support for <b>sdbm</b>
databases.
- When no <i>file</i><b>_</b><i>type</i> is specified, the software uses
- the database type specified via the <b><a href="postconf.5.html#default_database_type">default_data</a>-</b>
+ When no <i>file</i><b>_</b><i>type</i> is specified, the software uses
+ the database type specified via the <b><a href="postconf.5.html#default_database_type">default_data</a>-</b>
<b><a href="postconf.5.html#default_database_type">base_type</a></b> configuration parameter.
<i>file</i><b>_</b><i>name</i>
- The name of the lookup table source file when
+ The name of the lookup table source file when
rebuilding a database.
<b>DIAGNOSTICS</b>
- Problems are logged to the standard error stream and to
- <b>syslogd</b>(8). No output means that no problems were
- detected. Duplicate entries are skipped and are flagged
+ Problems are logged to the standard error stream and to
+ <b>syslogd</b>(8). No output means that no problems were
+ detected. Duplicate entries are skipped and are flagged
with a warning.
- <a href="postmap.1.html"><b>postmap</b>(1)</a> terminates with zero exit status in case of
- success (including successful "<b>postmap -q</b>" lookup) and
+ <a href="postmap.1.html"><b>postmap</b>(1)</a> terminates with zero exit status in case of
+ success (including successful "<b>postmap -q</b>" lookup) and
terminates with non-zero exit status in case of failure.
<b>ENVIRONMENT</b>
Enable verbose logging for debugging purposes.
<b>CONFIGURATION PARAMETERS</b>
- The following <a href="postconf.5.html"><b>main.cf</b></a> parameters are especially relevant
+ The following <a href="postconf.5.html"><b>main.cf</b></a> parameters are especially relevant
to this program. The text below provides only a parameter
- summary. See <a href="postconf.5.html"><b>postconf</b>(5)</a> for more details including exam-
+ summary. See <a href="postconf.5.html"><b>postconf</b>(5)</a> for more details including exam-
ples.
<b><a href="postconf.5.html#berkeley_db_create_buffer_size">berkeley_db_create_buffer_size</a> (16777216)</b>
- The per-table I/O buffer size for programs that
+ The per-table I/O buffer size for programs that
create Berkeley DB hash or btree tables.
<b><a href="postconf.5.html#berkeley_db_read_buffer_size">berkeley_db_read_buffer_size</a> (131072)</b>
- The per-table I/O buffer size for programs that
+ The per-table I/O buffer size for programs that
read Berkeley DB hash or btree tables.
<b><a href="postconf.5.html#config_directory">config_directory</a> (see 'postconf -d' output)</b>
- The default location of the Postfix <a href="postconf.5.html">main.cf</a> and
+ The default location of the Postfix <a href="postconf.5.html">main.cf</a> and
<a href="master.5.html">master.cf</a> configuration files.
<b><a href="postconf.5.html#default_database_type">default_database_type</a> (see 'postconf -d' output)</b>
The syslog facility of Postfix logging.
<b><a href="postconf.5.html#syslog_name">syslog_name</a> (see 'postconf -d' output)</b>
- The mail system name that is prepended to the
- process name in syslog records, so that "smtpd"
+ The mail system name that is prepended to the
+ process name in syslog records, so that "smtpd"
becomes, for example, "postfix/smtpd".
<b>SEE ALSO</b>
<a href="DATABASE_README.html">DATABASE_README</a>, Postfix lookup table overview
<b>LICENSE</b>
- The Secure Mailer license must be distributed with this
+ The Secure Mailer license must be distributed with this
software.
<b>AUTHOR(S)</b>
.IP \fBhash\fR
The output is a hashed file, named \fIfile_name\fB.db\fR.
This is available on systems with support for \fBdb\fR databases.
+.IP \fBfail\fR
+A table that reliably fails all requests. The lookup table
+name is used for logging only. This table exists to simplify
+Postfix error tests.
.IP \fBsdbm\fR
The output consists of two files, named \fIfile_name\fB.pag\fR and
\fIfile_name\fB.dir\fR.
useful someday.
.IP \fBfail\fR
A table that reliably fails all requests. The lookup table
-name provides the internal error result code. This table
-exists to simplify Postfix error tests.
+name is used for logging. This table exists to simplify
+Postfix error tests.
.IP \fBhash\fR
An indexed file type based on hashing.
This is available on systems with support for Berkeley DB
.IP \fBhash\fR
The output file is a hashed file, named \fIfile_name\fB.db\fR.
This is available on systems with support for \fBdb\fR databases.
+.IP \fBfail\fR
+A table that reliably fails all requests. The lookup table
+name is used for logging only. This table exists to simplify
+Postfix error tests.
.IP \fBsdbm\fR
The output consists of two files, named \fIfile_name\fB.pag\fR and
\fIfile_name\fB.dir\fR.
<dt> <b>fail</b> (read-write) </dt>
<dd> A table that reliably fails all requests. The lookup table
-name provides the internal error result code. This table exists to
-simplify Postfix error tests. </dd>
+name is used for logging only. This table exists to simplify Postfix
+error tests. </dd>
<dt> <b>hash</b> </dt>
cleanup.o: ../../include/mail_version.h
cleanup.o: ../../include/maps.h
cleanup.o: ../../include/match_list.h
-cleanup.o: ../../include/match_ops.h
cleanup.o: ../../include/milter.h
cleanup.o: ../../include/mime_state.h
cleanup.o: ../../include/msg.h
cleanup_addr.o: ../../include/mail_stream.h
cleanup_addr.o: ../../include/maps.h
cleanup_addr.o: ../../include/match_list.h
-cleanup_addr.o: ../../include/match_ops.h
cleanup_addr.o: ../../include/milter.h
cleanup_addr.o: ../../include/mime_state.h
cleanup_addr.o: ../../include/msg.h
cleanup_api.o: ../../include/mail_stream.h
cleanup_api.o: ../../include/maps.h
cleanup_api.o: ../../include/match_list.h
-cleanup_api.o: ../../include/match_ops.h
cleanup_api.o: ../../include/milter.h
cleanup_api.o: ../../include/mime_state.h
cleanup_api.o: ../../include/msg.h
cleanup_body_edit.o: ../../include/mail_stream.h
cleanup_body_edit.o: ../../include/maps.h
cleanup_body_edit.o: ../../include/match_list.h
-cleanup_body_edit.o: ../../include/match_ops.h
cleanup_body_edit.o: ../../include/milter.h
cleanup_body_edit.o: ../../include/mime_state.h
cleanup_body_edit.o: ../../include/msg.h
cleanup_bounce.o: ../../include/mail_stream.h
cleanup_bounce.o: ../../include/maps.h
cleanup_bounce.o: ../../include/match_list.h
-cleanup_bounce.o: ../../include/match_ops.h
cleanup_bounce.o: ../../include/milter.h
cleanup_bounce.o: ../../include/mime_state.h
cleanup_bounce.o: ../../include/msg.h
cleanup_envelope.o: ../../include/mail_stream.h
cleanup_envelope.o: ../../include/maps.h
cleanup_envelope.o: ../../include/match_list.h
-cleanup_envelope.o: ../../include/match_ops.h
cleanup_envelope.o: ../../include/milter.h
cleanup_envelope.o: ../../include/mime_state.h
cleanup_envelope.o: ../../include/msg.h
cleanup_extracted.o: ../../include/mail_stream.h
cleanup_extracted.o: ../../include/maps.h
cleanup_extracted.o: ../../include/match_list.h
-cleanup_extracted.o: ../../include/match_ops.h
cleanup_extracted.o: ../../include/milter.h
cleanup_extracted.o: ../../include/mime_state.h
cleanup_extracted.o: ../../include/msg.h
cleanup_final.o: ../../include/mail_stream.h
cleanup_final.o: ../../include/maps.h
cleanup_final.o: ../../include/match_list.h
-cleanup_final.o: ../../include/match_ops.h
cleanup_final.o: ../../include/milter.h
cleanup_final.o: ../../include/mime_state.h
cleanup_final.o: ../../include/msg.h
cleanup_init.o: ../../include/mail_version.h
cleanup_init.o: ../../include/maps.h
cleanup_init.o: ../../include/match_list.h
-cleanup_init.o: ../../include/match_ops.h
cleanup_init.o: ../../include/milter.h
cleanup_init.o: ../../include/mime_state.h
cleanup_init.o: ../../include/msg.h
cleanup_map11.o: ../../include/mail_stream.h
cleanup_map11.o: ../../include/maps.h
cleanup_map11.o: ../../include/match_list.h
-cleanup_map11.o: ../../include/match_ops.h
cleanup_map11.o: ../../include/milter.h
cleanup_map11.o: ../../include/mime_state.h
cleanup_map11.o: ../../include/msg.h
cleanup_map1n.o: ../../include/mail_stream.h
cleanup_map1n.o: ../../include/maps.h
cleanup_map1n.o: ../../include/match_list.h
-cleanup_map1n.o: ../../include/match_ops.h
cleanup_map1n.o: ../../include/milter.h
cleanup_map1n.o: ../../include/mime_state.h
cleanup_map1n.o: ../../include/msg.h
cleanup_masquerade.o: ../../include/mail_stream.h
cleanup_masquerade.o: ../../include/maps.h
cleanup_masquerade.o: ../../include/match_list.h
-cleanup_masquerade.o: ../../include/match_ops.h
cleanup_masquerade.o: ../../include/milter.h
cleanup_masquerade.o: ../../include/mime_state.h
cleanup_masquerade.o: ../../include/msg.h
cleanup_message.o: ../../include/mail_stream.h
cleanup_message.o: ../../include/maps.h
cleanup_message.o: ../../include/match_list.h
-cleanup_message.o: ../../include/match_ops.h
cleanup_message.o: ../../include/milter.h
cleanup_message.o: ../../include/mime_state.h
cleanup_message.o: ../../include/msg.h
cleanup_milter.o: ../../include/mail_stream.h
cleanup_milter.o: ../../include/maps.h
cleanup_milter.o: ../../include/match_list.h
-cleanup_milter.o: ../../include/match_ops.h
cleanup_milter.o: ../../include/milter.h
cleanup_milter.o: ../../include/mime_state.h
cleanup_milter.o: ../../include/msg.h
cleanup_out.o: ../../include/mail_stream.h
cleanup_out.o: ../../include/maps.h
cleanup_out.o: ../../include/match_list.h
-cleanup_out.o: ../../include/match_ops.h
cleanup_out.o: ../../include/milter.h
cleanup_out.o: ../../include/mime_state.h
cleanup_out.o: ../../include/msg.h
cleanup_out_recipient.o: ../../include/mail_stream.h
cleanup_out_recipient.o: ../../include/maps.h
cleanup_out_recipient.o: ../../include/match_list.h
-cleanup_out_recipient.o: ../../include/match_ops.h
cleanup_out_recipient.o: ../../include/milter.h
cleanup_out_recipient.o: ../../include/mime_state.h
cleanup_out_recipient.o: ../../include/msg.h
cleanup_region.o: ../../include/mail_stream.h
cleanup_region.o: ../../include/maps.h
cleanup_region.o: ../../include/match_list.h
-cleanup_region.o: ../../include/match_ops.h
cleanup_region.o: ../../include/milter.h
cleanup_region.o: ../../include/mime_state.h
cleanup_region.o: ../../include/msg.h
cleanup_rewrite.o: ../../include/mail_stream.h
cleanup_rewrite.o: ../../include/maps.h
cleanup_rewrite.o: ../../include/match_list.h
-cleanup_rewrite.o: ../../include/match_ops.h
cleanup_rewrite.o: ../../include/milter.h
cleanup_rewrite.o: ../../include/mime_state.h
cleanup_rewrite.o: ../../include/msg.h
cleanup_state.o: ../../include/mail_stream.h
cleanup_state.o: ../../include/maps.h
cleanup_state.o: ../../include/match_list.h
-cleanup_state.o: ../../include/match_ops.h
cleanup_state.o: ../../include/milter.h
cleanup_state.o: ../../include/mime_state.h
cleanup_state.o: ../../include/mymalloc.h
argv_free(new_addr);
if (expand_to_self)
return (did_rewrite);
- } else if (dict_errno != 0) {
+ } else if (maps->error != 0) {
msg_warn("%s: %s map lookup problem for %s",
state->queue_id, maps->title, STR(addr));
state->errs |= CLEANUP_STAT_WRITE;
}
myfree(saved_lhs);
argv_free(lookup);
- } else if (dict_errno != 0) {
+ } else if (maps->error != 0) {
msg_warn("%s: %s map lookup problem for %s -- "
"deferring delivery",
state->queue_id, maps->title, addr);
747 original_recipient: alias@hades.porcupine.org
774 canceled_recipient: root@porcupine.org
794 pointer_record: 1258
- 1258 named_attribute: notify_flags=1
- 1274 original_recipient: xxxx
- 1280 recipient: xxxx
- 1286 pointer_record: 1303
- 1303 named_attribute: notify_flags=1
- 1319 original_recipient: yyyy
- 1325 canceled_recipient: yyyy
- 1331 pointer_record: 811
+ 1258 original_recipient: xxxx
+ 1264 recipient: xxxx
+ 1270 pointer_record: 1287
+ 1287 original_recipient: yyyy
+ 1293 canceled_recipient: yyyy
+ 1299 pointer_record: 811
811 *** MESSAGE CONTENTS test-queue-file.tmp ***
813 regular_text: Received: from hades.porcupine.org (hades.porcupine.org [168.100.189.10])
888 regular_text: by hades.porcupine.org (Postfix) with SMTP id 38132290405;
949 regular_text: Sat, 20 Jan 2007 19:52:41 -0500 (EST)
- 989 pointer_record: 1348
- 1348 pointer_record: 1399
- 1399 pointer_record: 1365
- 1365 pointer_record: 1492
- 1492 regular_text: X: X-replaced-header replacement header text
- 1538 pointer_record: 1433
- 1433 regular_text: X2: test header value 3
- 1458 regular_text: Y: 1234567
- 1470 padding: 0
- 1475 pointer_record: 1047
+ 989 pointer_record: 1316
+ 1316 pointer_record: 1367
+ 1367 pointer_record: 1333
+ 1333 pointer_record: 1460
+ 1460 regular_text: X: X-replaced-header replacement header text
+ 1506 pointer_record: 1401
+ 1401 regular_text: X2: test header value 3
+ 1426 regular_text: Y: 1234567
+ 1438 padding: 0
+ 1443 pointer_record: 1047
1047 regular_text: Message-Id: <20070121005247.38132290405@hades.porcupine.org>
1109 regular_text: Date: Sat, 20 Jan 2007 19:52:41 -0500 (EST)
1154 regular_text: From: wietse@porcupine.org
516 original_recipient: you@porcupine.org
535 recipient: you@porcupine.org
554 pointer_record: 573
- 573 named_attribute: notify_flags=1
- 589 original_recipient: me@porcupine.org
- 607 canceled_recipient: me@porcupine.org
- 625 pointer_record: 642
- 642 named_attribute: notify_flags=1
- 658 original_recipient: em@porcupine.org
- 676 canceled_recipient: em@porcupine.org
- 694 pointer_record: 571
+ 573 original_recipient: me@porcupine.org
+ 591 canceled_recipient: me@porcupine.org
+ 609 pointer_record: 626
+ 626 original_recipient: em@porcupine.org
+ 644 canceled_recipient: em@porcupine.org
+ 662 pointer_record: 571
571 *** MESSAGE FILE END test-queue-file12.tmp ***
516 original_recipient: you@porcupine.org
535 recipient: you@porcupine.org
554 pointer_record: 573
- 573 named_attribute: notify_flags=1
- 589 original_recipient: me@porcupine.org
- 607 canceled_recipient: me@porcupine.org
- 625 pointer_record: 642
- 642 named_attribute: notify_flags=1
- 658 original_recipient: em@porcupine.org
- 676 canceled_recipient: em@porcupine.org
- 694 pointer_record: 571
+ 573 original_recipient: me@porcupine.org
+ 591 canceled_recipient: me@porcupine.org
+ 609 pointer_record: 626
+ 626 original_recipient: em@porcupine.org
+ 644 canceled_recipient: em@porcupine.org
+ 662 pointer_record: 571
571 *** MESSAGE FILE END test-queue-file13a.tmp ***
147 sender_fullname: Wietse Venema
162 sender: me@porcupine.org
180 pointer_record: 573
- 573 named_attribute: notify_flags=1
- 589 original_recipient: me@porcupine.org
- 607 canceled_recipient: me@porcupine.org
- 625 pointer_record: 1413
- 1413 named_attribute: notify_flags=1
- 1429 original_recipient: em@porcupine.org
- 1447 canceled_recipient: em@porcupine.org
- 1465 pointer_record: 197
+ 573 original_recipient: me@porcupine.org
+ 591 canceled_recipient: me@porcupine.org
+ 609 pointer_record: 1397
+ 1397 original_recipient: em@porcupine.org
+ 1415 canceled_recipient: em@porcupine.org
+ 1433 pointer_record: 197
197 *** MESSAGE CONTENTS test-queue-file3.tmp ***
199 regular_text: Received: by hades.porcupine.org (Postfix, from userid 1001)
261 regular_text: id B85F1290407; Sat, 20 Jan 2007 20:53:59 -0500 (EST)
- 317 pointer_record: 858
- 858 pointer_record: 1331
- 1331 pointer_record: 1372
- 1372 pointer_record: 1482
- 1482 pointer_record: 1739
- 1739 pointer_record: 1780
- 1780 regular_text: From: me@porcupine.org
- 1804 pointer_record: 1523
- 1523 pointer_record: 1821
- 1821 pointer_record: 1861
- 1861 regular_text: To: you@porcupine.org
- 1884 pointer_record: 1563
- 1563 pointer_record: 1901
- 1901 pointer_record: 1980
- 1980 regular_text: Message-Id: <20060725192735.5EC2D29013F@hades.porcupine.org>
- 2042 pointer_record: 1642
- 1642 pointer_record: 2059
- 2059 pointer_record: 2121
- 2121 regular_text: Date: Tue, 25 Jul 2006 15:27:19 -0400 (EDT)
- 2166 pointer_record: 1704
- 1704 pointer_record: 2183
- 2183 pointer_record: 2218
- 2218 regular_text: Subject: hey!
- 2233 padding: 0
- 2236 pointer_record: 489
+ 317 pointer_record: 842
+ 842 pointer_record: 1315
+ 1315 pointer_record: 1356
+ 1356 pointer_record: 1450
+ 1450 pointer_record: 1707
+ 1707 pointer_record: 1748
+ 1748 regular_text: From: me@porcupine.org
+ 1772 pointer_record: 1491
+ 1491 pointer_record: 1789
+ 1789 pointer_record: 1829
+ 1829 regular_text: To: you@porcupine.org
+ 1852 pointer_record: 1531
+ 1531 pointer_record: 1869
+ 1869 pointer_record: 1948
+ 1948 regular_text: Message-Id: <20060725192735.5EC2D29013F@hades.porcupine.org>
+ 2010 pointer_record: 1610
+ 1610 pointer_record: 2027
+ 2027 pointer_record: 2089
+ 2089 regular_text: Date: Tue, 25 Jul 2006 15:27:19 -0400 (EDT)
+ 2134 pointer_record: 1672
+ 1672 pointer_record: 2151
+ 2151 pointer_record: 2186
+ 2186 regular_text: Subject: hey!
+ 2201 padding: 0
+ 2204 pointer_record: 489
489 pointer_record: 0
506 regular_text:
508 regular_text: text
747 original_recipient: alias@hades.porcupine.org
774 recipient: root@porcupine.org
794 pointer_record: 1258
- 1258 named_attribute: notify_flags=1
- 1274 original_recipient: 01
- 1278 canceled_recipient: 01
- 1282 pointer_record: 1299
- 1299 named_attribute: notify_flags=1
- 1315 original_recipient: 02
- 1319 canceled_recipient: 02
- 1323 pointer_record: 1340
- 1340 named_attribute: notify_flags=1
- 1356 original_recipient: 03
- 1360 canceled_recipient: 03
- 1364 pointer_record: 811
+ 1258 original_recipient: 01
+ 1262 canceled_recipient: 01
+ 1266 pointer_record: 1283
+ 1283 original_recipient: 02
+ 1287 canceled_recipient: 02
+ 1291 pointer_record: 1308
+ 1308 original_recipient: 03
+ 1312 canceled_recipient: 03
+ 1316 pointer_record: 811
811 *** MESSAGE CONTENTS test-queue-file4.tmp ***
813 regular_text: Received: from hades.porcupine.org (hades.porcupine.org [168.100.189.10])
888 regular_text: by hades.porcupine.org (Postfix) with SMTP id 38132290405;
flush.o: ../../include/mail_version.h
flush.o: ../../include/maps.h
flush.o: ../../include/match_list.h
-flush.o: ../../include/match_ops.h
flush.o: ../../include/match_parent_style.h
flush.o: ../../include/msg.h
flush.o: ../../include/myflock.h
fold_addr.c header_body_checks.c mkmap_proxy.c data_redirect.c \
match_service.c mail_conf_nint.c addr_match_list.c mail_conf_nbool.c \
smtp_reply_footer.c safe_ultostr.c verify_sender_addr.c \
- dict_memcache.c mail_version.c memcache_proto.c server_acl.c
+ dict_memcache.c mail_version.c memcache_proto.c server_acl.c \
+ mkmap_fail.c
OBJS = abounce.o anvil_clnt.o been_here.o bounce.o bounce_log.o \
canon_addr.o cfg_parser.o cleanup_strerror.o cleanup_strflags.o \
clnt_stream.o conv_time.o db_common.o debug_peer.o debug_process.o \
fold_addr.o header_body_checks.o mkmap_proxy.o data_redirect.o \
match_service.o mail_conf_nint.o addr_match_list.o mail_conf_nbool.o \
smtp_reply_footer.o safe_ultostr.o verify_sender_addr.o \
- dict_memcache.o mail_version.o memcache_proto.o server_acl.o
+ dict_memcache.o mail_version.o memcache_proto.o server_acl.o \
+ mkmap_fail.o
HDRS = abounce.h anvil_clnt.h been_here.h bounce.h bounce_log.h \
canon_addr.h cfg_parser.h cleanup_user.h clnt_stream.h config.h \
conv_time.h db_common.h debug_peer.h debug_process.h defer.h \
abounce.o: msg_stats.h
abounce.o: recipient_list.h
addr_match_list.o: ../../include/argv.h
-addr_match_list.o: ../../include/dict.h
addr_match_list.o: ../../include/match_list.h
-addr_match_list.o: ../../include/match_ops.h
addr_match_list.o: ../../include/sys_defs.h
-addr_match_list.o: ../../include/vbuf.h
-addr_match_list.o: ../../include/vstream.h
-addr_match_list.o: ../../include/vstring.h
addr_match_list.o: addr_match_list.c
addr_match_list.o: addr_match_list.h
anvil_clnt.o: ../../include/attr.h
db_common.o: ../../include/argv.h
db_common.o: ../../include/dict.h
db_common.o: ../../include/match_list.h
-db_common.o: ../../include/match_ops.h
db_common.o: ../../include/msg.h
db_common.o: ../../include/mymalloc.h
db_common.o: ../../include/sys_defs.h
db_common.o: db_common.h
db_common.o: string_list.h
debug_peer.o: ../../include/argv.h
-debug_peer.o: ../../include/dict.h
debug_peer.o: ../../include/match_list.h
-debug_peer.o: ../../include/match_ops.h
debug_peer.o: ../../include/msg.h
debug_peer.o: ../../include/sys_defs.h
-debug_peer.o: ../../include/vbuf.h
-debug_peer.o: ../../include/vstream.h
-debug_peer.o: ../../include/vstring.h
debug_peer.o: debug_peer.c
debug_peer.o: debug_peer.h
debug_peer.o: mail_params.h
dict_ldap.o: ../../include/binhash.h
dict_ldap.o: ../../include/dict.h
dict_ldap.o: ../../include/match_list.h
-dict_ldap.o: ../../include/match_ops.h
dict_ldap.o: ../../include/msg.h
dict_ldap.o: ../../include/mymalloc.h
dict_ldap.o: ../../include/name_code.h
dict_memcache.o: ../../include/auto_clnt.h
dict_memcache.o: ../../include/dict.h
dict_memcache.o: ../../include/match_list.h
-dict_memcache.o: ../../include/match_ops.h
dict_memcache.o: ../../include/msg.h
dict_memcache.o: ../../include/mymalloc.h
dict_memcache.o: ../../include/stringops.h
dict_memcache.o: dict_memcache.h
dict_memcache.o: memcache_proto.h
dict_memcache.o: string_list.h
-dict_mysql.o: ../../include/argv.h
-dict_mysql.o: ../../include/dict.h
-dict_mysql.o: ../../include/events.h
-dict_mysql.o: ../../include/find_inet.h
-dict_mysql.o: ../../include/match_list.h
-dict_mysql.o: ../../include/match_ops.h
-dict_mysql.o: ../../include/msg.h
-dict_mysql.o: ../../include/mymalloc.h
-dict_mysql.o: ../../include/myrand.h
-dict_mysql.o: ../../include/split_at.h
-dict_mysql.o: ../../include/stringops.h
dict_mysql.o: ../../include/sys_defs.h
-dict_mysql.o: ../../include/vbuf.h
-dict_mysql.o: ../../include/vstream.h
-dict_mysql.o: ../../include/vstring.h
-dict_mysql.o: cfg_parser.h
-dict_mysql.o: db_common.h
dict_mysql.o: dict_mysql.c
-dict_mysql.o: dict_mysql.h
-dict_mysql.o: string_list.h
-dict_pgsql.o: ../../include/argv.h
-dict_pgsql.o: ../../include/dict.h
-dict_pgsql.o: ../../include/events.h
-dict_pgsql.o: ../../include/find_inet.h
-dict_pgsql.o: ../../include/match_list.h
-dict_pgsql.o: ../../include/match_ops.h
-dict_pgsql.o: ../../include/msg.h
-dict_pgsql.o: ../../include/mymalloc.h
-dict_pgsql.o: ../../include/myrand.h
-dict_pgsql.o: ../../include/split_at.h
-dict_pgsql.o: ../../include/stringops.h
dict_pgsql.o: ../../include/sys_defs.h
-dict_pgsql.o: ../../include/vbuf.h
-dict_pgsql.o: ../../include/vstream.h
-dict_pgsql.o: ../../include/vstring.h
-dict_pgsql.o: cfg_parser.h
-dict_pgsql.o: db_common.h
dict_pgsql.o: dict_pgsql.c
-dict_pgsql.o: dict_pgsql.h
-dict_pgsql.o: string_list.h
dict_proxy.o: ../../include/argv.h
dict_proxy.o: ../../include/attr.h
dict_proxy.o: ../../include/dict.h
dict_sqlite.o: ../../include/argv.h
dict_sqlite.o: ../../include/dict.h
dict_sqlite.o: ../../include/match_list.h
-dict_sqlite.o: ../../include/match_ops.h
dict_sqlite.o: ../../include/msg.h
dict_sqlite.o: ../../include/mymalloc.h
dict_sqlite.o: ../../include/stringops.h
dict_sqlite.o: dict_sqlite.h
dict_sqlite.o: string_list.h
domain_list.o: ../../include/argv.h
-domain_list.o: ../../include/dict.h
domain_list.o: ../../include/match_list.h
-domain_list.o: ../../include/match_ops.h
domain_list.o: ../../include/sys_defs.h
-domain_list.o: ../../include/vbuf.h
-domain_list.o: ../../include/vstream.h
-domain_list.o: ../../include/vstring.h
domain_list.o: domain_list.c
domain_list.o: domain_list.h
dot_lockfile.o: ../../include/iostuff.h
file_id.o: safe_ultostr.h
flush_clnt.o: ../../include/argv.h
flush_clnt.o: ../../include/attr.h
-flush_clnt.o: ../../include/dict.h
flush_clnt.o: ../../include/iostuff.h
flush_clnt.o: ../../include/match_list.h
-flush_clnt.o: ../../include/match_ops.h
flush_clnt.o: ../../include/msg.h
flush_clnt.o: ../../include/sys_defs.h
flush_clnt.o: ../../include/vbuf.h
flush_clnt.o: ../../include/vstream.h
-flush_clnt.o: ../../include/vstring.h
flush_clnt.o: domain_list.h
flush_clnt.o: flush_clnt.c
flush_clnt.o: flush_clnt.h
mark_corrupt.o: msg_stats.h
mark_corrupt.o: recipient_list.h
match_parent_style.o: ../../include/argv.h
-match_parent_style.o: ../../include/dict.h
match_parent_style.o: ../../include/match_list.h
-match_parent_style.o: ../../include/match_ops.h
match_parent_style.o: ../../include/sys_defs.h
-match_parent_style.o: ../../include/vbuf.h
-match_parent_style.o: ../../include/vstream.h
-match_parent_style.o: ../../include/vstring.h
match_parent_style.o: mail_params.h
match_parent_style.o: match_parent_style.c
match_parent_style.o: match_parent_style.h
mime_state.o: mime_state.c
mime_state.o: mime_state.h
mime_state.o: rec_type.h
+mkmap_cdb.o: ../../include/argv.h
+mkmap_cdb.o: ../../include/dict.h
+mkmap_cdb.o: ../../include/dict_cdb.h
+mkmap_cdb.o: ../../include/mymalloc.h
mkmap_cdb.o: ../../include/sys_defs.h
+mkmap_cdb.o: ../../include/vbuf.h
+mkmap_cdb.o: ../../include/vstream.h
+mkmap_cdb.o: ../../include/vstring.h
+mkmap_cdb.o: mkmap.h
mkmap_cdb.o: mkmap_cdb.c
mkmap_db.o: ../../include/argv.h
mkmap_db.o: ../../include/dict.h
mkmap_dbm.o: ../../include/vstring.h
mkmap_dbm.o: mkmap.h
mkmap_dbm.o: mkmap_dbm.c
+mkmap_fail.o: ../../include/argv.h
+mkmap_fail.o: ../../include/dict.h
+mkmap_fail.o: ../../include/dict_fail.h
+mkmap_fail.o: ../../include/mymalloc.h
+mkmap_fail.o: ../../include/sys_defs.h
+mkmap_fail.o: ../../include/vbuf.h
+mkmap_fail.o: ../../include/vstream.h
+mkmap_fail.o: ../../include/vstring.h
+mkmap_fail.o: mkmap.h
+mkmap_fail.o: mkmap_fail.c
mkmap_open.o: ../../include/argv.h
mkmap_open.o: ../../include/dict.h
mkmap_open.o: ../../include/dict_cdb.h
mkmap_open.o: ../../include/dict_db.h
mkmap_open.o: ../../include/dict_dbm.h
+mkmap_open.o: ../../include/dict_fail.h
mkmap_open.o: ../../include/dict_sdbm.h
mkmap_open.o: ../../include/msg.h
mkmap_open.o: ../../include/mymalloc.h
mypwd.o: mypwd.c
mypwd.o: mypwd.h
namadr_list.o: ../../include/argv.h
-namadr_list.o: ../../include/dict.h
namadr_list.o: ../../include/match_list.h
-namadr_list.o: ../../include/match_ops.h
namadr_list.o: ../../include/sys_defs.h
-namadr_list.o: ../../include/vbuf.h
-namadr_list.o: ../../include/vstream.h
-namadr_list.o: ../../include/vstring.h
namadr_list.o: namadr_list.c
namadr_list.o: namadr_list.h
off_cvt.o: ../../include/msg.h
resolve_local.o: ../../include/dict.h
resolve_local.o: ../../include/inet_addr_list.h
resolve_local.o: ../../include/match_list.h
-resolve_local.o: ../../include/match_ops.h
resolve_local.o: ../../include/msg.h
resolve_local.o: ../../include/myaddrinfo.h
resolve_local.o: ../../include/mymalloc.h
server_acl.o: ../../include/argv.h
server_acl.o: ../../include/dict.h
server_acl.o: ../../include/match_list.h
-server_acl.o: ../../include/match_ops.h
server_acl.o: ../../include/msg.h
server_acl.o: ../../include/mymalloc.h
server_acl.o: ../../include/stringops.h
stream2rec.o: record.h
stream2rec.o: stream2rec.c
string_list.o: ../../include/argv.h
-string_list.o: ../../include/dict.h
string_list.o: ../../include/match_list.h
-string_list.o: ../../include/match_ops.h
string_list.o: ../../include/sys_defs.h
-string_list.o: ../../include/vbuf.h
-string_list.o: ../../include/vstream.h
-string_list.o: ../../include/vstring.h
string_list.o: string_list.c
string_list.o: string_list.h
strip_addr.o: ../../include/mymalloc.h
user_acl.o: ../../include/dict.h
user_acl.o: ../../include/dict_static.h
user_acl.o: ../../include/match_list.h
-user_acl.o: ../../include/match_ops.h
user_acl.o: ../../include/sys_defs.h
user_acl.o: ../../include/vbuf.h
user_acl.o: ../../include/vstream.h
/* addr_match_list_init() performs initializations. The first
/* argument is the bit-wise OR of zero or more of the following:
/* .IP MATCH_FLAG_RETURN
-/* Request that addr_match_list_match() returns zero with
-/* dict_errno != 0, instead of raising a fatal error.
+/* Request that addr_match_list_match() logs a warning and
+/* returns zero with list->error set to a non-zero dictionary
+/* error code, instead of raising a fatal error.
/* .PP
/* Specify MATCH_FLAG_NONE to request none of the above.
/* The second argument is a list of patterns, or the absolute
while (vstring_get_nonl(buf, VSTREAM_IN) != VSTREAM_EOF)
vstream_printf("%s: %s\n", vstring_str(buf),
addr_match_list_match(list, vstring_str(buf)) ?
- "YES" : dict_errno == 0 ? "NO" : "ERROR");
+ "YES" : list->error == 0 ? "NO" : "ERROR");
vstring_free(buf);
} else {
vstream_printf("%s: %s\n", addr,
addr_match_list_match(list, addr) > 0 ?
- "YES" : dict_errno == 0 ? "NO" : "ERROR");
+ "YES" : list->error == 0 ? "NO" : "ERROR");
}
vstream_fflush(VSTREAM_OUT);
addr_match_list_free(list);
/* If \fIkey\fR is a fully qualified address, the domain part of the
/* address. Otherwise the query against the database is suppressed and
/* the lookup returns no results.
-/*
/* .PP
-/* \fIdb_common_check_domain\fR checks domain list so that query optimization
-/* can be performed
+/* \fIdb_common_check_domain\fR() checks the domain list so
+/* that query optimization can be performed. The result is >0
+/* (match found), 0 (no match), or <0 (dictionary error code).
/*
/* .PP
/* \fIdb_common_sql_build_query\fR builds the "default"(backwards compatible)
domainlist = cfg_get_str(parser, "domain", "", 0, 0);
if (*domainlist) {
- ctx->domain = string_list_init(MATCH_FLAG_NONE, domainlist);
+ ctx->domain = string_list_init(MATCH_FLAG_RETURN, domainlist);
if (ctx->domain == 0)
/*
if (domain == NULL || domain == addr + 1)
return (0);
if (match_list_match(ctx->domain, domain) == 0)
- return (0);
+ return (ctx->domain->error);
}
return (1);
}
/* .IP recursion_limit
/* Maximum recursion depth when expanding DN or URL references.
/* Queries which exceed the recursion limit fail with
-/* dict_errno = DICT_ERR_RETRY.
+/* dict->error = DICT_ERR_RETRY.
/* .IP expansion_limit
/* Limit (if any) on the total number of lookup result values. Lookups which
-/* exceed the limit fail with dict_errno=DICT_ERR_RETRY. Note that
+/* exceed the limit fail with dict->error=DICT_ERR_RETRY. Note that
/* each value of a multivalued result attribute counts as one result.
/* .IP size_limit
/* Limit on the number of entries returned by individual LDAP queries.
-/* Queries which exceed the limit fail with dict_errno=DICT_ERR_RETRY.
+/* Queries which exceed the limit fail with dict->error=DICT_ERR_RETRY.
/* This is an *entry* count, for any single query performed during the
/* possibly recursive lookup.
/* .IP chase_referrals
#define DICT_LDAP_UNBIND_RETURN(__ld, __err, __ret) do { \
dict_ldap_unbind(__ld); \
(__ld) = 0; \
- dict_errno = (__err); \
+ dict_ldap->dict.error = (__err); \
return ((__ret)); \
} while (0)
msg_warn("%s: Unable to set LDAP debug level.", myname);
#endif
- dict_errno = 0;
+ dict_ldap->dict.error = 0;
if (msg_verbose)
msg_info("%s: Connecting to server %s", myname,
if (dict_ldap->ld == NULL) {
msg_warn("%s: Unable to init LDAP server %s",
myname, dict_ldap->server_host);
- dict_errno = DICT_ERR_RETRY;
+ dict_ldap->dict.error = DICT_ERR_RETRY;
return (-1);
}
mytimeval.tv_sec = dict_ldap->timeout;
if ((saved_alarm = signal(SIGALRM, dict_ldap_timeout)) == SIG_ERR) {
msg_warn("%s: Error setting signal handler for open timeout: %m",
myname);
- dict_errno = DICT_ERR_RETRY;
+ dict_ldap->dict.error = DICT_ERR_RETRY;
return (-1);
}
alarm(dict_ldap->timeout);
if (signal(SIGALRM, saved_alarm) == SIG_ERR) {
msg_warn("%s: Error resetting signal handler after open: %m",
myname);
- dict_errno = DICT_ERR_RETRY;
+ dict_ldap->dict.error = DICT_ERR_RETRY;
return (-1);
}
if (dict_ldap->ld == NULL) {
msg_warn("%s: Unable to connect to LDAP server %s",
myname, dict_ldap->server_host);
- dict_errno = DICT_ERR_RETRY;
+ dict_ldap->dict.error = DICT_ERR_RETRY;
return (-1);
}
#endif
if (signal(SIGALRM, saved_alarm) == SIG_ERR) {
msg_warn("%s: Error resetting signal handler after STARTTLS: %m",
myname);
- dict_errno = DICT_ERR_RETRY;
+ dict_ldap->dict.error = DICT_ERR_RETRY;
return (-1);
}
if (rc != LDAP_SUCCESS) {
msg_error("%s: Unable to set STARTTLS: %d: %s", myname,
rc, ldap_err2string(rc));
- dict_errno = DICT_ERR_RETRY;
+ dict_ldap->dict.error = DICT_ERR_RETRY;
return (-1);
}
}
* LDAP should not, but may produce more than the requested maximum
* number of entries.
*/
- if (dict_errno == 0
+ if (dict_ldap->dict.error == 0
&& dict_ldap->size_limit
&& ++entries > dict_ldap->size_limit) {
msg_warn("%s[%d]: %s: Query size limit (%ld) exceeded",
myname, recursion, dict_ldap->parser->name,
dict_ldap->size_limit);
- dict_errno = DICT_ERR_RETRY;
+ dict_ldap->dict.error = DICT_ERR_RETRY;
}
/*
* leaks, but it will likely be more fragile and not worth the
* extra code.
*/
- if (dict_errno != 0 || valcount == 0) {
+ if (dict_ldap->dict.error != 0 || valcount == 0) {
ldap_value_free_len(vals);
continue;
}
msg_warn("%s[%d]: %s: Expansion limit exceeded "
"for key: '%s'", myname, recursion,
dict_ldap->parser->name, name);
- dict_errno = DICT_ERR_RETRY;
+ dict_ldap->dict.error = DICT_ERR_RETRY;
break;
}
}
- if (dict_errno != 0)
+ if (dict_ldap->dict.error != 0)
continue;
if (msg_verbose)
msg_info("%s[%d]: search returned %d value(s) for"
msg_warn("%s[%d]: malformed URL %s: %s(%d)",
myname, recursion, vals[i]->bv_val,
ldap_err2string(rc), rc);
- dict_errno = DICT_ERR_RETRY;
+ dict_ldap->dict.error = DICT_ERR_RETRY;
break;
}
} else {
default:
msg_warn("%s[%d]: search error %d: %s ", myname,
recursion, rc, ldap_err2string(rc));
- dict_errno = DICT_ERR_RETRY;
+ dict_ldap->dict.error = DICT_ERR_RETRY;
break;
}
if (resloop != 0)
ldap_msgfree(resloop);
- if (dict_errno != 0)
+ if (dict_ldap->dict.error != 0)
break;
}
- if (msg_verbose && dict_errno == 0)
+ if (msg_verbose && dict_ldap->dict.error == 0)
msg_info("%s[%d]: search returned %d value(s) for"
" special result attribute %s",
myname, recursion, valcount, attr);
msg_warn("%s[%d]: %s: Recursion limit exceeded"
" for special attribute %s=%s", myname, recursion,
dict_ldap->parser->name, attr, vals[0]->bv_val);
- dict_errno = DICT_ERR_RETRY;
+ dict_ldap->dict.error = DICT_ERR_RETRY;
}
ldap_value_free_len(vals);
}
static VSTRING *result;
int rc = 0;
int sizelimit;
+ int domain_rc;
- dict_errno = 0;
+ dict_ldap->dict.error = 0;
if (msg_verbose)
msg_info("%s: In dict_ldap_lookup", myname);
* addresses in domains on the list. This can significantly reduce the
* load on the LDAP server.
*/
- if (db_common_check_domain(dict_ldap->ctx, name) == 0) {
+ if ((domain_rc = db_common_check_domain(dict_ldap->ctx, name)) == 0) {
if (msg_verbose)
msg_info("%s: %s: Skipping lookup of key '%s': domain mismatch",
myname, dict_ldap->parser->name, name);
return (0);
}
+ if (domain_rc < 0)
+ DICT_ERR_VAL_RETURN(dict, domain_rc, (char *) 0);
+
#define INIT_VSTR(buf, len) do { \
if (buf == 0) \
buf = vstring_alloc(len); \
dict_ldap_connect(dict_ldap);
/*
- * if dict_ldap_connect() set dict_errno, abort.
+ * if dict_ldap_connect() set dict_ldap->dict.error, abort.
*/
- if (dict_errno)
+ if (dict_ldap->dict.error)
return (0);
} else if (msg_verbose)
msg_info("%s: Using existing connection for LDAP source %s",
!= LDAP_OPT_SUCCESS) {
msg_warn("%s: %s: Unable to set query result size limit to %ld.",
myname, dict_ldap->parser->name, dict_ldap->size_limit);
- dict_errno = DICT_ERR_RETRY;
+ dict_ldap->dict.error = DICT_ERR_RETRY;
return (0);
}
dict_ldap_connect(dict_ldap);
/*
- * if dict_ldap_connect() set dict_errno, abort.
+ * if dict_ldap_connect() set dict_ldap->dict.error, abort.
*/
- if (dict_errno)
+ if (dict_ldap->dict.error)
return (0);
rc = search_st(dict_ldap->ld, vstring_str(base), dict_ldap->scope,
msg_warn("%s: %s: Search base '%s' not found: %d: %s",
myname, dict_ldap->parser->name,
vstring_str(base), rc, ldap_err2string(rc));
- dict_errno = DICT_ERR_RETRY;
+ dict_ldap->dict.error = DICT_ERR_RETRY;
break;
default:
/*
* And tell the caller to try again later.
*/
- dict_errno = DICT_ERR_RETRY;
+ dict_ldap->dict.error = DICT_ERR_RETRY;
break;
}
* If we had an error, return nothing, Otherwise, return the result, if
* any.
*/
- return (VSTRING_LEN(result) > 0 && !dict_errno ? vstring_str(result) : 0);
+ return (VSTRING_LEN(result) > 0 && !dict_ldap->dict.error ? vstring_str(result) : 0);
}
/* dict_ldap_close - disassociate from data base */
VSTRING *clnt_buf; /* memcache client buffer */
VSTRING *key_buf; /* lookup key */
VSTRING *res_buf; /* lookup result */
- int mc_errno; /* memcache dict_errno */
+ int error; /* memcache dict_errno */
DICT *backup; /* persistent backup */
} DICT_MC;
/* dict_memcache_set - set memcache key/value */
-static void dict_memcache_set(DICT_MC *dict_mc, const char *value, int ttl)
+static int dict_memcache_set(DICT_MC *dict_mc, const char *value, int ttl)
{
VSTREAM *fp;
int count;
int data_len = strlen(value);
/*
- * If we can't retrieve it, then we must not store it.
+ * Return a permanent error if we can't store this data. This results in
+ * loss of information.
*/
- dict_mc->mc_errno = DICT_ERR_RETRY; /* XXX */
if (data_len > dict_mc->max_data) {
msg_warn("database %s:%s: data for key %s is too long (%s=%d) "
"-- not stored", DICT_TYPE_MEMCACHE, dict_mc->dict.name,
STR(dict_mc->key_buf), DICT_MC_NAME_MAX_DATA,
dict_mc->max_data);
- return;
+ /* Not stored! */
+ DICT_ERR_VAL_RETURN(dict_mc, DICT_ERR_NONE, DICT_STAT_FAIL);
}
for (count = 0; count < dict_mc->max_tries; count++) {
if (count > 0)
STR(dict_mc->clnt_buf));
} else {
/* Victory! */
- dict_mc->mc_errno = 0;
- break;
+ DICT_ERR_VAL_RETURN(dict_mc, DICT_ERR_NONE, DICT_STAT_SUCCESS);
}
auto_clnt_recover(dict_mc->clnt);
}
+ DICT_ERR_VAL_RETURN(dict_mc, DICT_ERR_RETRY, DICT_STAT_ERROR);
}
/* dict_memcache_get - get memcache key/value */
{
VSTREAM *fp;
long todo;
- const char *retval;
int count;
- dict_mc->mc_errno = DICT_ERR_RETRY;
- retval = 0;
for (count = 0; count < dict_mc->max_tries; count++) {
if (count > 0)
sleep(dict_mc->err_pause);
DICT_TYPE_MEMCACHE, dict_mc->dict.name);
} else if (strcmp(STR(dict_mc->clnt_buf), "END") == 0) {
/* Not found. */
- dict_mc->mc_errno = 0;
- break;
+ DICT_ERR_VAL_RETURN(dict_mc, DICT_ERR_NONE, (char *) 0);
} else if (sscanf(STR(dict_mc->clnt_buf),
"VALUE %*s %*s %ld", &todo) != 1
|| todo < 0 || todo > dict_mc->max_data) {
dict_mc->dict.name);
} else {
/* Victory! */
- retval = STR(dict_mc->res_buf);
- dict_mc->mc_errno = 0;
if (memcache_get(fp, dict_mc->clnt_buf, dict_mc->max_line) < 0
|| strcmp(STR(dict_mc->clnt_buf), "END") != 0)
auto_clnt_recover(dict_mc->clnt);
- break;
+ DICT_ERR_VAL_RETURN(dict_mc, DICT_ERR_NONE, STR(dict_mc->res_buf));
}
auto_clnt_recover(dict_mc->clnt);
}
- return (retval);
+ DICT_ERR_VAL_RETURN(dict_mc, DICT_ERR_RETRY, (char *) 0);
}
/* dict_memcache_del - delete memcache key/value */
{
VSTREAM *fp;
int count;
- int retval = -1;
- dict_mc->mc_errno = DICT_ERR_RETRY;
for (count = 0; count < dict_mc->max_tries; count++) {
if (count > 0)
sleep(dict_mc->err_pause);
DICT_TYPE_MEMCACHE, dict_mc->dict.name);
} else if (strcmp(STR(dict_mc->clnt_buf), "DELETED") == 0) {
/* Victory! */
- dict_mc->mc_errno = 0;
- retval = 0;
- break;
+ DICT_ERR_VAL_RETURN(dict_mc, DICT_ERR_NONE, DICT_STAT_SUCCESS);
} else if (strcmp(STR(dict_mc->clnt_buf), "NOT_FOUND") == 0) {
/* Not found! */
- dict_mc->mc_errno = 0;
- retval = 1;
- break;
+ DICT_ERR_VAL_RETURN(dict_mc, DICT_ERR_NONE, DICT_STAT_FAIL);
} else {
if (count > 0)
msg_warn("database %s:%s: delete failed: %.30s",
}
auto_clnt_recover(dict_mc->clnt);
}
- return (retval);
+ DICT_ERR_VAL_RETURN(dict_mc, DICT_ERR_RETRY, DICT_STAT_ERROR);
}
/* dict_memcache_prepare_key - prepare lookup key */
void (*log_func) (const char *,...))
{
unsigned char *cp;
+ int rc;
#define DICT_MC_SKIP(why) do { \
if (msg_verbose || log_func != msg_info) \
log_func("%s: skipping %s for name \"%s\": %s", \
dict_mc->dict.name, operation, name, (why)); \
- return(0); \
+ DICT_ERR_VAL_RETURN(dict_mc, DICT_ERR_NONE, 0); \
} while (0)
if (*name == 0)
DICT_MC_SKIP("empty lookup key");
- if (db_common_check_domain(dict_mc->dbc_ctxt, name) == 0)
+ if ((rc = db_common_check_domain(dict_mc->dbc_ctxt, name)) == 0)
DICT_MC_SKIP("domain mismatch");
+ if (rc < 0)
+ DICT_ERR_VAL_RETURN(dict_mc, rc, 0);
if (dict_memcache_prepare_key(dict_mc, name) == 0)
DICT_MC_SKIP("empty lookup key expansion");
for (cp = (unsigned char *) STR(dict_mc->key_buf); *cp; cp++)
if (isascii(*cp) && isspace(*cp))
DICT_MC_SKIP("name contains space");
- return (1);
+ DICT_ERR_VAL_RETURN(dict_mc, DICT_ERR_NONE, 1);
}
/* dict_memcache_update - update memcache */
-static void dict_memcache_update(DICT *dict, const char *name,
- const char *value)
+static int dict_memcache_update(DICT *dict, const char *name,
+ const char *value)
{
const char *myname = "dict_memcache_update";
DICT_MC *dict_mc = (DICT_MC *) dict;
DICT *backup = dict_mc->backup;
- int backup_errno = 0;
+ int upd_res;
/*
* Skip updates with an inapplicable key, noisily. This results in loss
* of information.
*/
- dict_errno = DICT_ERR_RETRY;
if (dict_memcache_valid_key(dict_mc, name, "update", msg_warn) == 0)
- return;
+ DICT_ERR_VAL_RETURN(dict, dict_mc->error, DICT_STAT_FAIL);
/*
* Update the memcache first.
*/
- dict_memcache_set(dict_mc, value, dict_mc->mc_ttl);
+ upd_res = dict_memcache_set(dict_mc, value, dict_mc->mc_ttl);
+ dict->error = dict_mc->error;
/*
* Update the backup database last.
*/
if (backup) {
- dict_errno = 0;
- backup->update(backup, name, value);
- backup_errno = dict_errno;
+ upd_res = backup->update(backup, name, value);
+ dict->error = backup->error;
}
if (msg_verbose)
msg_info("%s: %s: update key \"%s\"(%s) => \"%s\" %s",
myname, dict_mc->dict.name, name, STR(dict_mc->key_buf),
- value, dict_mc->mc_errno ? "(memcache error)" :
- backup_errno ? "(backup error)" : "(no error)");
+ value, dict_mc->error ? "(memcache error)" : (backup
+ && backup->error) ? "(backup error)" : "(no error)");
- dict_errno = (backup ? backup_errno : dict_mc->mc_errno);
+ return (upd_res);
}
/* dict_memcache_lookup - lookup memcache */
DICT_MC *dict_mc = (DICT_MC *) dict;
DICT *backup = dict_mc->backup;
const char *retval;
- int backup_errno = 0;
/*
* Skip lookups with an inapplicable key, silently. This is just asking
* for information that cannot exist.
*/
- dict_errno = 0;
if (dict_memcache_valid_key(dict_mc, name, "lookup", msg_info) == 0)
- return (0);
+ DICT_ERR_VAL_RETURN(dict, dict_mc->error, (char *) 0);
/*
* Search the memcache first.
*/
retval = dict_memcache_get(dict_mc);
+ dict->error = dict_mc->error;
/*
* Search the backup database last. Update the memcache if the data is
* found.
*/
- if (retval == 0 && backup) {
- retval = backup->lookup(backup, name);
- backup_errno = dict_errno;
- /* Update the cache. */
- if (retval != 0)
- dict_memcache_set(dict_mc, retval, dict_mc->mc_ttl);
+ if (backup) {
+ backup->error = 0;
+ if (retval == 0) {
+ retval = backup->lookup(backup, name);
+ dict->error = backup->error;
+ /* Update the cache. */
+ if (retval != 0)
+ dict_memcache_set(dict_mc, retval, dict_mc->mc_ttl);
+ }
}
if (msg_verbose)
msg_info("%s: %s: key \"%s\"(%s) => %s",
myname, dict_mc->dict.name, name, STR(dict_mc->key_buf),
- retval ? retval : dict_mc->mc_errno ? "(memcache error)" :
- backup_errno ? "(backup error)" : "(not found)");
-
- dict_errno = (backup ? backup_errno : dict_mc->mc_errno);
+ retval ? retval : dict_mc->error ? "(memcache error)" :
+ (backup && backup->error) ? "(backup error)" : "(not found)");
return (retval);
}
const char *myname = "dict_memcache_delete";
DICT_MC *dict_mc = (DICT_MC *) dict;
DICT *backup = dict_mc->backup;
- int backup_errno = 0;
int del_res;
/*
- * Skip lookups with an inapplicable key, silently. This is just deleting
+ * Skip lookups with an inapplicable key, noisily. This is just deleting
* information that cannot exist.
*/
- dict_errno = 0;
if (dict_memcache_valid_key(dict_mc, name, "delete", msg_info) == 0)
- return (1);
+ DICT_ERR_VAL_RETURN(dict, dict_mc->error, dict_mc->error ?
+ DICT_STAT_ERROR : DICT_STAT_FAIL);
/*
* Update the memcache first.
*/
del_res = dict_memcache_del(dict_mc);
+ dict->error = dict_mc->error;
/*
* Update the persistent database last.
*/
if (backup) {
- dict_errno = 0;
del_res = backup->delete(backup, name);
- backup_errno = dict_errno;
+ dict->error = backup->error;
}
if (msg_verbose)
msg_info("%s: %s: delete key \"%s\"(%s) => %s",
myname, dict_mc->dict.name, name, STR(dict_mc->key_buf),
- dict_mc->mc_errno ? "(memcache error)" :
- backup_errno ? "(backup error)" : "(no error)");
-
- dict_errno = (backup ? backup_errno : dict_mc->mc_errno);
+ dict_mc->error ? "(memcache error)" : (backup
+ && backup->error) ? "(backup error)" : "(no error)");
return (del_res);
}
static int dict_memcache_sequence(DICT *dict, int function, const char **key,
const char **value)
{
+ const char *myname = "dict_memcache_sequence";
DICT_MC *dict_mc = (DICT_MC *) dict;
DICT *backup = dict_mc->backup;
+ int seq_res;
if (backup == 0) {
msg_warn("database %s:%s: first/next support requires backup database",
DICT_TYPE_MEMCACHE, dict_mc->dict.name);
- return (1);
+ DICT_ERR_VAL_RETURN(dict, DICT_ERR_NONE, DICT_STAT_FAIL);
} else {
- return (backup->sequence(backup, function, key, value));
+ seq_res = backup->sequence(backup, function, key, value);
+ msg_info("%s: %s: key \"%s\" => %s",
+ myname, dict_mc->dict.name, *key ? *key : "(not found)",
+ *value ? *value : backup->error ? "(backup error)" :
+ "(not found)");
+ DICT_ERR_VAL_RETURN(dict, backup->error, seq_res);
}
}
/* the lookup result unchanged.
/* .IP expansion_limit
/* Limit (if any) on the total number of lookup result values. Lookups which
-/* exceed the limit fail with dict_errno=DICT_ERR_RETRY. Note that each
+/* exceed the limit fail with dict->error=DICT_ERR_RETRY. Note that each
/* non-empty (and non-NULL) column of a multi-column result row counts as
/* one result.
/* .IP table
#define TYPEINET (1<<1)
#define RETRY_CONN_MAX 100
-#define RETRY_CONN_INTV 60 /* 1 minute */
-#define IDLE_CONN_INTV 60 /* 1 minute */
+#define RETRY_CONN_INTV 60 /* 1 minute */
+#define IDLE_CONN_INTV 60 /* 1 minute */
/* internal function declarations */
static PLMYSQL *plmysql_init(ARGV *);
static MYSQL_RES *plmysql_query(DICT_MYSQL *, const char *, VSTRING *, char *,
- char *, char *);
+ char *, char *);
static void plmysql_dealloc(PLMYSQL *);
static void plmysql_close_host(HOST *);
static void plmysql_down_host(HOST *);
static void dict_mysql_quote(DICT *dict, const char *name, VSTRING *result)
{
DICT_MYSQL *dict_mysql = (DICT_MYSQL *) dict;
- int len = strlen(name);
- int buflen = 2*len + 1;
+ int len = strlen(name);
+ int buflen = 2 * len + 1;
/*
- * We won't get integer overflows in 2*len + 1, because Postfix
- * input keys have reasonable size limits, better safe than sorry.
+ * We won't get integer overflows in 2*len + 1, because Postfix input
+ * keys have reasonable size limits, better safe than sorry.
*/
if (buflen < len)
msg_panic("dict_mysql_quote: integer overflow in 2*%d+1", len);
static const char *dict_mysql_lookup(DICT *dict, const char *name)
{
const char *myname = "dict_mysql_lookup";
- DICT_MYSQL *dict_mysql = (DICT_MYSQL *)dict;
- PLMYSQL *pldb = dict_mysql->pldb;
+ DICT_MYSQL *dict_mysql = (DICT_MYSQL *) dict;
MYSQL_RES *query_res;
MYSQL_ROW row;
static VSTRING *result;
int expansion;
const char *r;
db_quote_callback_t quote_func = dict_mysql_quote;
+ int domain_rc;
- dict_errno = 0;
+ dict->error = 0;
/*
* Optionally fold the key.
}
/*
- * If there is a domain list for this map, then only search for
- * addresses in domains on the list. This can significantly reduce
- * the load on the server.
+ * If there is a domain list for this map, then only search for addresses
+ * in domains on the list. This can significantly reduce the load on the
+ * server.
*/
- if (db_common_check_domain(dict_mysql->ctx, name) == 0) {
- if (msg_verbose)
+ if ((domain_rc = db_common_check_domain(dict_mysql->ctx, name)) == 0) {
+ if (msg_verbose)
msg_info("%s: Skipping lookup of '%s'", myname, name);
- return (0);
+ return (0);
}
+ if (domain_rc < 0)
+ DICT_ERR_VAL_RETURN(dict, domain_rc, (char *) 0);
#define INIT_VSTR(buf, len) do { \
if (buf == 0) \
/*
* Suppress the lookup if the query expansion is empty
- *
- * This initial expansion is outside the context of any
- * specific host connection, we just want to check the
- * key pre-requisites, so when quoting happens separately
- * for each connection, we don't bother with quoting...
+ *
+ * This initial expansion is outside the context of any specific host
+ * connection, we just want to check the key pre-requisites, so when
+ * quoting happens separately for each connection, we don't bother with
+ * quoting...
*/
#if defined(MYSQL_VERSION_ID) && MYSQL_VERSION_ID >= 40000
quote_func = 0;
#endif
if (!db_common_expand(dict_mysql->ctx, dict_mysql->query,
- name, 0, query, quote_func))
- return (0);
-
- /* do the query - set dict_errno & cleanup if there's an error */
+ name, 0, query, quote_func))
+ return (0);
+
+ /* do the query - set dict->error & cleanup if there's an error */
if ((query_res = plmysql_query(dict_mysql, name, query,
dict_mysql->dbname,
dict_mysql->username,
dict_mysql->password)) == 0) {
- dict_errno = DICT_ERR_RETRY;
+ dict->error = DICT_ERR_RETRY;
return (0);
}
-
numrows = mysql_num_rows(query_res);
if (msg_verbose)
msg_info("%s: retrieved %d rows", myname, numrows);
mysql_free_result(query_res);
return 0;
}
-
INIT_VSTR(result, 10);
- for (expansion = i = 0; i < numrows && dict_errno == 0; i++) {
+ for (expansion = i = 0; i < numrows && dict->error == 0; i++) {
row = mysql_fetch_row(query_res);
for (j = 0; j < mysql_num_fields(query_res); j++) {
if (db_common_expand(dict_mysql->ctx, dict_mysql->result_format,
- row[j], name, result, 0)
+ row[j], name, result, 0)
&& dict_mysql->expansion_limit > 0
&& ++expansion > dict_mysql->expansion_limit) {
msg_warn("%s: %s: Expansion limit exceeded for key: '%s'",
myname, dict_mysql->parser->name, name);
- dict_errno = DICT_ERR_RETRY;
+ dict->error = DICT_ERR_RETRY;
break;
}
}
}
mysql_free_result(query_res);
r = vstring_str(result);
- return ((dict_errno == 0 && *r) ? r : 0);
+ return ((dict->error == 0 && *r) ? r : 0);
}
/* dict_mysql_check_stat - check the status of a host */
static int dict_mysql_check_stat(HOST *host, unsigned stat, unsigned type,
- time_t t)
+ time_t t)
{
if ((host->stat & stat) && (!type || host->type & type)) {
/* try not to hammer the dead hosts too often */
/* dict_mysql_get_active - get an active connection */
static HOST *dict_mysql_get_active(PLMYSQL *PLDB, char *dbname,
- char *username, char *password)
+ char *username, char *password)
{
const char *myname = "dict_mysql_get_active";
HOST *host;
}
/*
- * Try the remaining hosts.
- * "count" is a safety net, in case the loop takes more than
- * RETRY_CONN_INTV and the dead hosts are no longer skipped.
+ * Try the remaining hosts. "count" is a safety net, in case the loop
+ * takes more than RETRY_CONN_INTV and the dead hosts are no longer
+ * skipped.
*/
while (--count > 0 &&
((host = dict_mysql_find_host(PLDB, STATUNTRIED | STATFAIL,
TYPEUNIX)) != NULL ||
- (host = dict_mysql_find_host(PLDB, STATUNTRIED | STATFAIL,
- TYPEINET)) != NULL)) {
+ (host = dict_mysql_find_host(PLDB, STATUNTRIED | STATFAIL,
+ TYPEINET)) != NULL)) {
if (msg_verbose)
msg_info("%s: attempting to connect to host %s", myname,
host->hostname);
static MYSQL_RES *plmysql_query(DICT_MYSQL *dict_mysql,
const char *name,
- VSTRING *query,
+ VSTRING *query,
char *dbname,
char *username,
char *password)
while ((host = dict_mysql_get_active(PLDB, dbname, username, password)) != NULL) {
#if defined(MYSQL_VERSION_ID) && MYSQL_VERSION_ID >= 40000
+
/*
- * The active host is used to escape strings in the
- * context of the active connection's character encoding.
+ * The active host is used to escape strings in the context of the
+ * active connection's character encoding.
*/
dict_mysql->active_host = host;
VSTRING_RESET(query);
const char *myname = "mysqlname_parse";
CFG_PARSER *p;
VSTRING *buf;
- int i;
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);
dict_mysql->result_format = cfg_get_str(p, "result_format", "%s", 1, 0);
+
/*
* XXX: The default should be non-zero for safety, but that is not
* backwards compatible.
"expansion_limit", 0, 0, 0);
if ((dict_mysql->query = cfg_get_str(p, "query", NULL, 0, 0)) == 0) {
- /*
- * No query specified -- fallback to building it from components
- * (old style "select %s from %s where %s")
- */
+
+ /*
+ * No query specified -- fallback to building it from components (old
+ * style "select %s from %s where %s")
+ */
buf = vstring_alloc(64);
db_common_sql_build_query(buf, p);
dict_mysql->query = vstring_export(buf);
db_common_parse_domain(p, dict_mysql->ctx);
/*
- * Maps that use substring keys should only be used with the full
- * input key.
+ * Maps that use substring keys should only be used with the full input
+ * key.
*/
if (db_common_dict_partial(dict_mysql->ctx))
dict_mysql->dict.flags |= DICT_FLAG_PATTERN;
host->name = 0;
host->type = TYPEUNIX;
}
-
if (msg_verbose > 1)
msg_info("%s: host=%s, port=%d, type=%s", myname,
host->name ? host->name : "localhost",
static void dict_mysql_close(DICT *dict)
{
- int i;
DICT_MYSQL *dict_mysql = (DICT_MYSQL *) dict;
plmysql_dealloc(dict_mysql->pldb);
myfree(dict_mysql->query);
myfree(dict_mysql->result_format);
if (dict_mysql->hosts)
- argv_free(dict_mysql->hosts);
+ argv_free(dict_mysql->hosts);
if (dict_mysql->ctx)
db_common_free_ctx(dict_mysql->ctx);
if (dict->fold_buf)
/* the lookup result unchanged.
/* .IP expansion_limit
/* Limit (if any) on the total number of lookup result values. Lookups which
-/* exceed the limit fail with dict_errno=DICT_ERR_RETRY. Note that each
+/* exceed the limit fail with dict->error=DICT_ERR_RETRY. Note that each
/* non-empty (and non-NULL) column of a multi-column result row counts as
/* one result.
/* .IP select_function
#define TYPEINET (1<<1)
#define RETRY_CONN_MAX 100
-#define RETRY_CONN_INTV 60 /* 1 minute */
-#define IDLE_CONN_INTV 60 /* 1 minute */
+#define RETRY_CONN_INTV 60 /* 1 minute */
+#define IDLE_CONN_INTV 60 /* 1 minute */
typedef struct {
PGconn *db;
/* internal function declarations */
static PLPGSQL *plpgsql_init(ARGV *);
static PGSQL_RES *plpgsql_query(DICT_PGSQL *, const char *, VSTRING *, char *,
- char *, char *);
+ char *, char *);
static void plpgsql_dealloc(PLPGSQL *);
static void plpgsql_close_host(HOST *);
static void plpgsql_down_host(HOST *);
static void dict_pgsql_quote(DICT *dict, const char *name, VSTRING *result)
{
DICT_PGSQL *dict_pgsql = (DICT_PGSQL *) dict;
- HOST *active_host = dict_pgsql->active_host;
- char *myname = "dict_pgsql_quote";
- size_t len = strlen(name);
- size_t buflen = 2*len + 1;
- int err = 1;
+ HOST *active_host = dict_pgsql->active_host;
+ char *myname = "dict_pgsql_quote";
+ size_t len = strlen(name);
+ size_t buflen = 2 * len + 1;
+ int err = 1;
if (active_host == 0)
msg_panic("%s: bogus dict_pgsql->active_host", myname);
/*
- * We won't get arithmetic overflows in 2*len + 1, because Postfix
- * input keys have reasonable size limits, better safe than sorry.
+ * We won't get arithmetic overflows in 2*len + 1, because Postfix input
+ * keys have reasonable size limits, better safe than sorry.
*/
if (buflen <= len)
- msg_panic("%s: arithmetic overflow in 2*%lu+1",
+ msg_panic("%s: arithmetic overflow in 2*%lu+1",
myname, (unsigned long) len);
/*
return;
/*
- * Escape the input string, using PQescapeStringConn(), because
- * the older PQescapeString() is not safe anymore, as stated by the
- * documentation.
- *
+ * Escape the input string, using PQescapeStringConn(), because the older
+ * PQescapeString() is not safe anymore, as stated by the documentation.
+ *
* From current libpq (8.1.4) documentation:
- *
- * PQescapeStringConn writes an escaped version of the from string
- * to the to buffer, escaping special characters so that they cannot
- * cause any harm, and adding a terminating zero byte.
- *
+ *
+ * PQescapeStringConn writes an escaped version of the from string to the to
+ * buffer, escaping special characters so that they cannot cause any
+ * harm, and adding a terminating zero byte.
+ *
* ...
- *
- * The parameter from points to the first character of the string
- * that is to be escaped, and the length parameter gives the number
- * of bytes in this string. A terminating zero byte is not required,
- * and should not be counted in length.
- *
+ *
+ * The parameter from points to the first character of the string that is to
+ * be escaped, and the length parameter gives the number of bytes in this
+ * string. A terminating zero byte is not required, and should not be
+ * counted in length.
+ *
* ...
- *
- * (The parameter) to shall point to a buffer that is able to hold
- * at least one more byte than twice the value of length, otherwise
- * the behavior is undefined.
- *
+ *
+ * (The parameter) to shall point to a buffer that is able to hold at least
+ * one more byte than twice the value of length, otherwise the behavior
+ * is undefined.
+ *
* ...
- *
+ *
* If the error parameter is not NULL, then *error is set to zero on
- * success, nonzero on error ... The output string is still generated
- * on error, but it can be expected that the server will reject it as
- * malformed. On error, a suitable message is stored in the conn
- * object, whether or not error is NULL.
+ * success, nonzero on error ... The output string is still generated on
+ * error, but it can be expected that the server will reject it as
+ * malformed. On error, a suitable message is stored in the conn object,
+ * whether or not error is NULL.
*/
VSTRING_SPACE(result, buflen);
PQescapeStringConn(active_host->db, vstring_end(result), name, len, &err);
if (err == 0) {
VSTRING_SKIP(result);
} else {
- /*
- * PQescapeStringConn() failed. According to the docs, we still
- * have a valid, null-terminated output string, but we need not
- * rely on this behavior.
+
+ /*
+ * PQescapeStringConn() failed. According to the docs, we still have
+ * a valid, null-terminated output string, but we need not rely on
+ * this behavior.
*/
- msg_warn("dict pgsql: (host %s) cannot escape input string: %s",
+ msg_warn("dict pgsql: (host %s) cannot escape input string: %s",
active_host->hostname, PQerrorMessage(active_host->db));
active_host->stat = STATFAIL;
VSTRING_TERMINATE(result);
int numcols;
int expansion;
const char *r;
-
+ int domain_rc;
+
dict_pgsql = (DICT_PGSQL *) dict;
pldb = dict_pgsql->pldb;
INIT_VSTR(query, 10);
INIT_VSTR(result, 10);
- dict_errno = 0;
+ dict->error = 0;
/*
* Optionally fold the key.
}
/*
- * If there is a domain list for this map, then only search for
- * addresses in domains on the list. This can significantly reduce
- * the load on the server.
+ * If there is a domain list for this map, then only search for addresses
+ * in domains on the list. This can significantly reduce the load on the
+ * server.
*/
- if (db_common_check_domain(dict_pgsql->ctx, name) == 0) {
- if (msg_verbose)
+ if ((domain_rc = db_common_check_domain(dict_pgsql->ctx, name)) == 0) {
+ if (msg_verbose)
msg_info("%s: Skipping lookup of '%s'", myname, name);
- return (0);
+ return (0);
}
+ if (domain_rc < 0)
+ DICT_ERR_VAL_RETURN(dict, domain_rc, (char *) 0);
/*
* Suppress the actual lookup if the expansion is empty.
*
- * This initial expansion is outside the context of any
- * specific host connection, we just want to check the
- * key pre-requisites, so when quoting happens separately
- * for each connection, we don't bother with quoting...
+ * This initial expansion is outside the context of any specific host
+ * connection, we just want to check the key pre-requisites, so when
+ * quoting happens separately for each connection, we don't bother with
+ * quoting...
*/
if (!db_common_expand(dict_pgsql->ctx, dict_pgsql->query,
name, 0, query, 0))
return (0);
-
- /* do the query - set dict_errno & cleanup if there's an error */
+
+ /* do the query - set dict->error & cleanup if there's an error */
if ((query_res = plpgsql_query(dict_pgsql, name, query,
dict_pgsql->dbname,
dict_pgsql->username,
dict_pgsql->password)) == 0) {
- dict_errno = DICT_ERR_RETRY;
+ dict->error = DICT_ERR_RETRY;
return 0;
}
-
numrows = PQntuples(query_res);
if (msg_verbose)
msg_info("%s: retrieved %d rows", myname, numrows);
}
numcols = PQnfields(query_res);
- for (expansion = i = 0; i < numrows && dict_errno == 0; i++) {
+ for (expansion = i = 0; i < numrows && dict->error == 0; i++) {
for (j = 0; j < numcols; j++) {
r = PQgetvalue(query_res, i, j);
if (db_common_expand(dict_pgsql->ctx, dict_pgsql->result_format,
- r, name, result, 0)
+ r, name, result, 0)
&& dict_pgsql->expansion_limit > 0
&& ++expansion > dict_pgsql->expansion_limit) {
msg_warn("%s: %s: Expansion limit exceeded for key: '%s'",
myname, dict_pgsql->parser->name, name);
- dict_errno = DICT_ERR_RETRY;
+ dict->error = DICT_ERR_RETRY;
break;
}
}
}
PQclear(query_res);
r = vstring_str(result);
- return ((dict_errno == 0 && *r) ? r : 0);
+ return ((dict->error == 0 && *r) ? r : 0);
}
/* dict_pgsql_check_stat - check the status of a host */
static int dict_pgsql_check_stat(HOST *host, unsigned stat, unsigned type,
- time_t t)
+ time_t t)
{
if ((host->stat & stat) && (!type || host->type & type)) {
/* try not to hammer the dead hosts too often */
/* dict_pgsql_get_active - get an active connection */
static HOST *dict_pgsql_get_active(PLPGSQL *PLDB, char *dbname,
- char *username, char *password)
+ char *username, char *password)
{
const char *myname = "dict_pgsql_get_active";
HOST *host;
}
/*
- * Try the remaining hosts.
- * "count" is a safety net, in case the loop takes more than
- * RETRY_CONN_INTV and the dead hosts are no longer skipped.
+ * Try the remaining hosts. "count" is a safety net, in case the loop
+ * takes more than RETRY_CONN_INTV and the dead hosts are no longer
+ * skipped.
*/
while (--count > 0 &&
((host = dict_pgsql_find_host(PLDB, STATUNTRIED | STATFAIL,
- TYPEUNIX)) != NULL ||
- (host = dict_pgsql_find_host(PLDB, STATUNTRIED | STATFAIL,
- TYPEINET)) != NULL)) {
+ TYPEUNIX)) != NULL ||
+ (host = dict_pgsql_find_host(PLDB, STATUNTRIED | STATFAIL,
+ TYPEINET)) != NULL)) {
if (msg_verbose)
msg_info("%s: attempting to connect to host %s", myname,
host->hostname);
ExecStatusType status;
while ((host = dict_pgsql_get_active(PLDB, dbname, username, password)) != NULL) {
+
/*
- * The active host is used to escape strings in the
- * context of the active connection's character encoding.
+ * The active host is used to escape strings in the context of the
+ * active connection's character encoding.
*/
dict_pgsql->active_host = host;
VSTRING_RESET(query);
continue;
}
- /*
- * Submit a command to the server. Be paranoid when processing
- * the result set: try to enumerate every successful case, and
- * reject everything else.
- *
- * From PostgreSQL 8.1.4 docs: (PQexec) returns a PGresult
- * pointer or possibly a null pointer. A non-null pointer will
- * generally be returned except in out-of-memory conditions or
- * serious errors such as inability to send the command to the
- * server.
+ /*
+ * Submit a command to the server. Be paranoid when processing the
+ * result set: try to enumerate every successful case, and reject
+ * everything else.
+ *
+ * From PostgreSQL 8.1.4 docs: (PQexec) returns a PGresult pointer or
+ * possibly a null pointer. A non-null pointer will generally be
+ * returned except in out-of-memory conditions or serious errors such
+ * as inability to send the command to the server.
*/
if ((res = PQexec(host->db, vstring_str(query))) != 0) {
+
/*
- * XXX Because non-null result pointer does not imply success,
- * we need to check the command's result status.
- *
- * Section 28.3.1: A result of status PGRES_NONFATAL_ERROR
- * will never be returned directly by PQexec or other query
- * execution functions; results of this kind are instead
- * passed to the notice processor.
- *
- * PGRES_EMPTY_QUERY is being sent by the server when the
- * query string is empty. The sanity-checking done by
- * the Postfix infrastructure makes this case impossible,
- * so we need not handle this situation explicitly.
+ * XXX Because non-null result pointer does not imply success, we
+ * need to check the command's result status.
+ *
+ * Section 28.3.1: A result of status PGRES_NONFATAL_ERROR will
+ * never be returned directly by PQexec or other query execution
+ * functions; results of this kind are instead passed to the
+ * notice processor.
+ *
+ * PGRES_EMPTY_QUERY is being sent by the server when the query
+ * string is empty. The sanity-checking done by the Postfix
+ * infrastructure makes this case impossible, so we need not
+ * handle this situation explicitly.
*/
switch ((status = PQresultStatus(res))) {
case PGRES_TUPLES_OK:
case PGRES_COMMAND_OK:
/* Success. */
if (msg_verbose)
- msg_info("dict_pgsql: successful query from host %s",
+ msg_info("dict_pgsql: successful query from host %s",
host->hostname);
- event_request_timer(dict_pgsql_event, (char *) host,
+ event_request_timer(dict_pgsql_event, (char *) host,
IDLE_CONN_INTV);
return (res);
case PGRES_FATAL_ERROR:
break;
}
} else {
- /*
- * This driver treats null pointers like fatal, non-null
- * result pointer errors, as suggested by the PostgreSQL
- * 8.1.4 documentation.
+
+ /*
+ * This driver treats null pointers like fatal, non-null result
+ * pointer errors, as suggested by the PostgreSQL 8.1.4
+ * documentation.
*/
msg_warn("pgsql query failed: fatal error from host %s: %s",
host->hostname, PQerrorMessage(host->db));
}
- /*
+ /*
* XXX An error occurred. Clean up memory and skip this connection.
*/
if (res != 0)
plpgsql_down_host(host);
return;
}
-
if (msg_verbose)
msg_info("dict_pgsql: successful connection to host %s",
host->hostname);
/*
- * XXX Postfix does not send multi-byte characters. The following
- * piece of code is an explicit statement of this fact, and the
- * database server should not accept multi-byte information after
- * this point.
+ * XXX Postfix does not send multi-byte characters. The following piece
+ * of code is an explicit statement of this fact, and the database server
+ * should not accept multi-byte information after this point.
*/
if (PQsetClientEncoding(host->db, "LATIN1") != 0) {
msg_warn("dict_pgsql: cannot set the encoding to LATIN1, skipping %s",
plpgsql_down_host(host);
return;
}
-
/* Success. */
host->stat = STATACTIVE;
}
dict_pgsql->password = cfg_get_str(p, "password", "", 0, 0);
dict_pgsql->dbname = cfg_get_str(p, "dbname", "", 1, 0);
dict_pgsql->result_format = cfg_get_str(p, "result_format", "%s", 1, 0);
+
/*
* XXX: The default should be non-zero for safety, but that is not
* backwards compatible.
"expansion_limit", 0, 0, 0);
if ((dict_pgsql->query = cfg_get_str(p, "query", 0, 0, 0)) == 0) {
- /*
- * No query specified -- fallback to building it from components
- * ( old style "select %s from %s where %s" )
- */
+
+ /*
+ * No query specified -- fallback to building it from components (
+ * old style "select %s from %s where %s" )
+ */
query = vstring_alloc(64);
select_function = cfg_get_str(p, "select_function", 0, 0, 0);
if (select_function != 0) {
vstring_sprintf(query, "SELECT %s('%%s')", select_function);
myfree(select_function);
} else
- db_common_sql_build_query(query, p);
+ db_common_sql_build_query(query, p);
dict_pgsql->query = vstring_export(query);
}
db_common_parse_domain(p, dict_pgsql->ctx);
/*
- * Maps that use substring keys should only be used with the full
- * input key.
+ * Maps that use substring keys should only be used with the full input
+ * key.
*/
if (db_common_dict_partial(dict_pgsql->ctx))
dict_pgsql->dict.flags |= DICT_FLAG_PATTERN;
if (dict_pgsql->pldb == NULL)
msg_fatal("couldn't intialize pldb!\n");
dict_pgsql->dict.owner = cfg_get_owner(dict_pgsql->parser);
- return (DICT_DEBUG(&dict_pgsql->dict));
+ return (DICT_DEBUG (&dict_pgsql->dict));
}
/* plpgsql_init - initalize a PGSQL database */
myfree(dict_pgsql->query);
myfree(dict_pgsql->result_format);
if (dict_pgsql->hosts)
- argv_free(dict_pgsql->hosts);
+ argv_free(dict_pgsql->hosts);
if (dict_pgsql->ctx)
db_common_free_ctx(dict_pgsql->ctx);
if (dict->fold_buf)
case PROXY_STAT_OK:
*key = STR(dict_proxy->reskey);
*value = STR(dict_proxy->result);
- return (0);
+ DICT_ERR_VAL_RETURN(dict, DICT_ERR_NONE, DICT_STAT_SUCCESS);
case PROXY_STAT_NOKEY:
- dict_errno = 0;
*key = *value = 0;
- return (1);
+ DICT_ERR_VAL_RETURN(dict, DICT_ERR_NONE, DICT_STAT_FAIL);
case PROXY_STAT_RETRY:
- dict_errno = DICT_ERR_RETRY;
*key = *value = 0;
- return (1);
+ DICT_ERR_VAL_RETURN(dict, DICT_ERR_RETRY, DICT_STAT_ERROR);
default:
msg_warn("%s sequence failed for table \"%s\" function %d: "
"unexpected reply status %d",
msg_fatal("%s service is not configured for table \"%s\"",
dict_proxy->service, dict->name);
case PROXY_STAT_OK:
- return (STR(dict_proxy->result));
+ DICT_ERR_VAL_RETURN(dict, DICT_ERR_NONE, STR(dict_proxy->result));
case PROXY_STAT_NOKEY:
- dict_errno = 0;
- return (0);
+ DICT_ERR_VAL_RETURN(dict, DICT_ERR_NONE, (char *) 0);
case PROXY_STAT_RETRY:
- dict_errno = DICT_ERR_RETRY;
- return (0);
+ DICT_ERR_VAL_RETURN(dict, DICT_ERR_RETRY, (char *) 0);
default:
msg_warn("%s lookup failed for table \"%s\" key \"%s\": "
"unexpected reply status %d",
/* dict_proxy_update - update table entry */
-static void dict_proxy_update(DICT *dict, const char *key, const char *value)
+static int dict_proxy_update(DICT *dict, const char *key, const char *value)
{
const char *myname = "dict_proxy_update";
DICT_PROXY *dict_proxy = (DICT_PROXY *) dict;
msg_fatal("%s update access is not configured for table \"%s\"",
dict_proxy->service, dict->name);
case PROXY_STAT_OK:
- dict_errno = 0;
- return;
+ DICT_ERR_VAL_RETURN(dict, DICT_ERR_NONE, DICT_STAT_SUCCESS);
+ case PROXY_STAT_NOKEY:
+ DICT_ERR_VAL_RETURN(dict, DICT_ERR_NONE, DICT_STAT_FAIL);
case PROXY_STAT_RETRY:
- dict_errno = DICT_ERR_RETRY;
- return;
+ DICT_ERR_VAL_RETURN(dict, DICT_ERR_RETRY, DICT_STAT_ERROR);
default:
msg_warn("%s update failed for table \"%s\" key \"%s\": "
"unexpected reply status %d",
msg_fatal("%s update access is not configured for table \"%s\"",
dict_proxy->service, dict->name);
case PROXY_STAT_OK:
- return 0;
- case PROXY_STAT_RETRY:
- dict_errno = DICT_ERR_RETRY;
- return (-1);
+ DICT_ERR_VAL_RETURN(dict, DICT_ERR_NONE, DICT_STAT_SUCCESS);
case PROXY_STAT_NOKEY:
- dict_errno = 0;
- return (1);
+ DICT_ERR_VAL_RETURN(dict, DICT_ERR_NONE, DICT_STAT_FAIL);
+ case PROXY_STAT_RETRY:
+ DICT_ERR_VAL_RETURN(dict, DICT_ERR_RETRY, DICT_STAT_ERROR);
default:
msg_warn("%s delete failed for table \"%s\" key \"%s\": "
"unexpected reply status %d",
/* returning the lookup result unchanged.
/* .IP expansion_limit
/* Limit (if any) on the total number of lookup result values.
-/* Lookups which exceed the limit fail with dict_errno=DICT_ERR_RETRY.
+/* Lookups which exceed the limit fail with dict->error=DICT_ERR_RETRY.
/* Note that each non-empty (and non-NULL) column of a
/* multi-column result row counts as one result.
/* .IP "select_field, where_field, additional_conditions"
const char *retval;
int expansion = 0;
int status;
+ int domain_rc;
/*
* In case of return without lookup (skipped key, etc.).
*/
- dict_errno = 0;
+ dict->error = 0;
/*
* Don't frustrate future attempts to make Postfix UTF-8 transparent.
/*
* Apply the optional domain filter for email address lookups.
*/
- if (db_common_check_domain(dict_sqlite->ctx, name) == 0) {
+ if ((domain_rc = db_common_check_domain(dict_sqlite->ctx, name)) == 0) {
if (msg_verbose)
msg_info("%s: %s: Skipping lookup of '%s'",
myname, dict_sqlite->parser->name, name);
return (0);
}
+ if (domain_rc < 0)
+ DICT_ERR_VAL_RETURN(dict, domain_rc, (char *) 0);
/*
* Expand the query and query the database.
&& ++expansion > dict_sqlite->expansion_limit) {
msg_warn("%s: %s: Expansion limit exceeded for key '%s'",
myname, dict_sqlite->parser->name, name);
- dict_errno = DICT_ERR_RETRY;
+ dict->error = DICT_ERR_RETRY;
break;
}
}
msg_warn("%s: %s: SQL step failed for query '%s': %s\n",
myname, dict_sqlite->parser->name,
vstring_str(query), sqlite3_errmsg(dict_sqlite->db));
- dict_errno = DICT_ERR_RETRY;
+ dict->error = DICT_ERR_RETRY;
break;
}
}
myname, dict_sqlite->parser->name,
vstring_str(query), sqlite3_errmsg(dict_sqlite->db));
- return ((dict_errno == 0 && *(retval = vstring_str(result)) != 0) ?
+ return ((dict->error == 0 && *(retval = vstring_str(result)) != 0) ?
retval : 0);
}
/* is the bit-wise OR of zero or more of the following:
/* .IP MATCH_FLAG_PARENT
/* The hostname pattern foo.com matches itself and any name below
-/* the domain foo.com. If this flag is cleared, foo.com matches itself
+/* the domain foo.com. If this flag is cleared, foo.com matches itself
/* only, and .foo.com matches any name below the domain foo.com.
/* .IP MATCH_FLAG_RETURN
-/* Request that domain_list_match() returns zero with
-/* dict_errno != 0, instead of raising a fatal error.
+/* Request that domain_list_match() logs a warning and returns
+/* zero, with list->error set to a non-zero dictionary error
+/* code, instead of raising a fatal error.
/* .PP
/* Specify MATCH_FLAG_NONE to request none of the above.
/* The second argument is a list of domain patterns, or the name of
list = domain_list_init(MATCH_FLAG_PARENT | MATCH_FLAG_RETURN, argv[optind]);
host = argv[optind + 1];
vstream_printf("%s: %s\n", host, domain_list_match(list, host) ?
- "YES" : dict_errno == 0 ? "NO" : "ERROR");
+ "YES" : list->error == 0 ? "NO" : "ERROR");
vstream_fflush(VSTREAM_OUT);
domain_list_free(list);
return (0);
ATTR_TYPE_STR, MAIL_ATTR_REQ, FLUSH_REQ_SEND_SITE,
ATTR_TYPE_STR, MAIL_ATTR_SITE, site,
ATTR_TYPE_END);
- else if (dict_errno == 0)
+ else if (flush_domains->error == 0)
status = FLUSH_STAT_DENY;
else
status = FLUSH_STAT_FAIL;
ATTR_TYPE_STR, MAIL_ATTR_SITE, site,
ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, queue_id,
ATTR_TYPE_END);
- else if (dict_errno == 0)
+ else if (flush_domains->error == 0)
status = FLUSH_STAT_DENY;
else
status = FLUSH_STAT_FAIL;
/* The copy includes the recipient address delimiter.
/* The caller is expected to pass the copy to myfree().
/* DIAGNOSTICS
-/* The global \fIdict_errno\fR is non-zero when the lookup
+/* The maps->error value is non-zero when the lookup
/* should be tried again.
/* SEE ALSO
/* maps(3), multi-dictionary search
char *full_key;
char *bare_key;
char *saved_ext;
+ int rc = 0;
/*
* Initialize.
#define FULL 0
#define PARTIAL DICT_FLAG_FIXED
- if ((result = maps_find(path, full_key, FULL)) == 0 && dict_errno == 0
+ if ((result = maps_find(path, full_key, FULL)) == 0 && path->error == 0
&& bare_key != 0 && (result = maps_find(path, bare_key, PARTIAL)) != 0
&& extp != 0) {
*extp = saved_ext;
* Try user+foo@$myorigin, user+foo@$mydestination or
* user+foo@[${proxy,inet}_interfaces]. Then try with +foo stripped off.
*/
- if (result == 0 && dict_errno == 0
+ if (result == 0 && path->error == 0
&& (ratsign = strrchr(full_key, '@')) != 0
&& (strcasecmp(ratsign + 1, var_myorigin) == 0
- || resolve_local(ratsign + 1))) {
+ || (rc = resolve_local(ratsign + 1)) > 0)) {
*ratsign = 0;
result = maps_find(path, full_key, PARTIAL);
- if (result == 0 && dict_errno == 0 && bare_key != 0) {
+ if (result == 0 && path->error == 0 && bare_key != 0) {
if ((ratsign = strrchr(bare_key, '@')) == 0)
msg_panic("%s: bare key botch", myname);
*ratsign = 0;
}
}
*ratsign = '@';
- }
+ } else if (rc < 0)
+ path->error = rc;
/*
* Try @domain.
*/
- if (result == 0 && dict_errno == 0 && ratsign)
+ if (result == 0 && path->error == 0 && ratsign)
result = maps_find(path, ratsign, PARTIAL);
/*
if (msg_verbose)
msg_info("%s: %s -> %s", myname, address,
result ? result :
- dict_errno ? "(try again)" :
+ path->error ? "(try again)" :
"(not found)");
myfree(full_key);
if (bare_key)
extent = 0;
result = mail_addr_find(path, STR(buffer), &extent);
vstream_printf("%s -> %s (%s)\n", STR(buffer), result ? result :
- dict_errno ? "(try again)" :
+ path->error ? "(try again)" :
"(not found)", extent ? extent : "null extension");
vstream_fflush(VSTREAM_OUT);
if (extent)
/* DIAGNOSTICS
/* Warnings: map lookup returns a non-address result.
/*
-/* The global \fIdict_errno\fR is non-zero when the lookup
+/* The path->error value is non-zero when the lookup
/* should be tried again.
/* SEE ALSO
/* mail_addr_find(3), mail address matching
msg_warn("%s lookup of %s returns non-address result \"%s\"",
path->title, address, string);
argv = argv_free(argv);
- dict_errno = DICT_ERR_RETRY;
+ path->error = DICT_ERR_RETRY;
}
}
else {
if (msg_verbose)
msg_info("%s: %s -> %s", myname, address,
- dict_errno ? "(try again)" : "(not found)");
+ path->error ? "(try again)" : "(not found)");
}
/*
* other kinds of trouble. Enter the configuration directory into the
* default dictionary.
*/
- dict_unknown_allowed = 1;
if (var_config_dir)
myfree(var_config_dir);
if ((config_dir = getenv(CONF_ENV_PATH)) == 0)
* Patches change both the patchlevel and the release date. Snapshots have no
* patchlevel; they change the release date only.
*/
-#define MAIL_RELEASE_DATE "20120102"
+#define MAIL_RELEASE_DATE "20120108"
#define MAIL_VERSION_NUMBER "2.9"
#ifdef SNAPSHOT
/* to open database. Warnings: null string lookup result.
/*
/* maps_find() returns a null pointer when the requested
-/* information was not found. The global \fIdict_errno\fR
-/* variable indicates if the last lookup failed due to a problem.
+/* information was not found, and logs a warning when the
+/* lookup failed due to error. The maps->error value indicates
+/* if the last lookup failed due to error.
/* BUGS
/* The dictionary name space is flat, so dictionary names allocated
/* by maps_create() may collide with dictionary names allocated by
/*
* In case of return without map lookup (empty name or no maps).
*/
- dict_errno = 0;
+ maps->error = 0;
/*
* Temp. workaround, for buggy callers that pass zero-length keys when
maps->title, name);
msg_warn("%s should return NO RESULT in case of NOT FOUND",
maps->title);
- dict_errno = DICT_ERR_RETRY;
+ maps->error = DICT_ERR_RETRY;
return (0);
}
if (msg_verbose)
msg_info("%s: %s: %s: %s = %s", myname, maps->title,
*map_name, name, expansion);
return (expansion);
- } else if (dict_errno != 0) {
+ } else if ((maps->error = dict->error) != 0) {
msg_warn("%s:%s lookup of %s failed", dict->type, dict->name, name);
break;
}
}
if (msg_verbose)
- msg_info("%s: %s: %s: %s", myname, maps->title, name, dict_errno ?
+ msg_info("%s: %s: %s: %s", myname, maps->title, name, maps->error ?
"search aborted" : "not found");
return (0);
}
maps = maps_create("whatever", argv[1], DICT_FLAG_LOCK);
while (vstring_fgets_nonl(buf, VSTREAM_IN)) {
- dict_errno = 99;
+ maps->error = 99;
vstream_printf("\"%s\": ", vstring_str(buf));
if ((result = maps_find(maps, vstring_str(buf), 0)) != 0) {
vstream_printf("%s\n", result);
- } else if (dict_errno != 0) {
+ } else if (maps->error != 0) {
vstream_printf("lookup error\n");
} else {
vstream_printf("not found\n");
typedef struct MAPS {
char *title;
struct ARGV *argv;
+ int error; /* last request only */
} MAPS;
extern MAPS *maps_create(const char *, const char *, int);
/*
* Utility library.
*/
-#include <match_ops.h>
+#include <match_list.h>
/*
* External interface.
extern MKMAP *mkmap_btree_open(const char *);
extern MKMAP *mkmap_sdbm_open(const char *);
extern MKMAP *mkmap_proxy_open(const char *);
+extern MKMAP *mkmap_fail_open(const char *);
/* LICENSE
/* .ad
--- /dev/null
+/*++
+/* NAME
+/* mkmap_fail 3
+/* SUMMARY
+/* create or open database, fail: style
+/* SYNOPSIS
+/* #include <mkmap.h>
+/*
+/* MKMAP *mkmap_fail_open(path)
+/* const char *path;
+/*
+/* DESCRIPTION
+/* This module implements support for error testing postmap
+/* and postalias with the fail: table type.
+/* SEE ALSO
+/* dict_fail(3), fail dictionary interface.
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+
+/* Utility library. */
+
+#include <mymalloc.h>
+#include <dict.h>
+
+/* Application-specific. */
+
+#include <mkmap.h>
+#include <dict_fail.h>
+
+ /*
+ * Dummy module: the dict_fail module has all the functionality built-in.
+ */
+MKMAP *mkmap_fail_open(const char *unused_path)
+{
+ MKMAP *mkmap = (MKMAP *) mymalloc(sizeof(*mkmap));
+
+ mkmap->open = dict_fail_open;
+ mkmap->after_open = 0;
+ mkmap->after_close = 0;
+ return (mkmap);
+}
#include <dict_dbm.h>
#include <dict_sdbm.h>
#include <dict_proxy.h>
+#include <dict_fail.h>
#include <sigdelay.h>
#include <mymalloc.h>
DICT_TYPE_HASH, mkmap_hash_open,
DICT_TYPE_BTREE, mkmap_btree_open,
#endif
+ DICT_TYPE_FAIL, mkmap_fail_open,
0,
};
void mkmap_append(MKMAP *mkmap, const char *key, const char *value)
{
- dict_put(mkmap->dict, key, value);
+ DICT *dict = mkmap->dict;
+
+ if (dict_put(dict, key, value) != 0 && dict->error != 0)
+ msg_fatal("%s:%s: update failed", dict->type, dict->name);
}
/* mkmap_close - close database */
/* the domain foo.com. If this flag is cleared, foo.com matches itself
/* only, and .foo.com matches any name below the domain foo.com.
/* .IP MATCH_FLAG_RETURN
-/* Request that namadr_list_match() returns zero with
-/* dict_errno != 0, instead of raising a fatal error.
+/* Request that namadr_list_match() logs a warning and returns
+/* zero with list->error set to a non-zero dictionary error
+/* code, instead of raising a fatal error.
/* .PP
/* Specify MATCH_FLAG_NONE to request none of the above.
/* The second argument is a list of patterns, or the absolute
addr = argv[optind + 2];
vstream_printf("%s/%s: %s\n", host, addr,
namadr_list_match(list, host, addr) ?
- "YES" : dict_errno == 0 ? "NO" : "ERROR");
+ "YES" : list->error == 0 ? "NO" : "ERROR");
vstream_fflush(VSTREAM_OUT);
namadr_list_free(list);
return (0);
/* against the domains, files or tables listed in $mydestination,
/* or by a match of an [address-literal] against of the network
/* addresses listed in $inet_interfaces or in $proxy_interfaces.
-/* The result is non-zero if the domain matches the list of local
-/* domains and IP addresses, 0 when it does not match or in case
-/* of error (in the latter case dict_errno is non-zero).
+/* The result is > 0 if the domain matches the list of local
+/* domains and IP addresses, 0 when it does not match, and < 0
+/* in case of error.
/*
/* resolve_local_init() performs initialization. If this routine is
/* not called explicitly ahead of time, it will be called on the fly.
if (resolve_local_list == 0)
resolve_local_init();
- /*
- * In case of return without table lookup (empty address, malformed
- * address, empty destination list)
- */
- dict_errno = 0;
-
/*
* Strip one trailing dot but not dot-dot.
*
*/
if (string_list_match(resolve_local_list, saved_addr))
RETURN(1);
- if (dict_errno != 0)
- RETURN(0);
+ if (resolve_local_list->error != 0)
+ RETURN(resolve_local_list->error);
/*
* Compare the destination against the list of interface addresses that
int main(int argc, char **argv)
{
+ int rc;
+
if (argc != 3)
msg_fatal("usage: %s mydestination domain", argv[0]);
mail_conf_read();
myfree(var_mydest);
var_mydest = mystrdup(argv[1]);
- dict_errno = 99;
- vstream_printf("mydestination=%s destination=%s %s\n",
- argv[1], argv[2], resolve_local(argv[2]) ? "YES" :
- dict_errno == 0 ? "NO" : "ERROR");
+ vstream_printf("mydestination=%s destination=%s %s\n", argv[1], argv[2],
+ (rc = resolve_local(argv[2])) > 0 ? "YES" :
+ rc == 0 ? "NO" : "ERROR");
vstream_fflush(VSTREAM_OUT);
return (0);
}
} else if (STREQ(acl, SERVER_ACL_NAME_WL_MYNETWORKS)) {
if (addr_match_list_match(server_acl_mynetworks, client_addr))
return (SERVER_ACL_ACT_PERMIT);
- if (dict_errno != 0) {
+ if (server_acl_mynetworks->error != 0) {
msg_warn("%s: %s: mynetworks lookup error -- ignoring the "
"remainder of this access list", origin, acl);
return (SERVER_ACL_ACT_ERROR);
}
if (ret != SERVER_ACL_ACT_DUNNO)
return (ret);
- } else if (dict_errno != 0) {
+ } else if (dict->error != 0) {
msg_warn("%s: %s: table lookup error -- ignoring the remainder "
"of this access list", origin, acl);
return (SERVER_ACL_ACT_ERROR);
/* string_list_init() performs initializations. The first argument
/* is a bit-wise OR of zero or more of following:
/* .IP MATCH_FLAG_RETURN
-/* Request that string_list_match() returns zero with
-/* dict_errno != 0, instead of raising a fatal error.
+/* Request that string_list_match() logs a warning and returns
+/* zero with list->error set to a non-zero dictionary error
+/* code, instead of raising a fatal error.
/* .PP
/* Specify MATCH_FLAG_NONE to request none of the above.
/* The second argument specifies a list of string patterns.
}
if (argc != optind + 2)
usage(argv[0]);
- list = string_list_init(MATCH_FLAG_NONE | MATCH_FLAG_RETURN, argv[optind]);
+ list = string_list_init(MATCH_FLAG_RETURN, argv[optind]);
string = argv[optind + 1];
vstream_printf("%s: %s\n", string, string_list_match(list, string) ?
- "YES" : dict_errno == 0 ? "NO" : "ERROR");
+ "YES" : list->error == 0 ? "NO" : "ERROR");
vstream_fflush(VSTREAM_OUT);
string_list_free(list);
return (0);
* Deliver.
*/
alias_count = 0;
- if (dict_errno != 0) {
+ if (owner != 0 && alias_maps->error != 0) {
dsb_simple(state.msg_attr.why, "4.3.0",
"alias database unavailable");
*statusp = defer_append(BOUNCE_FLAGS(state.request),
* If the alias database was inaccessible for some reason, defer
* further delivery for the current top-level recipient.
*/
- if (dict_errno != 0) {
+ if (alias_result == 0 && dict->error != 0) {
+ msg_warn("%s:%s: lookup of '%s' failed",
+ dict->type, dict->name, name);
dsb_simple(state.msg_attr.why, "4.3.0",
"alias database unavailable");
*statusp = defer_append(BOUNCE_FLAGS(state.request),
char *stripped_recipient;
char *owner_alias;
const char *owner_expansion;
- int saved_dict_errno;
#define FIND_OWNER(lhs, rhs, addr) { \
lhs = concatenate("owner-", addr, (char *) 0); \
rhs = maps_find(alias_maps, lhs, DICT_FLAG_NONE); \
}
- dict_errno = 0;
FIND_OWNER(owner_alias, owner_expansion, state.msg_attr.rcpt.address);
- if ((saved_dict_errno = dict_errno) == 0 && owner_expansion == 0
+ if (alias_maps->error == 0 && owner_expansion == 0
&& (stripped_recipient = strip_addr(state.msg_attr.rcpt.address,
(char **) 0,
*var_rcpt_delim)) != 0) {
FIND_OWNER(owner_alias, owner_expansion, stripped_recipient);
myfree(stripped_recipient);
}
- if ((saved_dict_errno = dict_errno) == 0 && owner_expansion != 0) {
+ if (alias_maps->error == 0 && owner_expansion != 0) {
canon_owner = canon_addr_internal(vstring_alloc(10),
var_exp_own_alias ?
owner_expansion : owner_alias);
SET_OWNER_ATTR(state.msg_attr, STR(canon_owner), state.level);
}
myfree(owner_alias);
- if (saved_dict_errno != 0)
+ if (alias_maps->error != 0)
/* At this point, canon_owner == 0. */
return (defer_append(BOUNCE_FLAGS(state.request),
BOUNCE_ATTR(state.msg_attr)));
transp_maps = maps_create(VAR_MBOX_TRANSP_MAPS, var_mbox_transp_maps,
DICT_FLAG_LOCK | DICT_FLAG_NO_REGSUB);
/* The -1 is a hint for the down-stream deliver_completed() function. */
- dict_errno = 0;
- if (*var_mbox_transp_maps
+ if (transp_maps
&& (map_transport = maps_find(transp_maps, state.msg_attr.user,
DICT_FLAG_NONE)) != 0) {
state.msg_attr.rcpt.offset = -1L;
*statusp = deliver_pass(MAIL_CLASS_PRIVATE, map_transport,
state.request, &state.msg_attr.rcpt);
return (YES);
- } else if (dict_errno != 0) {
+ } else if (transp_maps && transp_maps->error != 0) {
/* Details in the logfile. */
dsb_simple(state.msg_attr.why, "4.3.0", "table lookup failure");
*statusp = defer_append(BOUNCE_FLAGS(state.request),
cmd_maps = maps_create(VAR_MAILBOX_CMD_MAPS, var_mailbox_cmd_maps,
DICT_FLAG_LOCK | DICT_FLAG_PARANOID);
- dict_errno = 0;
- if (*var_mailbox_cmd_maps
- && (map_command = maps_find(cmd_maps, state.msg_attr.user,
+ if (cmd_maps && (map_command = maps_find(cmd_maps, state.msg_attr.user,
DICT_FLAG_NONE)) != 0) {
status = deliver_command(state, usr_attr, map_command);
- } else if (dict_errno != 0) {
+ } else if (cmd_maps && cmd_maps->error != 0) {
/* Details in the logfile. */
dsb_simple(state.msg_attr.why, "4.3.0", "table lookup failure");
status = defer_append(BOUNCE_FLAGS(state.request),
transp_maps = maps_create(VAR_FBCK_TRANSP_MAPS, var_fbck_transp_maps,
DICT_FLAG_LOCK | DICT_FLAG_NO_REGSUB);
/* The -1 is a hint for the down-stream deliver_completed() function. */
- dict_errno = 0;
- if (*var_fbck_transp_maps
+ if (transp_maps
&& (map_transport = maps_find(transp_maps, state.msg_attr.user,
DICT_FLAG_NONE)) != 0) {
state.msg_attr.rcpt.offset = -1L;
return (deliver_pass(MAIL_CLASS_PRIVATE, map_transport,
state.request, &state.msg_attr.rcpt));
- } else if (dict_errno != 0) {
+ } else if (transp_maps && transp_maps->error != 0) {
/* Details in the logfile. */
dsb_simple(state.msg_attr.why, "4.3.0", "table lookup failure");
return (defer_append(BOUNCE_FLAGS(state.request),
update: ../../bin/$(PROG)
-tests: test1 test2
+tests: test1 test2 fail_test
root_tests:
done
rm -f map.in.db
+fail_test: $(PROG) aliases fail_test.in fail_test.ref
+ -sh fail_test.in > fail_test.tmp 2>&1 || exit 0
+ diff fail_test.ref fail_test.tmp
+ rm -f fail_test.tmp
+
../../bin/$(PROG): $(PROG)
cp $(PROG) ../../bin
--- /dev/null
+./postalias -q xx fail:aliases
+echo xx | ./postalias -q - fail:aliases
+./postalias -d xx fail:aliases
+echo xx | ./postalias -d - fail:aliases
+./postalias -s fail:aliases
+./postalias -i fail:aliases < aliases
+./postalias fail:aliases
--- /dev/null
+postalias: fatal: fail:aliases: query error: Unknown error: 0
+postalias: fatal: fail:aliases: query error: Unknown error: 0
+postalias: fatal: fail:aliases: delete error: Unknown error: 0
+postalias: fatal: fail:aliases: delete error: Unknown error: 0
+postalias: fatal: fail:aliases: sequence error: Unknown error: 0
+postalias: fatal: fail:aliases: write error: Unknown error: 0
+postalias: fatal: fail:aliases: write error: Unknown error: 0
/* .IP \fBhash\fR
/* The output is a hashed file, named \fIfile_name\fB.db\fR.
/* This is available on systems with support for \fBdb\fR databases.
+/* .IP \fBfail\fR
+/* A table that reliably fails all requests. The lookup table
+/* name is used for logging only. This table exists to simplify
+/* Postfix error tests.
/* .IP \fBsdbm\fR
/* The output consists of two files, named \fIfile_name\fB.pag\fR and
/* \fIfile_name\fB.dir\fR.
* Store the value under a case-insensitive key.
*/
mkmap_append(mkmap, STR(key_buffer), STR(value_buffer));
+ if (mkmap->dict->error)
+ msg_fatal("%s:%s: write error: %m",
+ mkmap->dict->type, mkmap->dict->name);
}
/*
* sendmail.
*/
mkmap_append(mkmap, "@", "@");
+ if (mkmap->dict->error)
+ msg_fatal("%s:%s: write error: %m",
+ mkmap->dict->type, mkmap->dict->name);
/*
* NIS compatibility: add time and master info. Unlike other information,
found = 1;
break;
}
+ if (dicts[n]->error)
+ msg_fatal("%s:%s: query error: %m",
+ dicts[n]->type, dicts[n]->name);
}
}
if (found)
}
vstream_printf("%s\n", value);
}
+ if (dict->error)
+ msg_fatal("%s:%s: query error: %m", dict->type, dict->name);
vstream_fflush(VSTREAM_OUT);
dict_close(dict);
return (value != 0);
/*
* Perform all requests.
*/
- while (vstring_get_nonl(keybuf, in) != VSTREAM_EOF)
- for (n = 0; n < map_count; n++)
+ while (vstring_get_nonl(keybuf, in) != VSTREAM_EOF) {
+ for (n = 0; n < map_count; n++) {
found |= (dict_del(dicts[n], STR(keybuf)) == 0);
+ if (dicts[n]->error)
+ msg_fatal("%s:%s: delete error: %m",
+ dicts[n]->type, dicts[n]->name);
+ }
+ }
/*
* Cleanup.
open_flags = O_RDWR;
dict = dict_open3(map_type, map_name, open_flags, dict_flags);
status = dict_del(dict, key);
+ if (dict->error)
+ msg_fatal("%s:%s: delete error: %m", dict->type, dict->name);
dict_close(dict);
return (status == 0);
}
}
vstream_printf("%s: %s\n", key, value);
}
+ if (dict->error)
+ msg_fatal("%s:%s: sequence error: %m", dict->type, dict->name);
vstream_fflush(VSTREAM_OUT);
dict_close(dict);
}
/* useful someday.
/* .IP \fBfail\fR
/* A table that reliably fails all requests. The lookup table
-/* name provides the internal error result code. This table
-/* exists to simplify Postfix error tests.
+/* name is used for logging. This table exists to simplify
+/* Postfix error tests.
/* .IP \fBhash\fR
/* An indexed file type based on hashing.
/* This is available on systems with support for Berkeley DB
* A direct rip-off of mail_conf_read(). XXX Avoid code duplication by
* better code decomposition.
*/
- dict_unknown_allowed = 1;
set_config_dir();
path = concatenate(var_config_dir, "/", MAIN_CONF_FILE, (char *) 0);
dict_load_file(CONFIG_DICT, path);
../../bin/$(PROG): $(PROG)
cp $(PROG) ../../bin
-tests: test1 test2
+tests: test1 test2 fail_test
root_tests:
done
rm -f map.in.db
+fail_test: $(PROG) aliases fail_test.in fail_test.ref
+ -sh fail_test.in > fail_test.tmp 2>&1 || exit 0
+ diff fail_test.ref fail_test.tmp
+ rm -f fail_test.tmp
+
printfck: $(OBJS) $(PROG)
rm -rf printfck
mkdir printfck
--- /dev/null
+./postmap -q xx fail:aliases
+echo xx | ./postmap -q - fail:aliases
+./postmap -d xx fail:aliases
+echo xx | ./postmap -d - fail:aliases
+./postmap -s fail:aliases
+./postmap -i fail:aliases < aliases
+./postmap fail:aliases
--- /dev/null
+postmap: fatal: fail:aliases: query error: Unknown error: 0
+postmap: fatal: fail:aliases: query error: Unknown error: 0
+postmap: fatal: fail:aliases: delete error: Unknown error: 0
+postmap: fatal: fail:aliases: delete error: Unknown error: 0
+postmap: fatal: fail:aliases: sequence error: Unknown error: 0
+postmap: fatal: fail:aliases: write error: Unknown error: 0
+postmap: fatal: fail:aliases: write error: Unknown error: 0
/* .IP \fBhash\fR
/* The output file is a hashed file, named \fIfile_name\fB.db\fR.
/* This is available on systems with support for \fBdb\fR databases.
+/* .IP \fBfail\fR
+/* A table that reliably fails all requests. The lookup table
+/* name is used for logging only. This table exists to simplify
+/* Postfix error tests.
/* .IP \fBsdbm\fR
/* The output consists of two files, named \fIfile_name\fB.pag\fR and
/* \fIfile_name\fB.dir\fR.
* Store the value under a case-insensitive key.
*/
mkmap_append(mkmap, key, value);
+ if (mkmap->dict->error)
+ msg_fatal("%s:%s: write error: %m",
+ mkmap->dict->type, mkmap->dict->name);
}
/*
state->found = 1;
break;
}
+ if (dicts[n]->error)
+ msg_fatal("%s:%s: query error: %m",
+ dicts[n]->type, dicts[n]->name);
}
}
found = 1;
break;
}
+ if (dicts[n]->error)
+ msg_fatal("%s:%s: query error: %m",
+ dicts[n]->type, dicts[n]->name);
}
}
} else {
}
vstream_printf("%s\n", value);
}
+ if (dict->error)
+ msg_fatal("%s:%s: query error: %m", dict->type, dict->name);
vstream_fflush(VSTREAM_OUT);
dict_close(dict);
return (value != 0);
/*
* Perform all requests.
*/
- while (vstring_get_nonl(keybuf, in) != VSTREAM_EOF)
- for (n = 0; n < map_count; n++)
+ while (vstring_get_nonl(keybuf, in) != VSTREAM_EOF) {
+ for (n = 0; n < map_count; n++) {
found |= (dict_del(dicts[n], STR(keybuf)) == 0);
+ if (dicts[n]->error)
+ msg_fatal("%s:%s: delete error: %m",
+ dicts[n]->type, dicts[n]->name);
+ }
+ }
/*
* Cleanup.
open_flags = O_RDWR;
dict = dict_open3(map_type, map_name, open_flags, dict_flags);
status = dict_del(dict, key);
+ if (dict->error)
+ msg_fatal("%s:%s: delete error: %m", dict->type, dict->name);
dict_close(dict);
return (status == 0);
}
}
vstream_printf("%s %s\n", key, value);
}
+ if (dict->error)
+ msg_fatal("%s:%s: sequence error: %m", dict->type, dict->name);
vstream_fflush(VSTREAM_OUT);
dict_close(dict);
}
postscreen.o: ../../include/mail_version.h
postscreen.o: ../../include/maps.h
postscreen.o: ../../include/match_list.h
-postscreen.o: ../../include/match_ops.h
postscreen.o: ../../include/msg.h
postscreen.o: ../../include/myaddrinfo.h
postscreen.o: ../../include/mymalloc.h
postscreen_dict.o: ../../include/htable.h
postscreen_dict.o: ../../include/maps.h
postscreen_dict.o: ../../include/match_list.h
-postscreen_dict.o: ../../include/match_ops.h
postscreen_dict.o: ../../include/msg.h
postscreen_dict.o: ../../include/server_acl.h
postscreen_dict.o: ../../include/string_list.h
postscreen_dnsbl.o: ../../include/mail_proto.h
postscreen_dnsbl.o: ../../include/maps.h
postscreen_dnsbl.o: ../../include/match_list.h
-postscreen_dnsbl.o: ../../include/match_ops.h
postscreen_dnsbl.o: ../../include/msg.h
postscreen_dnsbl.o: ../../include/myaddrinfo.h
postscreen_dnsbl.o: ../../include/mymalloc.h
postscreen_early.o: ../../include/mail_params.h
postscreen_early.o: ../../include/maps.h
postscreen_early.o: ../../include/match_list.h
-postscreen_early.o: ../../include/match_ops.h
postscreen_early.o: ../../include/msg.h
postscreen_early.o: ../../include/mymalloc.h
postscreen_early.o: ../../include/server_acl.h
postscreen_expand.o: ../../include/mail_proto.h
postscreen_expand.o: ../../include/maps.h
postscreen_expand.o: ../../include/match_list.h
-postscreen_expand.o: ../../include/match_ops.h
postscreen_expand.o: ../../include/msg.h
postscreen_expand.o: ../../include/server_acl.h
postscreen_expand.o: ../../include/string_list.h
postscreen_misc.o: ../../include/mail_params.h
postscreen_misc.o: ../../include/maps.h
postscreen_misc.o: ../../include/match_list.h
-postscreen_misc.o: ../../include/match_ops.h
postscreen_misc.o: ../../include/msg.h
postscreen_misc.o: ../../include/server_acl.h
postscreen_misc.o: ../../include/string_list.h
postscreen_send.o: ../../include/mail_params.h
postscreen_send.o: ../../include/maps.h
postscreen_send.o: ../../include/match_list.h
-postscreen_send.o: ../../include/match_ops.h
postscreen_send.o: ../../include/msg.h
postscreen_send.o: ../../include/server_acl.h
postscreen_send.o: ../../include/smtp_reply_footer.h
postscreen_smtpd.o: ../../include/mail_proto.h
postscreen_smtpd.o: ../../include/maps.h
postscreen_smtpd.o: ../../include/match_list.h
-postscreen_smtpd.o: ../../include/match_ops.h
postscreen_smtpd.o: ../../include/msg.h
postscreen_smtpd.o: ../../include/mymalloc.h
postscreen_smtpd.o: ../../include/name_code.h
postscreen_starttls.o: ../../include/mail_proto.h
postscreen_starttls.o: ../../include/maps.h
postscreen_starttls.o: ../../include/match_list.h
-postscreen_starttls.o: ../../include/match_ops.h
postscreen_starttls.o: ../../include/msg.h
postscreen_starttls.o: ../../include/mymalloc.h
postscreen_starttls.o: ../../include/name_code.h
postscreen_state.o: ../../include/mail_server.h
postscreen_state.o: ../../include/maps.h
postscreen_state.o: ../../include/match_list.h
-postscreen_state.o: ../../include/match_ops.h
postscreen_state.o: ../../include/msg.h
postscreen_state.o: ../../include/mymalloc.h
postscreen_state.o: ../../include/name_mask.h
postscreen_tests.o: ../../include/mail_params.h
postscreen_tests.o: ../../include/maps.h
postscreen_tests.o: ../../include/match_list.h
-postscreen_tests.o: ../../include/match_ops.h
postscreen_tests.o: ../../include/msg.h
postscreen_tests.o: ../../include/server_acl.h
postscreen_tests.o: ../../include/string_list.h
state->smtp_client_addr, state->smtp_client_port,
STR(state->cmd_buffer), cp);
vstring_strcpy(state->cmd_buffer, cp);
+ } else if (psc_cmd_filter->error != 0) {
+ /* XXX log something, even if regexps don't soft-fail. */
}
}
msg_panic("proxy_map_find: dict_open null result");
dict_register(STR(map_type_name_flags), dict);
}
- dict_errno = 0;
+ dict->error = 0;
return (dict);
}
int request_func;
const char *reply_key;
const char *reply_value;
+ int dict_status;
int reply_status;
/*
} else if ((dict = proxy_map_find(STR(request_map), request_flags,
&reply_status)) == 0) {
reply_key = reply_value = "";
- } else if (dict->flags = ((dict->flags & ~DICT_FLAG_RQST_MASK)
- | (request_flags & DICT_FLAG_RQST_MASK)),
- dict_seq(dict, request_func, &reply_key, &reply_value) == 0) {
- reply_status = PROXY_STAT_OK;
- } else if (dict_errno == 0) {
- reply_status = PROXY_STAT_NOKEY;
- reply_key = reply_value = "";
} else {
- reply_status = PROXY_STAT_RETRY;
- reply_key = reply_value = "";
+ dict->flags = ((dict->flags & ~DICT_FLAG_RQST_MASK)
+ | (request_flags & DICT_FLAG_RQST_MASK));
+ dict_status = dict_seq(dict, request_func, &reply_key, &reply_value);
+ if (dict_status == 0) {
+ reply_status = PROXY_STAT_OK;
+ } else if (dict->error == 0) {
+ reply_status = PROXY_STAT_NOKEY;
+ reply_key = reply_value = "";
+ } else {
+ reply_status = PROXY_STAT_RETRY;
+ reply_key = reply_value = "";
+ }
}
/*
| (request_flags & DICT_FLAG_RQST_MASK)),
(reply_value = dict_get(dict, STR(request_key))) != 0) {
reply_status = PROXY_STAT_OK;
- } else if (dict_errno == 0) {
+ } else if (dict->error == 0) {
reply_status = PROXY_STAT_NOKEY;
reply_value = "";
} else {
{
int request_flags;
DICT *dict;
+ int dict_status;
int reply_status;
/*
dict->flags = ((dict->flags & ~DICT_FLAG_RQST_MASK)
| (request_flags & DICT_FLAG_RQST_MASK)
| DICT_FLAG_SYNC_UPDATE | DICT_FLAG_DUP_REPLACE);
- dict_put(dict, STR(request_key), STR(request_value));
- reply_status = (dict_errno ? PROXY_STAT_RETRY : PROXY_STAT_OK);
+ dict_status = dict_put(dict, STR(request_key), STR(request_value));
+ if (dict_status == 0) {
+ reply_status = PROXY_STAT_OK;
+ } else if (dict->error == 0) {
+ reply_status = PROXY_STAT_NOKEY;
+ } else {
+ reply_status = PROXY_STAT_RETRY;
+ }
}
/*
| (request_flags & DICT_FLAG_RQST_MASK)
| DICT_FLAG_SYNC_UPDATE);
dict_status = dict_del(dict, STR(request_key));
- reply_status = (dict_status == 0 ? PROXY_STAT_OK :
- dict_status > 0 ? PROXY_STAT_NOKEY :
- PROXY_STAT_RETRY);
+ if (dict_status == 0) {
+ reply_status = PROXY_STAT_OK;
+ } else if (dict->error == 0) {
+ reply_status = PROXY_STAT_NOKEY;
+ } else {
+ reply_status = PROXY_STAT_RETRY;
+ }
}
/*
qmqpd.o: ../../include/mail_stream.h
qmqpd.o: ../../include/mail_version.h
qmqpd.o: ../../include/match_list.h
-qmqpd.o: ../../include/match_ops.h
qmqpd.o: ../../include/match_parent_style.h
qmqpd.o: ../../include/msg.h
qmqpd.o: ../../include/mymalloc.h
*/
if (namadr_list_match(qmqpd_clients, state->name, state->addr) != 0) {
qmqpd_receive(state);
- } else if (dict_errno == 0) {
+ } else if (qmqpd_clients->error == 0) {
qmqpd_reply(state, DONT_LOG, QMQPD_STAT_HARD,
"Error: %s is not authorized to use this service",
state->namaddr);
smtp.o: ../../include/mail_version.h
smtp.o: ../../include/maps.h
smtp.o: ../../include/match_list.h
-smtp.o: ../../include/match_ops.h
smtp.o: ../../include/mime_state.h
smtp.o: ../../include/msg.h
smtp.o: ../../include/msg_stats.h
smtp_addr.o: ../../include/mail_params.h
smtp_addr.o: ../../include/maps.h
smtp_addr.o: ../../include/match_list.h
-smtp_addr.o: ../../include/match_ops.h
smtp_addr.o: ../../include/mime_state.h
smtp_addr.o: ../../include/msg.h
smtp_addr.o: ../../include/msg_stats.h
smtp_chat.o: ../../include/mail_params.h
smtp_chat.o: ../../include/maps.h
smtp_chat.o: ../../include/match_list.h
-smtp_chat.o: ../../include/match_ops.h
smtp_chat.o: ../../include/mime_state.h
smtp_chat.o: ../../include/msg.h
smtp_chat.o: ../../include/msg_stats.h
smtp_connect.o: ../../include/mail_proto.h
smtp_connect.o: ../../include/maps.h
smtp_connect.o: ../../include/match_list.h
-smtp_connect.o: ../../include/match_ops.h
smtp_connect.o: ../../include/mime_state.h
smtp_connect.o: ../../include/msg.h
smtp_connect.o: ../../include/msg_stats.h
smtp_map11.o: ../../include/mail_addr_map.h
smtp_map11.o: ../../include/maps.h
smtp_map11.o: ../../include/match_list.h
-smtp_map11.o: ../../include/match_ops.h
smtp_map11.o: ../../include/mime_state.h
smtp_map11.o: ../../include/msg.h
smtp_map11.o: ../../include/msg_stats.h
smtp_proto.o: ../../include/maps.h
smtp_proto.o: ../../include/mark_corrupt.h
smtp_proto.o: ../../include/match_list.h
-smtp_proto.o: ../../include/match_ops.h
smtp_proto.o: ../../include/mime_state.h
smtp_proto.o: ../../include/msg.h
smtp_proto.o: ../../include/msg_stats.h
smtp_rcpt.o: ../../include/mail_params.h
smtp_rcpt.o: ../../include/maps.h
smtp_rcpt.o: ../../include/match_list.h
-smtp_rcpt.o: ../../include/match_ops.h
smtp_rcpt.o: ../../include/mime_state.h
smtp_rcpt.o: ../../include/msg.h
smtp_rcpt.o: ../../include/msg_stats.h
smtp_reuse.o: ../../include/mail_params.h
smtp_reuse.o: ../../include/maps.h
smtp_reuse.o: ../../include/match_list.h
-smtp_reuse.o: ../../include/match_ops.h
smtp_reuse.o: ../../include/mime_state.h
smtp_reuse.o: ../../include/msg.h
smtp_reuse.o: ../../include/msg_stats.h
smtp_sasl_auth_cache.o: ../../include/htable.h
smtp_sasl_auth_cache.o: ../../include/maps.h
smtp_sasl_auth_cache.o: ../../include/match_list.h
-smtp_sasl_auth_cache.o: ../../include/match_ops.h
smtp_sasl_auth_cache.o: ../../include/mime_state.h
smtp_sasl_auth_cache.o: ../../include/msg.h
smtp_sasl_auth_cache.o: ../../include/msg_stats.h
smtp_sasl_glue.o: ../../include/mail_params.h
smtp_sasl_glue.o: ../../include/maps.h
smtp_sasl_glue.o: ../../include/match_list.h
-smtp_sasl_glue.o: ../../include/match_ops.h
smtp_sasl_glue.o: ../../include/mime_state.h
smtp_sasl_glue.o: ../../include/msg.h
smtp_sasl_glue.o: ../../include/msg_stats.h
smtp_sasl_proto.o: ../../include/mail_params.h
smtp_sasl_proto.o: ../../include/maps.h
smtp_sasl_proto.o: ../../include/match_list.h
-smtp_sasl_proto.o: ../../include/match_ops.h
smtp_sasl_proto.o: ../../include/mime_state.h
smtp_sasl_proto.o: ../../include/msg.h
smtp_sasl_proto.o: ../../include/msg_stats.h
smtp_session.o: ../../include/mail_params.h
smtp_session.o: ../../include/maps.h
smtp_session.o: ../../include/match_list.h
-smtp_session.o: ../../include/match_ops.h
smtp_session.o: ../../include/mime_state.h
smtp_session.o: ../../include/msg.h
smtp_session.o: ../../include/msg_stats.h
smtp_state.o: ../../include/mail_params.h
smtp_state.o: ../../include/maps.h
smtp_state.o: ../../include/match_list.h
-smtp_state.o: ../../include/match_ops.h
smtp_state.o: ../../include/mime_state.h
smtp_state.o: ../../include/msg.h
smtp_state.o: ../../include/msg_stats.h
smtp_trouble.o: ../../include/mail_error.h
smtp_trouble.o: ../../include/maps.h
smtp_trouble.o: ../../include/match_list.h
-smtp_trouble.o: ../../include/match_ops.h
smtp_trouble.o: ../../include/mime_state.h
smtp_trouble.o: ../../include/msg.h
smtp_trouble.o: ../../include/msg_stats.h
smtp_unalias.o: ../../include/htable.h
smtp_unalias.o: ../../include/maps.h
smtp_unalias.o: ../../include/match_list.h
-smtp_unalias.o: ../../include/match_ops.h
smtp_unalias.o: ../../include/mime_state.h
smtp_unalias.o: ../../include/msg.h
smtp_unalias.o: ../../include/msg_stats.h
smtp_chat_append(session, "Replaced-by: ", "");
smtp_chat_append(session, " ", new_reply);
}
+ } else if (smtp_chat_resp_filter->error != 0) {
+ /* XXX log something, even if regexps don't soft-fail. */
}
}
if (chat_append_flag) {
argv_free(new_addr);
return (1);
} else {
- if (dict_errno != 0)
+ if (maps->error != 0)
msg_fatal("%s map lookup problem for %s", maps->title, STR(addr));
if (msg_verbose)
msg_info("%s: %s not found", myname, STR(addr));
if ((valid = smtp_sasl_auth_cache_valid_value(auth_cache, entry,
session->sasl_passwd)) == 0)
/* Remove expired, password changed, or malformed cache entry. */
- if (dict_del(auth_cache->dict, key) == 0)
+ if (dict_del(auth_cache->dict, key) != 0)
msg_warn("SASL auth failure map %s: entry not deleted: %s",
auth_cache->dict->name, key);
myfree(key);
smtpd.o: ../../include/mail_version.h
smtpd.o: ../../include/maps.h
smtpd.o: ../../include/match_list.h
-smtpd.o: ../../include/match_ops.h
smtpd.o: ../../include/milter.h
smtpd.o: ../../include/msg.h
smtpd.o: ../../include/myaddrinfo.h
smtpd_check.o: ../../include/mail_stream.h
smtpd_check.o: ../../include/maps.h
smtpd_check.o: ../../include/match_list.h
-smtpd_check.o: ../../include/match_ops.h
smtpd_check.o: ../../include/match_parent_style.h
smtpd_check.o: ../../include/milter.h
smtpd_check.o: ../../include/msg.h
msg_info("%s: replacing command \"%.100s\" with \"%.100s\"",
state->namaddr, STR(state->buffer), cp);
vstring_strcpy(state->buffer, cp);
+ } else if (smtpd_cmd_filter->error != 0) {
+ /* XXX log something, even if regexps don't soft-fail. */
}
}
if ((argc = smtpd_token(vstring_str(state->buffer), &argv)) == 0) {
static jmp_buf smtpd_check_buf;
/*
- * Results of restrictions.
+ * Results of restrictions. Errors are negative; see dict.h.
*/
-#define SMTPD_CHECK_ERROR (-1) /* server error */
#define SMTPD_CHECK_DUNNO 0 /* indifferent */
#define SMTPD_CHECK_OK 1 /* explicitly permit */
#define SMTPD_CHECK_REJECT 2 /* explicitly reject */
{
const char *result;
- dict_errno = 0;
- if ((result = mail_addr_find(maps, key, ext)) != 0 || dict_errno == 0)
+ if ((result = mail_addr_find(maps, key, ext)) != 0 || maps->error == 0)
return (result);
- if (dict_errno == DICT_ERR_RETRY)
+ if (maps->error == DICT_ERR_RETRY)
reject_dict_retry(state, reply_name);
else
reject_server_error(state);
if (namadr_list_match(mynetworks, state->name, state->addr))
return (SMTPD_CHECK_OK);
- else if (dict_errno == 0)
+ else if (mynetworks->error == 0)
return (SMTPD_CHECK_DUNNO);
else
- return (SMTPD_CHECK_ERROR);
+ return (mynetworks->error);
}
/* dup_if_truncate - save hostname and truncate if it ends in dot */
#ifdef USE_TLS
const char *found = 0;
- dict_errno = 0;
-
if (!state->tls_context)
return SMTPD_CHECK_DUNNO;
prints[0] = state->tls_context->peer_fingerprint;
prints[1] = state->tls_context->peer_pkey_fprint;
- /* After lookup error, leave dict_errno at non-zero value. */
+ /* After lookup error, leave relay_ccerts->error at non-zero value. */
for (i = 0; i < 2; ++i) {
found = maps_find(relay_ccerts, prints[i], DICT_FLAG_NONE);
if (found != 0) {
if (msg_verbose)
msg_info("Relaying allowed for certified client: %s", found);
return (SMTPD_CHECK_OK);
- } else if (dict_errno != 0) {
+ } else if (relay_ccerts->error != 0) {
msg_warn("relay_clientcerts: lookup error for fingerprint '%s', "
"pkey fingerprint %s", prints[0], prints[1]);
- return (SMTPD_CHECK_ERROR);
+ return (relay_ccerts->error);
}
}
if (msg_verbose)
msg_info("relay_clientcerts: No match for fingerprint '%s', "
"pkey fingerprint %s", prints[0], prints[1]);
}
-#else
- dict_errno = 0;
#endif
return (SMTPD_CHECK_DUNNO);
}
msg_info("%s: checking: %s", myname, hostaddr.buf);
if (!namadr_list_match(perm_mx_networks, host, hostaddr.buf)) {
- if (dict_errno == 0) {
+ if (perm_mx_networks->error == 0) {
/*
* Reject: at least one IP address is not listed in
for (mx = mx_list; mx != 0; mx = mx->next) {
if (msg_verbose)
msg_info("%s: resolve hostname: %s", myname, (char *) mx->data);
- if (resolve_local((char *) mx->data))
+ if (resolve_local((char *) mx->data) > 0)
return (YUP);
/* if no match or error, match interface addresses instead. */
}
CHK_ACCESS_RETURN(check_table_result(state, table, value, name,
reply_name, reply_class,
def_acl), FOUND);
- if (dict_errno != 0) {
+ if (dict->error != 0) {
msg_warn("%s: table lookup problem", table);
value = "451 4.3.5 Server configuration error";
CHK_ACCESS_RETURN(check_table_result(state, table, value, name,
CHK_DOMAIN_RETURN(check_table_result(state, table, value,
domain, reply_name, reply_class,
def_acl), FOUND);
- if (dict_errno != 0) {
+ if (dict->error != 0) {
msg_warn("%s: table lookup problem", table);
value = "451 4.3.5 Server configuration error";
CHK_DOMAIN_RETURN(check_table_result(state, table, value,
CHK_ADDR_RETURN(check_table_result(state, table, value, address,
reply_name, reply_class,
def_acl), FOUND);
- if (dict_errno != 0) {
+ if (dict->error != 0) {
msg_warn("%s: table lookup problem", table);
value = "451 4.3.5 Server configuration error";
CHK_ADDR_RETURN(check_table_result(state, table, value, address,
if (msg_verbose)
msg_info("%s: name=%s status=%d", myname, name, status);
- if (status == SMTPD_CHECK_ERROR) {
- if (dict_errno == DICT_ERR_RETRY)
+ if (status < 0) {
+ if (status == DICT_ERR_RETRY)
reject_dict_retry(state, reply_name);
else
reject_server_error(state);
} else if (is_map_command(state, name, CHECK_ADDR_MAP, &cpp)) {
if ((dict = dict_handle(*cpp)) == 0)
msg_panic("%s: dictionary not found: %s", myname, *cpp);
- dict_errno = 0;
if (dict_get(dict, state->addr) != 0)
status = SMTPD_CHECK_OK;
- else if (dict_errno != 0) {
+ else if (dict->error != 0) {
msg_warn("%s: %s: lookup error", VAR_LOC_RWR_CLIENTS, *cpp);
- status = SMTPD_CHECK_ERROR;
+ status = dict->error;
}
} else if (strcasecmp(name, PERMIT_SASL_AUTH) == 0) {
#ifdef USE_SASL_AUTH
VAR_LOC_RWR_CLIENTS, name);
continue;
}
- if (status == SMTPD_CHECK_ERROR) {
- if (dict_errno == DICT_ERR_RETRY) {
+ if (status < 0) {
+ if (status == DICT_ERR_RETRY) {
state->error_mask |= MAIL_ERROR_RESOURCE;
log_whatsup(state, "reject",
"451 4.3.0 Temporary lookup error");
RESOLVE_REPLY *reply)
{
const char *domain;
+ int rc;
if (addr == CONST_STR(reply->recipient))
msg_panic("resolve_clnt_query: result clobbers input");
if ((domain = strrchr(addr, '@')) == 0)
msg_fatal("%s: unqualified address", addr);
domain += 1;
- if (resolve_local(domain)) {
+ if ((rc = resolve_local(domain)) > 0) {
reply->flags = RESOLVE_CLASS_LOCAL;
vstring_strcpy(reply->transport, MAIL_SERVICE_LOCAL);
vstring_strcpy(reply->nexthop, domain);
- } else if (dict_errno) {
+ } else if (rc < 0) {
reply->flags = RESOLVE_FLAG_FAIL;
} else if (string_list_match(virt_alias_doms, domain)) {
reply->flags = RESOLVE_CLASS_ALIAS;
vstring_strcpy(reply->transport, MAIL_SERVICE_ERROR);
vstring_strcpy(reply->nexthop, "user unknown");
- } else if (dict_errno) {
+ } else if (virt_alias_doms->error) {
reply->flags = RESOLVE_FLAG_FAIL;
} else if (string_list_match(virt_mailbox_doms, domain)) {
reply->flags = RESOLVE_CLASS_VIRTUAL;
vstring_strcpy(reply->transport, MAIL_SERVICE_VIRTUAL);
vstring_strcpy(reply->nexthop, domain);
- } else if (dict_errno) {
+ } else if (virt_mailbox_doms->error) {
reply->flags = RESOLVE_FLAG_FAIL;
} else if (domain_list_match(relay_domains, domain)) {
reply->flags = RESOLVE_CLASS_RELAY;
vstring_strcpy(reply->transport, MAIL_SERVICE_RELAY);
vstring_strcpy(reply->nexthop, domain);
- } else if (dict_errno) {
+ } else if (relay_domains->error) {
reply->flags = RESOLVE_FLAG_FAIL;
} else {
reply->flags = RESOLVE_CLASS_DEFAULT;
resolve.o: ../../include/mail_proto.h
resolve.o: ../../include/maps.h
resolve.o: ../../include/match_list.h
-resolve.o: ../../include/match_ops.h
resolve.o: ../../include/match_parent_style.h
resolve.o: ../../include/msg.h
resolve.o: ../../include/mymalloc.h
transport.o: ../../include/mail_params.h
transport.o: ../../include/mail_proto.h
transport.o: ../../include/maps.h
-transport.o: ../../include/match_ops.h
+transport.o: ../../include/match_list.h
transport.o: ../../include/match_parent_style.h
transport.o: ../../include/msg.h
transport.o: ../../include/mymalloc.h
const char *relay;
const char *xport;
const char *sender_key;
+ int rc;
*flags = 0;
vstring_strcpy(channel, "CHANNEL NOT UPDATED");
* requestor.
*/
if ((domain = tok822_rfind_type(tree->tail, '@')) != 0) {
- if (domain->next && RESOLVE_LOCAL(domain->next) == 0) {
- if (dict_errno != 0) {
+ if (domain->next && (rc = RESOLVE_LOCAL(domain->next)) <= 0) {
+ if (rc < 0) {
*flags |= RESOLVE_FLAG_FAIL;
FREE_MEMORY_AND_RETURN;
}
vstring_insert(nextrcpt, rcpt_domain - STR(nextrcpt), "[", 1);
vstring_strcat(nextrcpt, "]");
rcpt_domain = strrchr(STR(nextrcpt), '@') + 1;
- if (resolve_local(rcpt_domain)) /* XXX */
+ if ((rc = resolve_local(rcpt_domain)) > 0) /* XXX */
domain = 0;
- else if (dict_errno != 0) {
+ else if (rc < 0) {
*flags |= RESOLVE_FLAG_FAIL;
FREE_MEMORY_AND_RETURN;
}
*/
#define STREQ(x,y) (strcmp((x), (y)) == 0)
- dict_errno = 0;
if (domain != 0) {
/*
var_show_unk_rcpt_table ?
" in virtual alias table" : "");
*flags |= RESOLVE_CLASS_ALIAS;
- } else if (dict_errno != 0) {
+ } else if (virt_alias_doms && virt_alias_doms->error != 0) {
msg_warn("%s lookup failure", VAR_VIRT_ALIAS_DOMS);
*flags |= RESOLVE_FLAG_FAIL;
FREE_MEMORY_AND_RETURN;
vstring_strcpy(nexthop, rcpt_domain);
blame = rp->virt_transport_name;
*flags |= RESOLVE_CLASS_VIRTUAL;
- } else if (dict_errno != 0) {
+ } else if (virt_mailbox_doms && virt_mailbox_doms->error != 0) {
msg_warn("%s lookup failure", VAR_VIRT_MAILBOX_DOMS);
*flags |= RESOLVE_FLAG_FAIL;
FREE_MEMORY_AND_RETURN;
vstring_strcpy(channel, RES_PARAM_VALUE(rp->relay_transport));
blame = rp->relay_transport_name;
*flags |= RESOLVE_CLASS_RELAY;
- } else if (dict_errno != 0) {
+ } else if (relay_domains && relay_domains->error != 0) {
msg_warn("%s lookup failure", VAR_RELAY_DOMAINS);
*flags |= RESOLVE_FLAG_FAIL;
FREE_MEMORY_AND_RETURN;
vstring_strcpy(channel, strcasecmp(xport, "DUNNO") == 0 ?
RES_PARAM_VALUE(rp->def_transport) : xport);
blame = rp->snd_def_xp_maps_name;
- } else if (dict_errno != 0) {
+ } else if (rp->snd_def_xp_info
+ && rp->snd_def_xp_info->error != 0) {
msg_warn("%s lookup failure", rp->snd_def_xp_maps_name);
*flags |= RESOLVE_FLAG_FAIL;
FREE_MEMORY_AND_RETURN;
}
vstring_strcpy(nexthop, strcasecmp(relay, "DUNNO") == 0 ?
rcpt_domain : relay);
- } else if (dict_errno != 0) {
+ } else if (rp->snd_relay_info
+ && rp->snd_relay_info->error != 0) {
msg_warn("%s lookup failure", rp->snd_relay_maps_name);
*flags |= RESOLVE_FLAG_FAIL;
FREE_MEMORY_AND_RETURN;
if (rp->transport_info && !(*flags & RESOLVE_CLASS_ALIAS)) {
if (transport_lookup(rp->transport_info, STR(nextrcpt),
rcpt_domain, channel, nexthop) == 0
- && dict_errno != 0) {
+ && rp->transport_info->transport_path->error != 0) {
msg_warn("%s lookup failure", rp->transport_maps_name);
*flags |= RESOLVE_FLAG_FAIL;
FREE_MEMORY_AND_RETURN;
vstring_strcpy(channel, MAIL_SERVICE_ERROR);
/* 5.1.6 is the closest match, but not perfect. */
vstring_sprintf(nexthop, "5.1.6 User has moved to %s", newloc);
- } else if (dict_errno != 0) {
+ } else if (relocated_maps->error != 0) {
msg_warn("%s lookup failure", VAR_RELOCATED_MAPS);
*flags |= RESOLVE_FLAG_FAIL;
FREE_MEMORY_AND_RETURN;
const char *host;
const char *value;
- /*
- * Reset previous error history.
- */
- dict_errno = 0;
-
#define FOUND 1
#define NOTFOUND 0
msg_info("wildcard_{chan:hop}={%s:%s}",
vstring_str(channel), vstring_str(nexthop));
} else {
- tp->wildcard_errno = dict_errno;
+ tp->wildcard_errno = tp->transport_path->error;
vstring_free(channel);
vstring_free(nexthop);
tp->wildcard_channel = 0;
if (find_transport_entry(tp, addr, rcpt_domain, FULL, channel, nexthop))
return (FOUND);
- if (dict_errno != 0)
+ if (tp->transport_path->error != 0)
return (NOTFOUND);
/*
myfree(stripped_addr);
if (found)
return (FOUND);
- if (dict_errno != 0)
+ if (tp->transport_path->error != 0)
return (NOTFOUND);
}
for (name = ratsign + 1; *name != 0; name = next) {
if (find_transport_entry(tp, name, rcpt_domain, PARTIAL, channel, nexthop))
return (FOUND);
- if (dict_errno != 0)
+ if (tp->transport_path->error != 0)
return (NOTFOUND);
if ((next = strchr(name + 1, '.')) == 0)
break;
if (tp->wildcard_errno || event_time() > tp->expire)
transport_wildcard_init(tp);
if (tp->wildcard_errno) {
- dict_errno = tp->wildcard_errno;
+ tp->transport_path->error = tp->wildcard_errno;
return (NOTFOUND);
} else if (tp->wildcard_channel) {
update_entry(STR(tp->wildcard_channel), STR(tp->wildcard_nexthop),
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
+ dict_fail.c msg_rate_delay.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 \
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
+ dict_fail.o msg_rate_delay.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 \
get_domainname.h get_hostname.h hex_code.h hex_quote.h host_port.h \
htable.h inet_addr_host.h inet_addr_list.h inet_addr_local.h \
inet_proto.h iostuff.h line_wrap.h listen.h lstat_as.h mac_expand.h \
- mac_parse.h make_dirs.h mask_addr.h match_list.h match_ops.h msg.h \
+ mac_parse.h make_dirs.h mask_addr.h match_list.h msg.h \
msg_output.h msg_syslog.h msg_vstream.h mvect.h myaddrinfo.h myflock.h \
mymalloc.h myrand.h name_code.h name_mask.h netstring.h nvtable.h \
open_as.h open_lock.h percentm.h posix_signals.h readlline.h ring.h \
attr_scan0 host_port attr_scan_plain attr_print_plain htable \
unix_recv_fd unix_send_fd stream_recv_fd stream_send_fd hex_code \
myaddrinfo myaddrinfo4 inet_proto sane_basename format_tv \
- valid_utf_8 ip_match base32_code
+ valid_utf_8 ip_match base32_code msg_rate_delay
LIB_DIR = ../../lib
INC_DIR = ../../include
$(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS)
mv junk $@.o
+msg_rate_delay: $(LIB)
+ mv $@.o junk
+ $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS)
+ mv junk $@.o
+
tests: valid_hostname_test mac_expand_test dict_test unescape_test \
hex_quote_test ctable_test inet_addr_list_test base64_code_test \
attr_scan64_test attr_scan0_test dict_pcre_test host_port_test \
match_list.o: dict.h
match_list.o: match_list.c
match_list.o: match_list.h
-match_list.o: match_ops.h
match_list.o: msg.h
match_list.o: mymalloc.h
match_list.o: stringops.h
match_ops.o: argv.h
match_ops.o: cidr_match.h
match_ops.o: dict.h
+match_ops.o: match_list.h
match_ops.o: match_ops.c
-match_ops.o: match_ops.h
match_ops.o: msg.h
match_ops.o: myaddrinfo.h
match_ops.o: mymalloc.h
msg_output.o: vbuf.h
msg_output.o: vstream.h
msg_output.o: vstring.h
+msg_rate_delay.o: events.h
+msg_rate_delay.o: msg.h
+msg_rate_delay.o: msg_rate_delay.c
+msg_rate_delay.o: sys_defs.h
+msg_rate_delay.o: vbuf.h
+msg_rate_delay.o: vstring.h
msg_syslog.o: msg.h
msg_syslog.o: msg_output.h
msg_syslog.o: msg_syslog.c
/* SYNOPSIS
/* #include <dict.h>
/*
-/* extern int dict_unknown_allowed;
-/* extern int dict_errno;
-/*
/* void dict_register(dict_name, dict_info)
/* const char *dict_name;
/* DICT *dict_info;
/* void dict_unregister(dict_name)
/* const char *dict_name;
/*
-/* void dict_update(dict_name, member, value)
+/* int dict_update(dict_name, member, value)
/* const char *dict_name;
/* const char *member;
/* const char *value;
/* void (*action)(dict_name, dict_handle, context)
/* char *context;
/*
+/* int dict_error(dict_name)
+/* const char *dict_name;
+/*
/* const char *dict_changed_name()
/* AUXILIARY FUNCTIONS
/* void dict_load_file(dict_name, path)
/* value of \fIname\fR. The forms $(\fIname\fR) and ${\fIname\fR} are
/* also supported.
/* .IP "unknown names"
-/* References to unknown dictionary or dictionary member names either
-/* default to an empty dictionary or null pointer value, or cause a
-/* run-time error. The behavior is controlled by the global
-/* \fIdict_unknown_allowed\fR boolean variable.
+/* An update request for an unknown dictionary name will trigger
+/* the instantiation of an in-memory dictionary with that name.
+/* A lookup request (including delete and sequence) for an
+/* unknown dictionary will result in a "not found" and "no
+/* error" result.
/* .PP
/* dict_register() adds a new dictionary, including access methods,
/* to the list of known dictionaries, or increments the reference
/*
/* dict_update() updates the value of the named dictionary member.
/* The dictionary member and the named dictionary are instantiated
-/* on the fly.
+/* on the fly. The result value is zero (DICT_STAT_SUCCESS)
+/* when the update was made.
/*
/* dict_lookup() returns the value of the named member (i.e. without
/* expanding macros in the member value). The \fIdict_name\fR argument
/* modified, or if the result is to survive multiple dict_lookup() calls.
/*
/* dict_delete() removes the named member from the named dictionary.
-/* The result value is zero when the member was found, > 0 if
-/* it was not found, and < 0 in case of error (a database may
-/* not return after error).
+/* The result value is zero (DICT_STAT_SUCCESS) when the member
+/* was found.
/*
/* dict_sequence() steps through the named dictionary and returns
/* keys and values in some implementation-defined order. The func
/* entry or DICT_SEQ_FUN_NEXT to select the next entry. The result
/* is owned by the underlying dictionary method. Make a copy if the
/* result is to be modified, or if the result is to survive multiple
-/* dict_sequence() calls. The result value is zero when a member
-/* was found.
+/* dict_sequence() calls. The result value is zero (DICT_STAT_SUCCESS)
+/* when a member was found.
/*
/* dict_eval() expands macro references in the specified string.
/* The result is owned by the dictionary manager. Make a copy if the
/* htable(3)
/* BUGS
/* DIAGNOSTICS
-/* Fatal errors: out of memory, reference to unknown name,
-/* malformed macro name.
+/* Fatal errors: out of memory, malformed macro name.
/*
-/* The lookup routines may set the \fIdict_errno\fR variable when
-/* they were unable to find the requested result. The lookup
-/* routines must reset \fIdict_errno\fR before each lookup operation.
-/* \fIdict_errno\fR can have the following values:
-/* .IP DICT_ERR_RETRY
+/* The lookup routine returns non-null when the request is
+/* satisfied. The update, delete and sequence routines return
+/* zero (DICT_STAT_SUCCESS) when the request is satisfied.
+/* The dict_error() function returns non-zero only when the
+/* last operation was not satisfied due to a dictionary access
+/* error. The result can have the following values:
+/* .IP DICT_ERR_NONE(zero)
+/* There was no dictionary access error. For example, the
+/* request was satisfied, the requested information did not
+/* exist in the dictionary, or the information already existed
+/* when it should not exist (collision).
+/* .IP DICT_ERR_RETRY(<0)
/* The dictionary was temporarily unavailable. This can happen
/* with network-based services.
+/* .IP DICT_ERR_CONFIG(<0)
+/* The dictionary was unavailable due to a configuration error.
+/* .PP
+/* Generally, a program is expected to test the function result
+/* value for "success" first. If the operation was not successful,
+/* a program is expected to test for a non-zero dict->error
+/* status to distinguish between a data notfound/collision
+/* condition or a dictionary access error.
/* LICENSE
/* .ad
/* .fi
#include "dict.h"
#include "dict_ht.h"
- /*
- * By default, use a sane default for an unknown name.
- */
-int dict_unknown_allowed = 1;
-int dict_errno = 0;
-
static HTABLE *dict_table;
/*
#define dict_node(dict) \
(dict_table ? (DICT_NODE *) htable_find(dict_table, dict) : 0)
+/* Find a dictionary handle by name for lookup purposes. */
+
+#define DICT_FIND_FOR_LOOKUP(dict, dict_name) do { \
+ DICT_NODE *node; \
+ if ((node = dict_node(dict_name)) != 0) \
+ dict = node->dict; \
+ else \
+ dict = 0; \
+} while (0)
+
+/* Find a dictionary handle by name for update purposes. */
+
+#define DICT_FIND_FOR_UPDATE(dict, dict_name) do { \
+ DICT_NODE *node; \
+ if ((node = dict_node(dict_name)) == 0) { \
+ dict = dict_ht_open(dict_name, O_CREAT | O_RDWR, 0); \
+ dict_register(dict_name, dict); \
+ } else \
+ dict = node->dict; \
+} while (0)
+
#define STR(x) vstring_str(x)
/* dict_register - make association with dictionary */
/* dict_update - replace or add dictionary entry */
-void dict_update(const char *dict_name, const char *member, const char *value)
+int dict_update(const char *dict_name, const char *member, const char *value)
{
const char *myname = "dict_update";
- DICT_NODE *node;
DICT *dict;
- if ((node = dict_node(dict_name)) == 0) {
- if (dict_unknown_allowed == 0)
- msg_fatal("%s: unknown dictionary: %s", myname, dict_name);
- dict = dict_ht_open(dict_name, O_CREAT | O_RDWR, 0);
- dict_register(dict_name, dict);
- } else
- dict = node->dict;
+ DICT_FIND_FOR_UPDATE(dict, dict_name);
if (msg_verbose > 1)
msg_info("%s: %s = %s", myname, member, value);
- dict->update(dict, member, value);
+ return (dict->update(dict, member, value));
}
/* dict_lookup - look up dictionary entry */
const char *dict_lookup(const char *dict_name, const char *member)
{
const char *myname = "dict_lookup";
- DICT_NODE *node;
DICT *dict;
- const char *ret = 0;
+ const char *ret;
- if ((node = dict_node(dict_name)) == 0) {
- if (dict_unknown_allowed == 0)
- msg_fatal("%s: unknown dictionary: %s", myname, dict_name);
- } else {
- dict = node->dict;
+ DICT_FIND_FOR_LOOKUP(dict, dict_name);
+ if (dict != 0) {
ret = dict->lookup(dict, member);
- if (ret == 0 && dict_unknown_allowed == 0)
- msg_fatal("dictionary %s: unknown member: %s", dict_name, member);
+ if (msg_verbose > 1)
+ msg_info("%s: %s = %s", myname, member, ret ? ret :
+ dict->error ? "(error)" : "(notfound)");
+ return (ret);
+ } else {
+ if (msg_verbose > 1)
+ msg_info("%s: %s = %s", myname, member, "(notfound)");
+ return (0);
}
- if (msg_verbose > 1)
- msg_info("%s: %s = %s", myname, member, ret ? ret : "(notfound)");
- return (ret);
}
/* dict_delete - delete dictionary entry */
int dict_delete(const char *dict_name, const char *member)
{
const char *myname = "dict_delete";
- DICT_NODE *node;
DICT *dict;
- int result;
- if ((node = dict_node(dict_name)) == 0) {
- if (dict_unknown_allowed == 0)
- msg_fatal("%s: unknown dictionary: %s", myname, dict_name);
- dict = dict_ht_open(dict_name, O_CREAT | O_RDWR, 0);
- dict_register(dict_name, dict);
- } else
- dict = node->dict;
+ DICT_FIND_FOR_LOOKUP(dict, dict_name);
if (msg_verbose > 1)
msg_info("%s: delete %s", myname, member);
- if ((result = dict->delete(dict, member)) != 0 && dict_unknown_allowed == 0)
- msg_fatal("%s: dictionary %s: unknown member: %s",
- myname, dict_name, member);
- return (result);
+ return (dict ? dict->delete(dict, member) : DICT_STAT_FAIL);
}
/* dict_sequence - traverse dictionary */
const char **member, const char **value)
{
const char *myname = "dict_sequence";
- DICT_NODE *node;
DICT *dict;
- if ((node = dict_node(dict_name)) == 0) {
- if (dict_unknown_allowed == 0)
- msg_fatal("%s: unknown dictionary: %s", myname, dict_name);
- dict = dict_ht_open(dict_name, O_CREAT | O_RDWR, 0);
- dict_register(dict_name, dict);
- } else
- dict = node->dict;
+ DICT_FIND_FOR_LOOKUP(dict, dict_name);
if (msg_verbose > 1)
msg_info("%s: sequence func %d", myname, func);
- return (dict->sequence(dict, func, member, value));
+ return (dict ? dict->sequence(dict, func, member, value) : DICT_STAT_FAIL);
+}
+
+/* dict_error - return last error */
+
+int dict_error(const char *dict_name)
+{
+ DICT *dict;
+
+ DICT_FIND_FOR_LOOKUP(dict, dict_name);
+ return (dict ? dict->error : DICT_ERR_NONE);
}
/* dict_load_file - read entries from text file */
int lineno;
const char *err;
struct stat st;
- DICT_NODE *node;
DICT *dict;
/*
* Instantiate the dictionary even if the file is empty.
*/
- if ((node = dict_node(dict_name)) == 0) {
- if (dict_unknown_allowed == 0)
- msg_fatal("%s: unknown dictionary: %s", myname, dict_name);
- dict = dict_ht_open(dict_name, O_CREAT | O_RDWR, 0);
- dict_register(dict_name, dict);
- } else
- dict = node->dict;
-
+ DICT_FIND_FOR_UPDATE(dict, dict_name);
buf = vstring_alloc(100);
lineno = 0;
VSTREAM_PATH(fp), lineno, err, STR(buf));
if (msg_verbose > 1)
msg_info("%s: %s = %s", myname, member, val);
- dict->update(dict, member, val);
+ if (dict->update(dict, member, val) != 0)
+ msg_fatal("%s, line %d: unable to update %s:%s",
+ VSTREAM_PATH(fp), lineno, dict->type, dict->name);
}
vstring_free(buf);
dict->owner.uid = st.st_uid;
static const char *dict_eval_lookup(const char *key, int unused_type,
char *dict_name)
{
- const char *pp;
+ const char *pp = 0;
+ DICT *dict;
/*
* XXX how would one recover?
*/
- if ((pp = dict_lookup(dict_name, key)) == 0 && dict_errno != 0)
- msg_fatal("dictionary %s: lookup %s: temporary error", dict_name, key);
-
+ DICT_FIND_FOR_LOOKUP(dict, dict_name);
+ if (dict != 0
+ && (pp = dict->lookup(dict, key)) == 0 && dict->error != 0)
+ msg_fatal("dictionary %s: lookup %s: operation failed", dict_name, key);
return (pp);
}
char *name; /* for diagnostics */
int flags; /* see below */
const char *(*lookup) (struct DICT *, const char *);
- void (*update) (struct DICT *, const char *, const char *);
+ int (*update) (struct DICT *, const char *, const char *);
int (*delete) (struct DICT *, const char *);
int (*sequence) (struct DICT *, int, const char **, const char **);
void (*close) (struct DICT *);
time_t mtime; /* mod time at open */
VSTRING *fold_buf; /* key folding buffer */
DICT_OWNER owner; /* provenance */
+ int error; /* last operation only */
} DICT;
extern DICT *dict_alloc(const char *, const char *, ssize_t);
DICT_FLAG_PARANOID)
#define DICT_FLAG_INST_MASK ~(DICT_FLAG_IMPL_MASK | DICT_FLAG_RQST_MASK)
-extern int dict_unknown_allowed;
-extern int dict_errno;
-
+ /*
+ * dict->error values. Errors must be negative; smtpd_check depends on this.
+ */
#define DICT_ERR_NONE 0 /* no error */
-#define DICT_ERR_RETRY 1 /* soft error */
-#define DICT_ERR_CONFIG 2 /* configuration error */
+#define DICT_ERR_RETRY (-1) /* soft error */
+#define DICT_ERR_CONFIG (-2) /* configuration error */
+
+ /*
+ * FAIL/ERROR are suggested result values, not meant for use in comparisons.
+ */
+#define DICT_STAT_FAIL 1 /* any value > 0: notfound, conflict */
+#define DICT_STAT_SUCCESS 0 /* request satisfied */
+#define DICT_STAT_ERROR (-1) /* any value < 0: database error */
+
+#define DICT_ERR_VAL_RETURN(dict, err, val) do { \
+ (dict)->error = (err); \
+ return (val); \
+ } while (0)
/*
* Sequence function types.
extern void dict_register(const char *, DICT *);
extern DICT *dict_handle(const char *);
extern void dict_unregister(const char *);
-extern void dict_update(const char *, const char *, const char *);
+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 void dict_load_fp(const char *, VSTREAM *);
extern const char *dict_eval(const char *, const char *, int);
+extern int dict_error(const char *);
/*
* Low-level interface, with physical dictionary handles.
/* dict_default_update - trap unimplemented operation */
-static void dict_default_update(DICT *dict, const char *unused_key,
- const char *unused_value)
+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",
dict->type, dict->name);
dict->fold_buf = 0;
dict->owner.status = DICT_OWNER_UNKNOWN;
dict->owner.uid = ~0;
+ dict->error = DICT_ERR_NONE;
return dict;
}
/* .IP table
/* A bare dictonary handle.
/* DIAGNOSTICS
-/* These routines terminate with a fatal run-time error
-/* for unrecoverable database errors. This allows the
-/* program to restart and reset the database to an
-/* empty initial state.
+/* When a request is satisfied, the lookup routine returns
+/* non-null, and the update, delete and sequence routines
+/* return zero. The cache->error value is zero when a request
+/* could not be satisfied because an item did not exist (delete,
+/* sequence) or if it could not be updated. The cache->error
+/* value is non-zero only when a request could not be satisfied,
+/* and the cause was a database error.
+/*
+/* Cache access errors are logged with a warning message. To
+/* avoid spamming the log, each type of operation logs no more
+/* than one cache access error per second, per cache. Specify
+/* the DICT_CACHE_FLAG_VERBOSE flag (see above) to log all
+/* warnings.
/* BUGS
/* There should be a way to suspend automatic program suicide
/* until a cache cleanup run is completed. Some entries may
int cache_flags; /* see below */
int user_flags; /* logging */
DICT *db; /* database handle */
+ int error; /* last operation only */
/* Delete-behind support. */
char *saved_curr_key; /* "current" cache lookup key */
char *exp_context; /* call-back context */
int retained; /* entries retained in cleanup run */
int dropped; /* entries removed in cleanup run */
+
+ /* Rate-limited logging support. */
+ int log_delay;
+ time_t upd_log_stamp; /* last update warning */
+ time_t get_log_stamp; /* last lookup warning */
+ time_t del_log_stamp; /* last delete warning */
+ time_t seq_log_stamp; /* last sequence warning */
};
#define DC_FLAG_DEL_SAVED_CURRENT_KEY (1<<0) /* delete-behind is scheduled */
+ /*
+ * Don't log cache access errors more than once per second.
+ */
+#define DC_DEF_LOG_DELAY 1
+
/*
* Macros to make obscure code more readable.
*/
{
const char *myname = "dict_cache_lookup";
const char *cache_val;
+ DICT *db = cp->db;
/*
* Search for the cache entry. Don't return an entry that is scheduled
if (cp->user_flags & DICT_CACHE_FLAG_VERBOSE)
msg_info("%s: key=%s (pretend not found - scheduled for deletion)",
myname, cache_key);
- dict_errno = 0;
- return (0);
+ DICT_ERR_VAL_RETURN(cp, DICT_ERR_NONE, (char *) 0);
} else {
- cache_val = dict_get(cp->db, cache_key);
-#if 0
- if (cache_val == 0 && dict_errno != 0)
- msg_warn("%s: cache lookup for '%s' failed due to error",
- cp->name, cache_key);
-#endif
+ cache_val = dict_get(db, cache_key);
+ if (cache_val == 0 && db->error != 0)
+ msg_rate_delay(&cp->get_log_stamp, cp->log_delay, msg_warn,
+ "%s: cache lookup for '%s' failed due to error",
+ cp->name, cache_key);
if (cp->user_flags & DICT_CACHE_FLAG_VERBOSE)
msg_info("%s: key=%s value=%s", myname, cache_key,
- cache_val ? cache_val : dict_errno ?
+ cache_val ? cache_val : db->error ?
"error" : "(not found)");
- return (cache_val);
+ DICT_ERR_VAL_RETURN(cp, db->error, cache_val);
}
}
/* dict_cache_update - save entry to cache */
-void dict_cache_update(DICT_CACHE *cp, const char *cache_key,
+int dict_cache_update(DICT_CACHE *cp, const char *cache_key,
const char *cache_val)
{
const char *myname = "dict_cache_update";
+ DICT *db = cp->db;
+ int put_res;
/*
* Store the cache entry and cancel the delete-behind operation.
}
if (cp->user_flags & DICT_CACHE_FLAG_VERBOSE)
msg_info("%s: key=%s value=%s", myname, cache_key, cache_val);
- dict_put(cp->db, cache_key, cache_val);
+ put_res = dict_put(db, cache_key, cache_val);
+ if (put_res != 0)
+ msg_rate_delay(&cp->upd_log_stamp, cp->log_delay, msg_warn,
+ "%s: could not update entry for %s", cp->name, cache_key);
+ DICT_ERR_VAL_RETURN(cp, db->error, put_res);
}
/* dict_cache_delete - delete entry from cache */
int dict_cache_delete(DICT_CACHE *cp, const char *cache_key)
{
const char *myname = "dict_cache_delete";
- int zero_means_found;
+ int del_res;
+ DICT *db = cp->db;
/*
* Delete the entry, unless we would delete the current first/next entry.
if (cp->user_flags & DICT_CACHE_FLAG_VERBOSE)
msg_info("%s: key=%s (current entry - schedule for delete-behind)",
myname, cache_key);
- zero_means_found = 0;
+ DICT_ERR_VAL_RETURN(cp, DICT_ERR_NONE, DICT_STAT_SUCCESS);
} else {
- zero_means_found = dict_del(cp->db, cache_key);
-#if 0
- if (zero_means_found != 0)
- msg_warn("%s: could not delete entry for %s", cp->name, cache_key);
-#endif
+ del_res = dict_del(db, cache_key);
+ if (del_res != 0)
+ msg_rate_delay(&cp->del_log_stamp, cp->log_delay, msg_warn,
+ "%s: could not delete entry for %s", cp->name, cache_key);
if (cp->user_flags & DICT_CACHE_FLAG_VERBOSE)
msg_info("%s: key=%s (%s)", myname, cache_key,
- zero_means_found == 0 ? "found" : "not found");
+ del_res == 0 ? "found" :
+ db->error ? "error" : "not found");
+ DICT_ERR_VAL_RETURN(cp, db->error, del_res);
}
- return (zero_means_found);
}
/* dict_cache_sequence - look up the first/next cache entry */
const char **cache_val)
{
const char *myname = "dict_cache_sequence";
- int zero_means_found;
+ int seq_res;
const char *raw_cache_key;
const char *raw_cache_val;
char *previous_curr_key;
char *previous_curr_val;
+ DICT *db = cp->db;
/*
* Find the first or next database entry. Hide the record with the cache
* cleanup completion time stamp.
*/
- zero_means_found =
- dict_seq(cp->db, first_next, &raw_cache_key, &raw_cache_val);
- if (zero_means_found == 0
+ seq_res = dict_seq(db, first_next, &raw_cache_key, &raw_cache_val);
+ if (seq_res == 0
&& strcmp(raw_cache_key, DC_LAST_CACHE_CLEANUP_COMPLETED) == 0)
- zero_means_found =
- dict_seq(cp->db, DICT_SEQ_FUN_NEXT, &raw_cache_key, &raw_cache_val);
+ seq_res =
+ dict_seq(db, DICT_SEQ_FUN_NEXT, &raw_cache_key, &raw_cache_val);
if (cp->user_flags & DICT_CACHE_FLAG_VERBOSE)
msg_info("%s: key=%s value=%s", myname,
- zero_means_found == 0 ? raw_cache_key : "(not found)",
- zero_means_found == 0 ? raw_cache_val : "(not found)");
+ seq_res == 0 ? raw_cache_key : db->error ?
+ "(error)" : "(not found)",
+ seq_res == 0 ? raw_cache_val : db->error ?
+ "(error)" : "(not found)");
+ if (db->error)
+ msg_rate_delay(&cp->seq_log_stamp, cp->log_delay, msg_warn,
+ "%s: sequence error", cp->name);
/*
* Save the current cache_key and cache_val before they are clobbered by
*/
previous_curr_key = cp->saved_curr_key;
previous_curr_val = cp->saved_curr_val;
- if (zero_means_found == 0) {
+ if (seq_res == 0) {
cp->saved_curr_key = mystrdup(raw_cache_key);
cp->saved_curr_val = mystrdup(raw_cache_val);
} else {
/*
* Delete behind.
*/
- if (DC_IS_SCHEDULED_FOR_DELETE_BEHIND(cp)) {
+ if (db->error == 0 && DC_IS_SCHEDULED_FOR_DELETE_BEHIND(cp)) {
DC_CANCEL_DELETE_BEHIND(cp);
if (cp->user_flags & DICT_CACHE_FLAG_VERBOSE)
msg_info("%s: delete-behind key=%s value=%s",
myname, previous_curr_key, previous_curr_val);
- if (dict_del(cp->db, previous_curr_key) != 0)
- msg_warn("%s: could not delete entry for %s",
- cp->name, previous_curr_key);
+ if (dict_del(db, previous_curr_key) != 0)
+ msg_rate_delay(&cp->del_log_stamp, cp->log_delay, msg_warn,
+ "%s: could not delete entry for %s",
+ cp->name, previous_curr_key);
}
/*
*/
*cache_key = (cp)->saved_curr_key;
*cache_val = (cp)->saved_curr_val;
- return (zero_means_found);
+ DICT_ERR_VAL_RETURN(cp, db->error, seq_res);
}
/* dict_cache_delete_behind_reset - reset "delete behind" state */
/*
* Cache cleanup completed. Report vital statistics.
*/
-#if 0
- else if (dict_errno != 0) {
- msg_warn("%s: cache cleanup scan failed due to error", cp->name);
+ else if (cp->error != 0) {
+ msg_warn("%s: cache cleanup scan terminated due to error", cp->name);
dict_cache_clean_stat_log_reset(cp, "partial");
next_interval = cp->exp_interval;
- }
-#endif
- else {
+ } else {
if (cp->user_flags & DICT_CACHE_FLAG_VERBOSE)
msg_info("%s: done %s cache cleanup scan", myname, cp->name);
dict_cache_clean_stat_log_reset(cp, "full");
break;
case DICT_CACHE_CTL_FLAGS:
cp->user_flags = va_arg(ap, int);
+ cp->log_delay = (cp->user_flags & DICT_CACHE_FLAG_VERBOSE) ?
+ 0 : DC_DEF_LOG_DELAY;
break;
case DICT_CACHE_CTL_INTERVAL:
cp->exp_interval = va_arg(ap, int);
cp->exp_context = 0;
cp->retained = 0;
cp->dropped = 0;
+ cp->log_delay = DC_DEF_LOG_DELAY;
+ cp->upd_log_stamp = cp->get_log_stamp =
+ cp->del_log_stamp = cp->seq_log_stamp = 0;
return (cp);
}
extern DICT_CACHE *dict_cache_open(const char *, int, int);
extern void dict_cache_close(DICT_CACHE *);
extern const char *dict_cache_lookup(DICT_CACHE *, const char *);
-extern void dict_cache_update(DICT_CACHE *, const char *, const char *);
+extern int dict_cache_update(DICT_CACHE *, const char *, const char *);
extern int dict_cache_delete(DICT_CACHE *, const char *);
extern int dict_cache_sequence(DICT_CACHE *, int, const char **, const char **);
extern void dict_cache_control(DICT_CACHE *,...);
static unsigned len;
const char *result = 0;
- dict_errno = 0;
+ dict->error = 0;
/* CDB is constant, so do not try to acquire a lock. */
/* dict_cdbm_update - add database entry, create mode */
-static void dict_cdbm_update(DICT *dict, const char *name, const char *value)
+static int dict_cdbm_update(DICT *dict, const char *name, const char *value)
{
DICT_CDBM *dict_cdbm = (DICT_CDBM *) dict;
unsigned ksize, vsize;
int r;
+ dict->error = 0;
+
/*
* Optionally fold the key.
*/
msg_fatal("%s: duplicate entry: \"%s\"",
dict_cdbm->dict.name, name);
}
+ return (r);
#else
if (cdb_make_add(&dict_cdbm->cdbm, name, ksize, value, vsize) < 0)
msg_fatal("error writing %s: %m", dict_cdbm->tmp_path);
+ return (0);
#endif
}
if (msg_verbose)
msg_info("dict_cidr_lookup: %s: %s", dict->name, key);
- dict_errno = 0;
+ dict->error = 0;
if ((entry = (DICT_CIDR_ENTRY *)
cidr_match_execute(&(dict_cidr->head->cidr_info), key)) != 0)
int status;
const char *result = 0;
+ dict->error = 0;
+
/*
* Sanity check.
*/
if ((dict->flags & (DICT_FLAG_TRY1NULL | DICT_FLAG_TRY0NULL)) == 0)
msg_panic("dict_db_lookup: no DICT_FLAG_TRY1NULL | DICT_FLAG_TRY0NULL flag");
- dict_errno = 0;
memset(&db_key, 0, sizeof(db_key));
memset(&db_value, 0, sizeof(db_value));
/* dict_db_update - add or update database entry */
-static void dict_db_update(DICT *dict, const char *name, const char *value)
+static int dict_db_update(DICT *dict, const char *name, const char *value)
{
DICT_DB *dict_db = (DICT_DB *) dict;
DB *db = dict_db->db;
DBT db_value;
int status;
+ dict->error = 0;
+
/*
* Sanity check.
*/
if ((dict->flags & DICT_FLAG_LOCK)
&& myflock(dict->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_NONE) < 0)
msg_fatal("%s: unlock dictionary: %m", dict_db->dict.name);
+
+ return (status);
}
/* delete one entry from the dictionary */
int status = 1;
int flags = 0;
+ dict->error = 0;
+
/*
* Sanity check.
*/
int status = 0;
int db_function;
+ dict->error = 0;
+
#if DB_VERSION_MAJOR > 1
/*
* Initialize.
*/
- dict_errno = 0;
memset(&db_key, 0, sizeof(db_key));
memset(&db_value, 0, sizeof(db_value));
datum dbm_value;
const char *result = 0;
+ dict->error = 0;
+
/*
* Sanity check.
*/
if ((dict->flags & (DICT_FLAG_TRY1NULL | DICT_FLAG_TRY0NULL)) == 0)
msg_panic("dict_dbm_lookup: no DICT_FLAG_TRY1NULL | DICT_FLAG_TRY0NULL flag");
- dict_errno = 0;
-
/*
* Optionally fold the key.
*/
/* dict_dbm_update - add or update database entry */
-static void dict_dbm_update(DICT *dict, const char *name, const char *value)
+static int dict_dbm_update(DICT *dict, const char *name, const char *value)
{
DICT_DBM *dict_dbm = (DICT_DBM *) dict;
datum dbm_key;
datum dbm_value;
int status;
+ dict->error = 0;
+
/*
* Sanity check.
*/
if ((dict->flags & DICT_FLAG_LOCK)
&& myflock(dict->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_NONE) < 0)
msg_fatal("%s: unlock dictionary: %m", dict_dbm->dict.name);
+
+ return (status);
}
/* dict_dbm_delete - delete one entry from the dictionary */
datum dbm_key;
int status = 1;
+ dict->error = 0;
+
/*
* Sanity check.
*/
datum dbm_value;
int status;
+ dict->error = 0;
+
/*
* Acquire a shared lock.
*/
static const char *dict_debug_lookup(DICT *dict, const char *key)
{
DICT_DEBUG *dict_debug = (DICT_DEBUG *) dict;
+ DICT *real_dict = dict_debug->real_dict;
const char *result;
- result = dict_get(dict_debug->real_dict, key);
+ result = dict_get(real_dict, key);
msg_info("%s:%s lookup: \"%s\" = \"%s\"", dict->type, dict->name, key,
- result ? result : dict_errno ? "try again" : "not_found");
- return (result);
+ result ? result : real_dict->error ? "error" : "not_found");
+ DICT_ERR_VAL_RETURN(dict, real_dict->error, result);
}
/* dict_debug_update - log update operation */
-static void dict_debug_update(DICT *dict, const char *key, const char *value)
+static int dict_debug_update(DICT *dict, const char *key, const char *value)
{
DICT_DEBUG *dict_debug = (DICT_DEBUG *) dict;
+ DICT *real_dict = dict_debug->real_dict;
+ int result;
- msg_info("%s:%s update: \"%s\" = \"%s\"", dict->type, dict->name,
- key, value);
- dict_put(dict_debug->real_dict, key, value);
+ result = dict_put(real_dict, key, value);
+ msg_info("%s:%s update: \"%s\" = \"%s\": %s", dict->type, dict->name,
+ key, value, result == 0 ? "success" : real_dict->error ?
+ "error" : "failed");
+ DICT_ERR_VAL_RETURN(dict, real_dict->error, result);
}
/* dict_debug_delete - log delete operation */
static int dict_debug_delete(DICT *dict, const char *key)
{
DICT_DEBUG *dict_debug = (DICT_DEBUG *) dict;
+ DICT *real_dict = dict_debug->real_dict;
int result;
- result = dict_del(dict_debug->real_dict, key);
- msg_info("%s:%s delete: \"%s\" = \"%s\"", dict->type, dict->name, key,
- result ? "failed" : "success");
- return (result);
+ result = dict_del(real_dict, key);
+ msg_info("%s:%s delete: \"%s\": %s", dict->type, dict->name, key,
+ result == 0 ? "success" : real_dict->error ?
+ "error" : "failed");
+ DICT_ERR_VAL_RETURN(dict, real_dict->error, result);
}
/* dict_debug_sequence - log sequence operation */
const char **key, const char **value)
{
DICT_DEBUG *dict_debug = (DICT_DEBUG *) dict;
+ DICT *real_dict = dict_debug->real_dict;
int result;
- result = dict_seq(dict_debug->real_dict, function, key, value);
+ result = dict_seq(real_dict, function, key, value);
if (result == 0)
msg_info("%s:%s sequence: \"%s\" = \"%s\"", dict->type, dict->name,
*key, *value);
else
msg_info("%s:%s sequence: found EOF", dict->type, dict->name);
- return (result);
+ DICT_ERR_VAL_RETURN(dict, real_dict->error, result);
}
/* dict_debug_close - log operation */
/* dict_env_update - update environment array */
-static void dict_env_update(DICT *dict, const char *name, const char *value)
+static int dict_env_update(DICT *dict, const char *name, const char *value)
{
+ dict->error = 0;
/*
* Optionally fold the key.
}
if (setenv(name, value, 1))
msg_fatal("setenv: %m");
+
+ return (DICT_STAT_SUCCESS);
}
/* dict_env_lookup - access environment array */
static const char *dict_env_lookup(DICT *dict, const char *name)
{
- dict_errno = 0;
+ dict->error = 0;
/*
* Optionally fold the key.
/* int dict_flags;
/* DESCRIPTION
/* dict_fail_open() implements a dummy dictionary that fails
-/* all operations. The name specifies the dict_errno value.
+/* all operations. The name can be used for logging.
/* SEE ALSO
/* dict(3) generic dictionary manager
/* LICENSE
/* System library. */
#include <sys_defs.h>
-#include <stdlib.h>
/* Utility library. */
/* dict_fail_sequence - fail lookup */
static int dict_fail_sequence(DICT *dict, int unused_func,
- const char **key, const char **value)
+ const char **key, const char **value)
{
DICT_FAIL *dp = (DICT_FAIL *) dict;
- dict_errno = dp->dict_errno;
- return (1);
+ DICT_ERR_VAL_RETURN(dict, dp->dict_errno, DICT_STAT_ERROR);
}
/* dict_fail_update - fail lookup */
-static void dict_fail_update(DICT *dict, const char *unused_name,
- const char *unused_value)
+static int dict_fail_update(DICT *dict, const char *unused_name,
+ const char *unused_value)
{
DICT_FAIL *dp = (DICT_FAIL *) dict;
- dict_errno = dp->dict_errno;
+ DICT_ERR_VAL_RETURN(dict, dp->dict_errno, DICT_STAT_ERROR);
}
/* dict_fail_lookup - fail lookup */
{
DICT_FAIL *dp = (DICT_FAIL *) dict;
- dict_errno = dp->dict_errno;
- return (0);
+ DICT_ERR_VAL_RETURN(dict, dp->dict_errno, (char *) 0);
}
/* dict_fail_delete - fail delete */
{
DICT_FAIL *dp = (DICT_FAIL *) dict;
- dict_errno = dp->dict_errno;
- return (-1);
+ DICT_ERR_VAL_RETURN(dict, dp->dict_errno, DICT_STAT_ERROR);
}
/* dict_fail_close - close fail dictionary */
dp->dict.sequence = dict_fail_sequence;
dp->dict.close = dict_fail_close;
dp->dict.flags = dict_flags | DICT_FLAG_PATTERN;
- dp->dict_errno = atoi(name);
- if (dp->dict_errno == 0)
- dp->dict_errno = 1;
+ dp->dict_errno = DICT_ERR_RETRY;
dp->dict.owner.status = DICT_OWNER_TRUSTED;
return (DICT_DEBUG (&dp->dict));
}
{
DICT_HT *dict_ht = (DICT_HT *) dict;
- dict_errno = 0;
-
/*
* Optionally fold the key.
*/
vstring_strcpy(dict->fold_buf, name);
name = lowercase(vstring_str(dict->fold_buf));
}
- return (htable_find(dict_ht->table, name));
+ DICT_ERR_VAL_RETURN(dict, DICT_ERR_NONE, htable_find(dict_ht->table, name));
}
/* dict_ht_update - add or update hash-table entry */
-static void dict_ht_update(DICT *dict, const char *name, const char *value)
+static int dict_ht_update(DICT *dict, const char *name, const char *value)
{
DICT_HT *dict_ht = (DICT_HT *) dict;
HTABLE_INFO *ht;
ht = htable_enter(dict_ht->table, name, (char *) 0);
}
ht->value = saved_value;
+ DICT_ERR_VAL_RETURN(dict, DICT_ERR_NONE, DICT_STAT_SUCCESS);
}
/* dict_ht_sequence - first/next iterator */
if (ht != 0) {
*name = ht->key;
*value = ht->value;
- return (0);
+ DICT_ERR_VAL_RETURN(dict, DICT_ERR_NONE, DICT_STAT_SUCCESS);
} else {
*name = 0;
*value = 0;
- return (1);
+ DICT_ERR_VAL_RETURN(dict, DICT_ERR_NONE, DICT_STAT_FAIL);
}
}
ni_status r;
ni_id dir;
- dict_errno = 0;
-
if (msg_verbose)
msg_info("ni_lookup %s %s=%s", path, key_prop, key_value);
{
DICT_NI *d = (DICT_NI *) dict;
+ dict->error = 0;
+
/*
* Optionally fold the key.
*/
int err;
static VSTRING *buf;
+ dict->error = 0;
+
/*
* Sanity check.
*/
if ((dict->flags & (DICT_FLAG_TRY1NULL | DICT_FLAG_TRY0NULL)) == 0)
msg_panic("dict_nis_lookup: no DICT_FLAG_TRY1NULL | DICT_FLAG_TRY0NULL flag");
- dict_errno = 0;
if (dict_nis_domain == dict_nis_disabled)
return (0);
msg_warn("lookup %s, NIS domain %s, map %s: %s",
key, dict_nis_domain, dict_nis->dict.name,
dict_nis_strerror(err));
- dict_errno = DICT_ERR_RETRY;
+ dict->error = DICT_ERR_RETRY;
}
return (0);
}
int last_col;
int ch;
+ dict->error = 0;
+
/*
* Initialize.
*/
- dict_errno = 0;
if (quoted_key == 0) {
query = vstring_alloc(100);
retval = vstring_alloc(100);
msg_warn("lookup %s, NIS+ map %s: %s",
key, dict_nisplus->dict.name,
nis_sperrno(reply->status));
- dict_errno = DICT_ERR_RETRY;
+ dict->error = DICT_ERR_RETRY;
} else {
if (msg_verbose)
msg_info("%s: not found: query %s", myname, STR(query));
/* int open_flags;
/* int dict_flags;
/*
-/* void dict_put(dict, key, value)
+/* int dict_put(dict, key, value)
/* DICT *dict;
/* const char *key;
/* const char *value;
/* DICT *dict;
/* const char *key;
/*
-/* void dict_seq(dict, func, key, value)
+/* int dict_seq(dict, func, key, value)
/* DICT *dict;
/* int func;
/* const char **key;
/* With file-based maps, flush I/O buffers to file after each update.
/* Thus feature is not supported with some file-based dictionaries.
/* .IP DICT_FLAG_NO_REGSUB
-/* Disallow regular expression substitution from left-hand side data
+/* Disallow regular expression substitution from left-hand side data
/* into the right-hand side.
/* .IP DICT_FLAG_NO_PROXY
/* Disallow access through the \fBproxymap\fR service.
/* or if the result is to survive multiple table lookups.
/*
/* dict_put() stores the specified key and value into the named
-/* dictionary.
+/* dictionary. A zero (DICT_STAT_SUCCESS) result means the
+/* update was made.
/*
-/* dict_del() removes a dictionary entry, and returns zero
-/* in case of success.
+/* dict_del() removes a dictionary entry, and returns
+/* DICT_STAT_SUCCESS in case of success.
/*
/* dict_seq() iterates over all members in the named dictionary.
/* func is define DICT_SEQ_FUN_FIRST (select first member) or
-/* DICT_SEQ_FUN_NEXT (select next member). A zero result means
-/* that an entry was found.
+/* DICT_SEQ_FUN_NEXT (select next member). A zero (DICT_STAT_SUCCESS)
+/* result means that an entry was found.
/*
/* dict_close() closes the specified dictionary and cleans up the
/* associated data structures.
/* DIAGNOSTICS
/* Fatal error: open error, unsupported dictionary type, attempt to
/* update non-writable dictionary.
+/*
+/* The lookup routine returns non-null when the request is
+/* satisfied. The update, delete and sequence routines return
+/* zero (DICT_STAT_SUCCESS) when the request is satisfied.
+/* The dict->errno value is non-zero only when the last operation
+/* was not satisfied due to a dictionary access error. This
+/* can have the following values:
+/* .IP DICT_ERR_NONE(zero)
+/* There was no dictionary access error. For example, the
+/* request was satisfied, the requested information did not
+/* exist in the dictionary, or the information already existed
+/* when it should not exist (collision).
+/* .IP DICT_ERR_RETRY(<0)
+/* The dictionary was temporarily unavailable. This can happen
+/* with network-based services.
+/* .IP DICT_ERR_CONFIG(<0)
+/* The dictionary was unavailable due to a configuration error.
+/* .PP
+/* Generally, a program is expected to test the function result
+/* value for "success" first. If the operation was not successful,
+/* a program is expected to test for a non-zero dict->error
+/* status to distinguish between a data notfound/collision
+/* condition or a dictionary access error.
/* LICENSE
/* .ad
/* .fi
DICT_PCRE_EXPAND_CONTEXT ctxt;
int nesting = 0;
- dict_errno = 0;
+ dict->error = 0;
if (msg_verbose)
msg_info("dict_pcre_lookup: %s: %s", dict->name, lookup_string);
int error;
int nesting = 0;
- dict_errno = 0;
+ dict->error = 0;
if (msg_verbose)
msg_info("dict_regexp_lookup: %s: %s", dict->name, lookup_string);
datum dbm_value;
const char *result = 0;
+ dict->error = 0;
+
/*
* Sanity check.
*/
if ((dict->flags & (DICT_FLAG_TRY1NULL | DICT_FLAG_TRY0NULL)) == 0)
msg_panic("dict_sdbm_lookup: no DICT_FLAG_TRY1NULL | DICT_FLAG_TRY0NULL flag");
- dict_errno = 0;
-
/*
* Optionally fold the key.
*/
/* dict_sdbm_update - add or update database entry */
-static void dict_sdbm_update(DICT *dict, const char *name, const char *value)
+static int dict_sdbm_update(DICT *dict, const char *name, const char *value)
{
DICT_SDBM *dict_sdbm = (DICT_SDBM *) dict;
datum dbm_key;
datum dbm_value;
int status;
+ dict->error = 0;
+
/*
* Sanity check.
*/
if ((dict->flags & DICT_FLAG_LOCK)
&& myflock(dict->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_NONE) < 0)
msg_fatal("%s: unlock dictionary: %m", dict_sdbm->dict.name);
+
+ return (status);
}
/* dict_sdbm_delete - delete one entry from the dictionary */
datum dbm_key;
int status = 1;
+ dict->error = 0;
+
/*
* Sanity check.
*/
DICT_SDBM *dict_sdbm = (DICT_SDBM *) dict;
datum dbm_key;
datum dbm_value;
+ int status;
+
+ dict->error = 0;
/*
* Acquire a shared lock.
* Copy the value so that it is guaranteed null terminated.
*/
*value = SCOPY(dict_sdbm->val_buf, dbm_value.dptr, dbm_value.dsize);
+ status = 0;
} else {
/*
*/
if (sdbm_error(dict_sdbm->dbm))
msg_fatal("error seeking %s: %m", dict_sdbm->dict.name);
- return (1); /* no error: eof/not found
+ status = 1; /* no error: eof/not found
* (should not happen!) */
}
} else {
*/
if (sdbm_error(dict_sdbm->dbm))
msg_fatal("error seeking %s: %m", dict_sdbm->dict.name);
- return (1); /* no error: eof/not found */
+ status = 1; /* no error: eof/not found */
}
/*
&& myflock(dict->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_NONE) < 0)
msg_fatal("%s: unlock dictionary: %m", dict_sdbm->dict.name);
- return (0);
+ return (status);
}
/* dict_sdbm_close - disassociate from data base */
static const char *dict_static_lookup(DICT *dict, const char *unused_name)
{
- dict_errno = 0;
-
- return (dict->name);
+ DICT_ERR_VAL_RETURN(dict, DICT_ERR_NONE, dict->name);
}
/* dict_static_close - close static dictionary */
char *start;
int last_ch;
-#define RETURN(errval, result) { dict_errno = errval; return (result); }
+#define RETURN(errval, result) { dict->error = errval; return (result); }
if (msg_verbose)
msg_info("%s: key %s", myname, key);
{
DICT_TCP *dict_tcp;
- dict_errno = 0;
-
/*
* Sanity checks.
*/
vstream_printf("%s: deleted\n", key);
} else if (strcmp(cmd, "get") == 0 && key && !value) {
if ((value = dict_get(dict, key)) == 0) {
- vstream_printf("%s: %s\n", key, dict_errno ?
+ vstream_printf("%s: %s\n", key, dict->error ?
"error" : "not found");
} else {
vstream_printf("%s=%s\n", key, value);
}
} else if (strcmp(cmd, "put") == 0 && key && value) {
- /* XXX dict_put returns void, so dict_memcache sets dict_errno. */
- dict_errno = 0;
- dict_put(dict, key, value);
- if (dict_errno)
- vstream_printf("%s: error\n", key);
+ if (dict_put(dict, key, value) != 0)
+ vstream_printf("%s: %s\n", key, dict->error ?
+ "error" : "not updated");
else
vstream_printf("%s=%s\n", key, value);
} else if (strcmp(cmd, "first") == 0 && !key && !value) {
if (dict_seq(dict, DICT_SEQ_FUN_FIRST, &key, &value) == 0)
vstream_printf("%s=%s\n", key, value);
else
- vstream_printf("%s\n", dict_errno ?
+ vstream_printf("%s\n", dict->error ?
"error" : "not found");
} else if (strcmp(cmd, "next") == 0 && !key && !value) {
if (dict_seq(dict, DICT_SEQ_FUN_NEXT, &key, &value) == 0)
vstream_printf("%s=%s\n", key, value);
else
- vstream_printf("%s\n", dict_errno ?
+ vstream_printf("%s\n", dict->error ?
"error" : "not found");
} else if (strcmp(cmd, "flags") == 0 && !key && !value) {
vstream_printf("dict flags %s\n",
HTABLE *table; /* in-memory hash */
HTABLE_INFO **info; /* for iterator */
HTABLE_INFO **cursor; /* ditto */
-} DICT_THASH;
+} DICT_THASH;
#define STR vstring_str
DICT_THASH *dict_thash = (DICT_THASH *) dict;
const char *result = 0;
- dict_errno = 0;
-
/*
* Optionally fold the key.
*/
*/
result = htable_find(dict_thash->table, name);
- return (result);
+ DICT_ERR_VAL_RETURN(dict, DICT_ERR_NONE, result);
}
/* dict_thash_sequence - traverse the dictionary */
const char *myname = "dict_thash_sequence";
DICT_THASH *dict_thash = (DICT_THASH *) dict;
- dict_errno = 0;
-
/*
* Determine and execute the seek function.
*/
if (dict_thash->cursor[0]) {
*key = dict_thash->cursor[0]->key;
*value = dict_thash->cursor[0]->value;
- return (0);
+ DICT_ERR_VAL_RETURN(dict, DICT_ERR_NONE, DICT_STAT_SUCCESS);
} else {
- return (1);
+ *key = 0;
+ *value = 0;
+ DICT_ERR_VAL_RETURN(dict, DICT_ERR_NONE, DICT_STAT_FAIL);
}
}
static VSTRING *buf;
static int sanity_checked;
- dict_errno = 0;
+ dict->error = 0;
/*
* Optionally fold the key.
errno = 0;
if (getpwuid(0) == 0) {
msg_warn("cannot access UNIX password database: %m");
- dict_errno = DICT_ERR_RETRY;
+ dict->error = DICT_ERR_RETRY;
}
}
return (0);
char **cpp;
static int sanity_checked;
- dict_errno = 0;
+ dict->error = 0;
/*
* Optionally fold the key.
errno = 0;
if (getgrgid(0) == 0) {
msg_warn("cannot access UNIX group database: %m");
- dict_errno = DICT_ERR_RETRY;
+ dict->error = DICT_ERR_RETRY;
}
}
return (0);
};
struct dict_unix_lookup *lp;
- dict_errno = 0;
-
dict_unix = (DICT_UNIX *) dict_alloc(DICT_TYPE_UNIX, map,
sizeof(*dict_unix));
for (lp = dict_unix_lookup; /* void */ ; lp++) {
/* foo.com. If this flag is cleared, foo.com matches itself
/* only, and .foo.com matches any name below the domain foo.com.
/* .IP MATCH_FLAG_RETURN
-/* Request that match_list_match() returns zero (with dict_errno
-/* set) instead of raising a fatal run-time error.
+/* Request that match_list_match() logs a warning and returns
+/* zero (with list->error set to a non-zero dictionary error
+/* code) instead of raising a fatal run-time error.
/* .RE
/* Specify MATCH_FLAG_NONE to request none of the above.
/* The pattern_list argument specifies a list of patterns. The third
/* match_list_free() releases storage allocated by match_list_init().
/* DIAGNOSTICS
/* Fatal error: unable to open or read a match_list file; invalid
-/* match_list pattern.
+/* match_list pattern.
/* SEE ALSO
/* host_match(3) match hosts by name or by address
/* LICENSE
#include <stringops.h>
#include <argv.h>
#include <dict.h>
-#include <match_ops.h>
#include <match_list.h>
/* Application-specific */
-struct MATCH_LIST {
- int flags; /* processing options */
- ARGV *patterns; /* one pattern each */
- int match_count; /* match function/argument count */
- MATCH_LIST_FN *match_func; /* match functions */
- const char **match_args; /* match arguments */
-};
-
#define MATCH_DICTIONARY(pattern) \
((pattern)[0] != '[' && strchr((pattern), ':') != 0)
list->match_args[i] = va_arg(ap, const char *);
va_end(ap);
- dict_errno = 0;
+ list->error = 0;
for (cpp = list->patterns->argv; (pat = *cpp) != 0; cpp++) {
for (match = 1; *pat == '!'; pat++)
match = !match;
for (i = 0; i < list->match_count; i++)
- if (list->match_func[i] (list->flags, list->match_args[i], pat))
+ if (list->match_func[i] (list, list->match_args[i], pat))
return (match);
- else if (dict_errno != 0)
+ else if (list->error != 0)
return (0);
}
if (msg_verbose)
/*
* Utility library.
*/
-#include <match_ops.h>
+#include <argv.h>
/*
* External interface.
*/
typedef struct MATCH_LIST MATCH_LIST;
-typedef int (*MATCH_LIST_FN) (int, const char *, const char *);
+
+typedef int (*MATCH_LIST_FN) (MATCH_LIST *, const char *, const char *);
+
+struct MATCH_LIST {
+ int flags; /* processing options */
+ ARGV *patterns; /* one pattern each */
+ int match_count; /* match function/argument count */
+ MATCH_LIST_FN *match_func; /* match functions */
+ const char **match_args; /* match arguments */
+ int error; /* last operation */
+};
+
+#define MATCH_FLAG_NONE 0
+#define MATCH_FLAG_PARENT (1<<0)
+#define MATCH_FLAG_RETURN (1<<1)
+#define MATCH_FLAG_ALL (MATCH_FLAG_PARENT | MATCH_FLAG_RETURN)
extern MATCH_LIST *match_list_init(int, const char *, int,...);
extern int match_list_match(MATCH_LIST *,...);
extern void match_list_free(MATCH_LIST *);
+ /*
+ * The following functions are not part of the public interface. These
+ * functions may be called only through match_list_match().
+ */
+extern int match_string(MATCH_LIST *, const char *, const char *);
+extern int match_hostname(MATCH_LIST *, const char *, const char *);
+extern int match_hostaddr(MATCH_LIST *, const char *, const char *);
+
/* LICENSE
/* .ad
/* .fi
/*--*/
#endif
+
/* SUMMARY
/* simple string or host pattern matching
/* SYNOPSIS
-/* #include <match_ops.h>
+/* #include <match_list.h>
/*
-/* int match_string(flags, string, pattern)
-/* int flags;
+/* int match_string(list, string, pattern)
+/* MATCH_LIST *list;
/* const char *string;
/* const char *pattern;
/*
-/* int match_hostname(flags, name, pattern)
-/* int flags;
+/* int match_hostname(list, name, pattern)
+/* MATCH_LIST *list;
/* const char *name;
/* const char *pattern;
/*
-/* int match_hostaddr(flags, addr, pattern)
-/* int flags;
+/* int match_hostaddr(list, addr, pattern)
+/* MATCH_LIST *list;
/* const char *addr;
/* const char *pattern;
/* DESCRIPTION
/* the domain foo.com. If this flag is cleared, foo.com matches itself
/* only, and .foo.com matches any name below the domain foo.com.
/* .IP MATCH_FLAG_RETURN
-/* Return "not found" and set dict_errno, instead of raising
-/* a fatal run-time error.
+/* Log a warning, return "not found", and set list->error to
+/* a non-zero dictionary error code, instead of raising a fatal
+/* run-time error.
/* .RE
/* Specify MATCH_FLAG_NONE to request none of the above.
/*
#include <mymalloc.h>
#include <split_at.h>
#include <dict.h>
-#include <match_ops.h>
+#include <match_list.h>
#include <stringops.h>
#include <cidr_match.h>
/* match_error - return or raise fatal error */
-static int match_error(int flags, const char *fmt,...)
+static int match_error(MATCH_LIST *list, const char *fmt,...)
{
VSTRING *buf = vstring_alloc(100);
va_list ap;
va_start(ap, fmt);
vstring_vsprintf(buf, fmt, ap);
va_end(ap);
- if (flags & MATCH_FLAG_RETURN) {
+ if (list->flags & MATCH_FLAG_RETURN) {
msg_warn("%s", vstring_str(buf));
} else {
msg_fatal("%s", vstring_str(buf));
/* match_string - match a string literal */
-int match_string(int flags, const char *string, const char *pattern)
+int match_string(MATCH_LIST *list, const char *string, const char *pattern)
{
const char *myname = "match_string";
DICT *dict;
msg_panic("%s: unknown dictionary: %s", myname, pattern);
if (dict_get(dict, string) != 0)
return (1);
- if (dict_errno != 0)
- return (match_error(flags, "%s:%s: table lookup problem",
+ if ((list->error = dict->error) != 0)
+ return (match_error(list, "%s:%s: table lookup problem",
dict->type, dict->name));
return (0);
}
/* match_hostname - match a host by name */
-int match_hostname(int flags, const char *name, const char *pattern)
+int match_hostname(MATCH_LIST *list, const char *name, const char *pattern)
{
const char *myname = "match_hostname";
const char *pd;
match ? "found" : "notfound");
if (match != 0)
break;
- if (dict_errno != 0)
- return (match_error(flags, "%s:%s: table lookup problem",
+ if ((list->error = dict->error) != 0)
+ return (match_error(list, "%s:%s: table lookup problem",
dict->type, dict->name));
}
if ((next = strchr(entry + 1, '.')) == 0)
break;
- if (flags & MATCH_FLAG_PARENT)
+ if (list->flags & MATCH_FLAG_PARENT)
next += 1;
}
return (match);
* See if the pattern is a parent domain of the hostname.
*/
else {
- if (flags & MATCH_FLAG_PARENT) {
+ if (list->flags & MATCH_FLAG_PARENT) {
pd = name + strlen(name) - strlen(pattern);
if (pd > name && pd[-1] == '.' && strcasecmp(pd, pattern) == 0)
return (1);
/* match_hostaddr - match host by address */
-int match_hostaddr(int flags, const char *addr, const char *pattern)
+int match_hostaddr(MATCH_LIST *list, const char *addr, const char *pattern)
{
const char *myname = "match_hostaddr";
char *saved_patt;
msg_panic("%s: unknown dictionary: %s", myname, pattern);
if (dict_get(dict, addr) != 0)
return (1);
- if (dict_errno != 0)
- return (match_error(flags, "%s:%s: table lookup problem",
+ if ((list->error = dict->error) != 0)
+ return (match_error(list, "%s:%s: table lookup problem",
dict->type, dict->name));
return (0);
}
err = cidr_match_parse(&match_info, saved_patt, (VSTRING *) 0);
myfree(saved_patt);
if (err != 0) {
- dict_errno = DICT_ERR_CONFIG;
- rc = match_error(flags, "%s", vstring_str(err));
+ list->error = DICT_ERR_CONFIG;
+ rc = match_error(list, "%s", vstring_str(err));
vstring_free(err);
return (rc);
}
+++ /dev/null
-#ifndef _MATCH_OPS_H_INCLUDED_
-#define _MATCH_OPS_H_INCLUDED_
-
-/*++
-/* NAME
-/* match_ops 3h
-/* SUMMARY
-/* simple string or host pattern matching
-/* SYNOPSIS
-/* #include <match_ops.h>
-/* DESCRIPTION
-/* .nf
-
- /*
- * Utility library.
- */
-#include <dict.h>
-
- /*
- * External interface.
- */
-#define MATCH_FLAG_NONE 0
-#define MATCH_FLAG_PARENT (1<<0)
-#define MATCH_FLAG_RETURN (1<<1)
-#define MATCH_FLAG_ALL (MATCH_FLAG_PARENT | MATCH_FLAG_RETURN)
-
-extern int match_string(int, const char *, const char *);
-extern int match_hostname(int, const char *, const char *);
-extern int match_hostaddr(int, const char *, const char *);
-
-/* LICENSE
-/* .ad
-/* .fi
-/* The Secure Mailer license must be distributed with this software.
-/* AUTHOR(S)
-/* Wietse Venema
-/* IBM T.J. Watson Research
-/* P.O. Box 704
-/* Yorktown Heights, NY 10598, USA
-/*--*/
-
-#endif
/* DESCRIPTION
/* .nf
+/*
+ * System library.
+ */
+#include <time.h>
+
/*
* External interface.
*/
extern void msg_error_clear(void);
extern MSG_CLEANUP_FN msg_cleanup(MSG_CLEANUP_FN);
+extern void PRINTFLIKE(4, 5) msg_rate_delay(time_t *, int,
+ void (*log_fn) (const char *,...),
+ const char *,...);
+
/* LICENSE
/* .ad
/* .fi
--- /dev/null
+/*++
+/* NAME
+/* msg_rate_delay 3
+/* SUMMARY
+/* diagnostic interface
+/* SYNOPSIS
+/* #include <msg.h>
+/*
+/* void msg_rate_delay(stamp, delay, log_fn, fmt, ...)
+/* time_t *stamp;
+/* int delay;
+/* void (*log_fn)(const char *fmt, ...);
+/* const char *fmt;
+/* DESCRIPTION
+/* msg_rate_delay() produces log output at a reduced rate: no
+/* more than one message per 'delay' seconds. It discards log
+/* output that would violate the output rate policy.
+/*
+/* This is typically used to log errors accessing a cache with
+/* high-frequency access but low-value information, to avoid
+/* spamming the logfile with the same kind of message.
+/*
+/* Arguments:
+/* .IP stamp
+/* Time stamp of last log output; specify a zero time stamp
+/* on the first call. This is an input-output parameter.
+/* This parameter is ignored when verbose logging is enabled
+/* or when the delay value is zero.
+/* .IP delay
+/* The minimum time between log outputs; specify zero to log
+/* all output for debugging purposes. This parameter is ignored
+/* when verbose logging is enabled.
+/* .IP log_fn
+/* The function that produces log output. Typically, this will
+/* be msg_info() or msg_warn().
+/* .IP fmt
+/* Format string as used with msg(3) routines.
+/* SEE ALSO
+/* msg(3) diagnostics interface
+/* DIAGNOSTICS
+/* Fatal errors: memory allocation problem.
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*--*/
+
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <time.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <vstring.h>
+#include <events.h>
+
+/* SLMs. */
+
+#define STR(x) vstring_str(x)
+
+/* msg_rate_delay - rate-limit message logging */
+
+void msg_rate_delay(time_t *stamp, int delay,
+ void (*log_fn) (const char *,...),
+ const char *fmt,...)
+{
+ const char *myname = "msg_rate_delay";
+ static time_t saved_event_time;
+ time_t now;
+ VSTRING *buf;
+ va_list ap;
+
+ /*
+ * Sanity check.
+ */
+ if (delay < 0)
+ msg_panic("%s: bad message rate delay: %d", myname, delay);
+
+ /*
+ * This function may be called frequently. Avoid an unnecessary syscall
+ * if possible. Deal with the possibility that a program does not use the
+ * events(3) engine, so that event_time() always produces the same
+ * result.
+ */
+ if (msg_verbose == 0 && delay > 0) {
+ if (saved_event_time == 0)
+ now = saved_event_time = event_time();
+ else if ((now = event_time()) == saved_event_time)
+ now = time((time_t *) 0);
+
+ /*
+ * Don't log if time is too early.
+ */
+ if (*stamp + delay > now)
+ return;
+ *stamp = now;
+ }
+
+ /*
+ * OK to log. This is a low-rate event, so we can afford some overhead.
+ */
+ buf = vstring_alloc(100);
+ va_start(ap, fmt);
+ vstring_vsprintf(buf, fmt, ap);
+ va_end(ap);
+ log_fn("%s", STR(buf));
+ vstring_free(buf);
+}
+
+#ifdef TEST
+
+ /*
+ * Proof-of-concept test program: log messages but skip messages during a
+ * two-second gap.
+ */
+#include <unistd.h>
+
+int main(int argc, char **argv)
+{
+ int n;
+ time_t stamp = 0;
+
+ for (n = 0; n < 6; n++) {
+ msg_rate_delay(&stamp, 2, msg_info, "text here %d", n);
+ sleep(1);
+ }
+ return (0);
+}
+
+#endif
mailbox_res = mail_addr_find(virtual_mailbox_maps, state.msg_attr.user,
IGNORE_EXTENSION);
if (mailbox_res == 0) {
- if (dict_errno == 0)
+ if (virtual_mailbox_maps->error == 0)
return (NO);
msg_warn("table %s: lookup %s: %m", virtual_mailbox_maps->title,
state.msg_attr.user);