Bitrot: Berkeley DB 18 is like Berkeley DB 6. Yasuhiro
Kimura. File: util/dict_db.c.
+
+20220204
+
+ Updated collate.pl script for better tracking when a
+ Milter rejects, discards, or quarantines a message. Viktor
+ Dukhovni. File: auxiliary/collate/collate.pl.
+
+20220212
+
+ Cleanup: removed WISHLIST items that were recently fixed.
+
+20220217
+
+ Typo: "pcre2 --libs" should be "pcre2 --libs8". Reported by
+ Carlos Velasco. File proto/PCRE_README.html.
+
+ Future proofing: added comments on the purpose of address
+ sanitization. File: showq/showq.c.
+
+20220220
+
+ Added a hash_fnvz() function to eliminate unnecessary strlen()
+ calls, and added regression tests. File: util/hash_fnv.c,
+ util/htable.c.
+
+ Cleanup: unused initialization. File: util/make_dirs.c
$ make -f Makefile.init makefiles \
"CCARGS=-DHAS_PCRE=2 `pcre2-config --cflags`" \
- "AUXLIBS_PCRE=`pcre2-config --libs`"
+ "AUXLIBS_PCRE=`pcre2-config --libs8`"
$ make
To build Postfix explicitly with a legacy pcre library (all Postfix versions):
Major changes - lmdb support
----------------------------
-[Feature 20210605] Overhauled the LMDB client implementation, and
+[Feature 20210605] Overhauled the LMDB client's error handling, and
added integration tests for future-proofing. There are no visible
changes in documented behavior.
Disable -DSNAPSHOT and -DNONPROD in makedefs.
- Alias htable(3) calls to equivalent binhash(3) calls,
- and obsolete the htable(3) module.
-
FILTER_README needs some text on multi-instance implementations,
and existing multi-instance references need to be updated.
A smart query service for live Postfix tables that outputs JSON?
+ JSON logging?
+
+ default_transport_maps? This would simplify configuration.
+
Add a pointer to
http://mmogilvi.users.sourceforge.net/software/oauthbearer.html
in documentation or on-line howtos.
Maybe don't whitelist a client that has maxed out its
per-MTA connection count limit.
- Inline support for pcre:{/pattern/=action, ...} and ditto
- support for regexp: and cidr: tables. Factor out and reuse
- code that already exists in inline: and other tables.
-
Log command=good/bad statistics in postscreen?
smtpd_checks tests either must use a DNS dummy resolver
Cleanup: make DNSBL query format configurable beyond the
client's reversed IP address.
- With 'final delivery' in the LMTP client, need an option
- to also add delivered-to and other pipe(8) features. This
- requires making mail_copy() functionality available in
- non-mailbox context.
-
- Cleanup: modernize the "add missing From: header" code, to
- ``phrase <addr>'' form. Most likely, quote the entire phrase
- if it contains any text that is special, then rfc822_externalize
- the whole thing.
-
- SMTP server: make the server_addr and server_port available
- to policy server, Dovecot, and perhaps Milters.
-
- Med: local and remote source port and IP address for smtpd
- policy hook.
-
Maybe change maps_rbl_reject_code default to 521, and
update wording in STRESS_README.
Plan for time_t larger than long, or wait for LP64 to
dominate the world?
- Make "AUTH=<>" appendage to MAIL FROM configurable, enabled
- by default.
-
- To support ternary operator without a huge parsing effort,
- consider ${value?{xxx}:{yyy}} where ${name} is existing
- syntax, and where ?{text} and :{text} are new syntax that
- is unlikely to break existing configurations. Or perhaps
- it's just too ugly.
-
Write delivery rate delay example (which _README?) and auth
failure cache example (SASL_README). Then include them in
SOHO_README.
my %transaction;
my $i = 0;
my %seqno;
+my %deleted;
my %isagent = map { ($_, 1) } @agents;
if (m{\Gconnect from }gc) {
# Start new log
$smtpd{$pid}->{"log"} = $_; next;
+ undef $smtpd{$pid}->{"qid"};
}
$smtpd{$pid}->{"log"} .= $_;
my $qid = $smtpd{$pid}->{"qid"};
$transaction{$qid} .= $_
if (defined($qid) && exists $transaction{$qid});
- delete $smtpd{$pid} if (m{\Gdisconnect from}gc);
+ if (m{\Gdisconnect from}gc) {
+ if (!defined($qid)) {
+ print $smtpd{$pid}->{"log"}, "\n";
+ } elsif (delete $deleted{$qid}) {
+ print delete $transaction{$qid}, "\n";
+ }
+ delete $smtpd{$pid};
+ }
next;
}
my $qid = "$inst/$1";
$transaction{$qid} .= $_;
$seqno{$qid} = ++$i if (! exists $seqno{$qid});
+ if (m{\G(?:milter(?:-(?:header|body))?-)?(?:reject|discard|hold): }) {
+ $deleted{$qid} = 1;
+ }
next;
}
<pre>
$ make -f Makefile.init makefiles \
"CCARGS=-DHAS_PCRE=2 `pcre2-config --cflags`" \
- "<a href="PCRE_README.html">AUXLIBS_PCRE</a>=`pcre2-config --libs`"
+ "<a href="PCRE_README.html">AUXLIBS_PCRE</a>=`pcre2-config --libs8`"
$ make
</pre>
</blockquote>
<b><a href="postconf.5.html#tlsproxy_client_scert_verifydepth">tlsproxy_client_scert_verifydepth</a> ($<a href="postconf.5.html#smtp_tls_scert_verifydepth">smtp_tls_scert_verifydepth</a>)</b>
The verification depth for remote TLS server certificates.
- <b><a href="postconf.5.html#tlsproxy_client_level">tlsproxy_client_level</a> ($<a href="postconf.5.html#smtp_tls_security_level">smtp_tls_security_level</a>)</b>
- The default TLS security level for the Postfix <a href="tlsproxy.8.html"><b>tlsproxy</b>(8)</a>
- client.
-
- <b><a href="postconf.5.html#tlsproxy_client_policy">tlsproxy_client_policy</a> ($<a href="postconf.5.html#smtp_tls_policy_maps">smtp_tls_policy_maps</a>)</b>
- Optional lookup tables with the Postfix <a href="tlsproxy.8.html"><b>tlsproxy</b>(8)</a> client TLS
- security policy by next-hop destination.
-
<b><a href="postconf.5.html#tlsproxy_client_use_tls">tlsproxy_client_use_tls</a> ($<a href="postconf.5.html#smtp_use_tls">smtp_use_tls</a>)</b>
Opportunistic mode: use TLS when a remote server announces TLS
support.
usage policy by next-hop destination and by remote TLS server
hostname.
+ Available in Postfix version 3.4-3.6:
+
+ <b><a href="postconf.5.html#tlsproxy_client_level">tlsproxy_client_level</a> ($<a href="postconf.5.html#smtp_tls_security_level">smtp_tls_security_level</a>)</b>
+ The default TLS security level for the Postfix <a href="tlsproxy.8.html"><b>tlsproxy</b>(8)</a>
+ client.
+
+ <b><a href="postconf.5.html#tlsproxy_client_policy">tlsproxy_client_policy</a> ($<a href="postconf.5.html#smtp_tls_policy_maps">smtp_tls_policy_maps</a>)</b>
+ Optional lookup tables with the Postfix <a href="tlsproxy.8.html"><b>tlsproxy</b>(8)</a> client TLS
+ security policy by next-hop destination.
+
Available in Postfix version 3.7 and later:
<b><a href="postconf.5.html#tlsproxy_client_security_level">tlsproxy_client_security_level</a> ($<a href="postconf.5.html#smtp_tls_security_level">smtp_tls_security_level</a>)</b>
value.
.IP "\fBtlsproxy_client_scert_verifydepth ($smtp_tls_scert_verifydepth)\fR"
The verification depth for remote TLS server certificates.
-.IP "\fBtlsproxy_client_level ($smtp_tls_security_level)\fR"
-The default TLS security level for the Postfix \fBtlsproxy\fR(8)
-client.
-.IP "\fBtlsproxy_client_policy ($smtp_tls_policy_maps)\fR"
-Optional lookup tables with the Postfix \fBtlsproxy\fR(8) client TLS
-security policy by next\-hop destination.
.IP "\fBtlsproxy_client_use_tls ($smtp_use_tls)\fR"
Opportunistic mode: use TLS when a remote server announces TLS
support.
usage policy by next\-hop destination and by remote TLS server
hostname.
.PP
+Available in Postfix version 3.4\-3.6:
+.IP "\fBtlsproxy_client_level ($smtp_tls_security_level)\fR"
+The default TLS security level for the Postfix \fBtlsproxy\fR(8)
+client.
+.IP "\fBtlsproxy_client_policy ($smtp_tls_policy_maps)\fR"
+Optional lookup tables with the Postfix \fBtlsproxy\fR(8) client TLS
+security policy by next\-hop destination.
+.PP
Available in Postfix version 3.7 and later:
.IP "\fBtlsproxy_client_security_level ($smtp_tls_security_level)\fR"
The default TLS security level for the Postfix \fBtlsproxy\fR(8)
<pre>
$ make -f Makefile.init makefiles \
"CCARGS=-DHAS_PCRE=2 `pcre2-config --cflags`" \
- "AUXLIBS_PCRE=`pcre2-config --libs`"
+ "AUXLIBS_PCRE=`pcre2-config --libs8`"
$ make
</pre>
</blockquote>
postlogd
proxied
raf
+fnvz
* Patches change both the patchlevel and the release date. Snapshots have no
* patchlevel; they change the release date only.
*/
-#define MAIL_RELEASE_DATE "20220204"
+#define MAIL_RELEASE_DATE "20220220"
#define MAIL_VERSION_NUMBER "3.8"
#ifdef SNAPSHOT
if (*start == 0)
start = var_empty_addr;
quote_822_local(printable_quoted_addr, start);
+ /* For consistency with REC_TYPE_RCPT below. */
printable(STR(printable_quoted_addr), '?');
if (sender_seen++ > 0) {
msg_warn("%s: duplicate sender address: %s "
if (*start == 0) /* can't happen? */
start = var_empty_addr;
quote_822_local(printable_quoted_addr, start);
+ /* For consistency with recipients in bounce logfile. */
printable(STR(printable_quoted_addr), '?');
if (dup_filter == 0
|| htable_locate(dup_filter, STR(printable_quoted_addr)) == 0)
/* value.
/* .IP "\fBtlsproxy_client_scert_verifydepth ($smtp_tls_scert_verifydepth)\fR"
/* The verification depth for remote TLS server certificates.
-/* .IP "\fBtlsproxy_client_level ($smtp_tls_security_level)\fR"
-/* The default TLS security level for the Postfix \fBtlsproxy\fR(8)
-/* client.
-/* .IP "\fBtlsproxy_client_policy ($smtp_tls_policy_maps)\fR"
-/* Optional lookup tables with the Postfix \fBtlsproxy\fR(8) client TLS
-/* security policy by next-hop destination.
/* .IP "\fBtlsproxy_client_use_tls ($smtp_use_tls)\fR"
/* Opportunistic mode: use TLS when a remote server announces TLS
/* support.
/* usage policy by next-hop destination and by remote TLS server
/* hostname.
/* .PP
+/* Available in Postfix version 3.4-3.6:
+/* .IP "\fBtlsproxy_client_level ($smtp_tls_security_level)\fR"
+/* The default TLS security level for the Postfix \fBtlsproxy\fR(8)
+/* client.
+/* .IP "\fBtlsproxy_client_policy ($smtp_tls_policy_maps)\fR"
+/* Optional lookup tables with the Postfix \fBtlsproxy\fR(8) client TLS
+/* security policy by next-hop destination.
+/* .PP
/* Available in Postfix version 3.7 and later:
/* .IP "\fBtlsproxy_client_security_level ($smtp_tls_security_level)\fR"
/* The default TLS security level for the Postfix \fBtlsproxy\fR(8)
valid_utf8_string ip_match base32_code msg_rate_delay netstring \
vstream timecmp dict_cache midna_domain casefold strcasecmp_utf8 \
vbuf_print split_qnameval vstream msg_logger byte_mask \
- known_tcp_ports dict_stream find_inet binhash
+ known_tcp_ports dict_stream find_inet binhash hash_fnv
PLUGIN_MAP_SO = $(LIB_PREFIX)pcre$(LIB_SUFFIX)
HTABLE_FIX = NORANDOMIZE=1
LIB_DIR = ../../lib
$(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS)
mv junk $@.o
+hash_fnv: $(LIB)
+ mv $@.o junk
+ $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS)
+ mv junk $@.o
+
unix_recv_fd: $(LIB)
mv $@.o junk
$(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS)
binhash_test: binhash /usr/share/dict/words
$(SHLIB_ENV) ${VALGRIND} ./binhash < /usr/share/dict/words
+hash_fnv_test: hash_fnv
+ $(SHLIB_ENV) ${VALGRIND} ./hash_fnv
+
hex_code_test: hex_code
$(SHLIB_ENV) ${VALGRIND} ./hex_code
/* HASH_FNV_T hash_fnv(
/* const void *src,
/* size_t len)
+/*
+/* HASH_FNV_T hash_fnvz(
+/* const char *src)
/* DESCRIPTION
/* hash_fnv() implements a modified FNV type 1a hash function.
/*
+/* hash_fnvz() provides the same functionality for null-terminated
+/* strings, avoiding an unnecessary strlen() call.
+/*
/* To thwart collision attacks, the hash function is seeded
/* once from /dev/urandom, and if that is unavailable, from
/* wallclock time, monotonic system clocks, and the process
/*
/* This function implements a workaround for a "sticky state"
/* problem with FNV hash functions: when an input produces a
-/* zero intermediate hash state, and the next input byte is
-/* zero, then the operations "hash ^= 0" and "hash *= FNV_prime"
-/* would not change the hash value. To avoid this, hash_fnv()
-/* adds 1 to each input byte. Compile with -DSTRICT_FNV1A to
-/* get the standard behavior.
+/* zero hash state, and the next input byte is zero, then the
+/* hash state would not change. To avoid this, hash_fnv() adds
+/* 1 to each input value. Compile with -DSTRICT_FNV1A to get
+/* the standard behavior.
/*
/* The default HASH_FNV_T result type is uint64_t. When compiled
/* with -DUSE_FNV_32BIT, the result type is uint32_t. On ancient
#define FNV_offset_basis 0xcbf29ce484222325ULL
#endif
+ /*
+ * Workaround for the sticky all-zero hash state: when the next input byte
+ * is zero, then the operations "hash ^= 0" and "hash *= FNV_prime" would
+ * not change the hash state. To avoid that, add 1 to the every input value.
+ */
+#ifdef STRICT_FNV1A
+#define HASH_FNV_NEW_BITS(new_bits) (new_bits)
+#else
+#define HASH_FNV_NEW_BITS(new_bits) (1 + (new_bits))
+#endif
+
+static HASH_FNV_T hash_fnv_basis = FNV_offset_basis;
+static int hash_fnv_must_init = 1;
+
+/* hash_fnv_init - seed the hash */
+
+static void hash_fnv_init(void)
+{
+ HASH_FNV_T seed;
+
+ if (!getenv("NORANDOMIZE")) {
+ ldseed(&seed, sizeof(seed));
+ hash_fnv_basis ^= seed;
+ }
+ hash_fnv_must_init = 0;
+}
+
/* hash_fnv - modified FNV 1a hash */
HASH_FNV_T hash_fnv(const void *src, size_t len)
{
- static HASH_FNV_T basis = FNV_offset_basis;
- static int randomize = 1;
HASH_FNV_T hash;
+ HASH_FNV_T new_bits;
- /*
- * Initialize.
- */
- if (randomize) {
- if (!getenv("NORANDOMIZE")) {
- HASH_FNV_T seed;
+ if (hash_fnv_must_init)
+ hash_fnv_init();
- ldseed(&seed, sizeof(seed));
- basis ^= seed;
- }
- randomize = 0;
+ hash = hash_fnv_basis;
+ while (len-- > 0) {
+ new_bits = *(unsigned char *) src++;
+ hash ^= HASH_FNV_NEW_BITS(new_bits);
+ hash *= FNV_prime;
+ }
+ return (hash);
+}
+
+/* hash_fnvz - modified FNV 1a hash for null-terminated strings */
+
+HASH_FNV_T hash_fnvz(const char *src)
+{
+ HASH_FNV_T hash;
+ HASH_FNV_T new_bits;
+
+ if (hash_fnv_must_init)
+ hash_fnv_init();
+
+ hash = hash_fnv_basis;
+ while ((new_bits = *(unsigned char *) src++) != 0) {
+ hash ^= HASH_FNV_NEW_BITS(new_bits);
+ hash *= FNV_prime;
}
+ return (hash);
+}
+#ifdef TEST
+#include <stdlib.h>
+#include <string.h>
+#include <msg.h>
+
+int main(void)
+{
+ int pass = 0;
+ int fail = 0;
+
+ /*
+ * Sanity check.
+ */
#ifdef STRICT_FNV1A
-#define FNV_NEXT_BYTE(s) ((HASH_FNV_T) * (const unsigned char *) s++)
+ msg_fatal("This test requires no STRICT_FNV1A");
+#endif
+
+ /*
+ * Force unseeded hash, to make tests predictable.
+ */
+ if (putenv("NORANDOMIZE=") != 0)
+ msg_fatal("putenv(\"NORANDOMIZE=\"): %m");
+
+ /*
+ * Test: hashing produces the expected results.
+ */
+ {
+ struct testcase {
+ HASH_FNV_T hval;
+ const char *str;
+ };
+ static struct testcase testcases[] =
+ {
+#ifdef USE_FNV_32BIT
+ 0x1c00fc06UL, "overdeeply",
+ 0x1c00fc06UL, "undescript",
+ 0x1e1e52a4UL, "fanfold",
+ 0x1e1e52a4UL, "phrensied",
#else
-#define FNV_NEXT_BYTE(s) (1 + (HASH_FNV_T) * (const unsigned char *) s++)
+ 0xda19999ec0bda706ULL, "overdeeply",
+ 0xd7b9e43f26396a66ULL, "undescript",
+ 0xa50c585d385a2604ULL, "fanfold",
+ 0x1ec3ef9bb2b734a4ULL, "phrensied",
#endif
+ 0,
+ };
+ struct testcase *tp;
+ HASH_FNV_T hval;
+ int test_failed;
- hash = basis;
- while (len-- > 0) {
- hash ^= FNV_NEXT_BYTE(src);
- hash *= FNV_prime;
+ for (tp = testcases; tp->str; tp++) {
+ test_failed = 0;
+ if ((hval = hash_fnvz(tp->str)) != tp->hval) {
+ msg_warn("hash_fnv(\"%s\") want %lu, got: %lu",
+ tp->str, (unsigned long) tp->hval,
+ (unsigned long) hval);
+ test_failed = 1;
+ }
+ if (test_failed) {
+ fail += 1;
+ msg_info("FAIL: %s", tp->str);
+ } else {
+ pass += 1;
+ msg_info("PASS: %s", tp->str);
+ }
+ }
}
- return (hash);
+
+ /*
+ * Test: hash_fnvz(s) is equivalent to hash_fnv(s, strlen(s)). No need to
+ * verify the actual result; we already did that above.
+ */
+ {
+ const char *strval = "foobar";
+ HASH_FNV_T h1 = hash_fnv(strval, strlen(strval));
+ HASH_FNV_T h2 = hash_fnvz(strval);
+
+ if (h1 == h2) {
+ pass += 1;
+ msg_info("PASS: hash_fnvz(\"%s\") == hash_fnv(\"%s\", %ld)",
+ strval, strval, (long) strlen(strval));
+ } else {
+ fail += 1;
+ msg_info("FAIL: hash_fnvz(\"%s\") == hash_fnv(\"%s\", %ld)",
+ strval, strval, (long) strlen(strval));
+ }
+ }
+
+
+ /*
+ * Wrap up.
+ */
+ msg_info("PASS=%d FAIL=%d", pass, fail);
+ return (fail != 0);
}
+
+#endif
#endif /* HASH_FNV_T */
extern HASH_FNV_T hash_fnv(const void *, size_t);
+extern HASH_FNV_T hash_fnvz(const char *);
/* LICENSE
/* .ad
#ifndef NO_HASH_FNV
#include "hash_fnv.h"
-#define htable_hash(s, size) (hash_fnv((s), strlen(s)) % (size))
+#define htable_hash(s, size) (hash_fnvz(s) % (size))
#else
int saved_ch;
struct stat st;
int ret;
- mode_t saved_mode = 0;
+ mode_t saved_mode;
gid_t egid = -1;
/*