Cleanup: simplified the rule parser in global/server_acl.c.
Unbroke dict_debug Valgrind checks. File: util/dict_debug_test.sh.
+
+20250710
+
+ Bugfix (defect introduced: postfix-2.2, date 20050203):
+ after detecting a lookup table change, and after starting
+ a new postscreen process, the old postscreen process logged
+ an ENOTSOCK error while attempting to accept a connection
+ on a socket that it was no longer listening on. This error
+ was introduced first in the multi_server skeleton code, and
+ was five years later duplicated in the event_server skeleton
+ that was created for postscreen. Problem reported by Florian
+ Piekert. Files: master/multi_server.c, master/event_server.c.
+
+20250713
+
+ Cleanup: allow "postmap -s" and "postalias -s" with proxied
+ tables. The proxymap protocol already supported this. Files:
+ postmap/postmap.c, postalias/postalias.c.
+
+ Cleanup: simplified the proxymap protocol and the proxymap
+ table sharing strategy. Share only table instances that
+ have identical client-side dictionary flags when opening a
+ table (instead of sharing tables that have a common subset
+ of flags). With each client request, propagate all client-side
+ dictionary flags to the server, and upon request completion,
+ propagate all resulting server-side dictionary flags to the
+ client. Files: dict.h, dict_proxy.c, proxymap/proxymap.c,
+ global/mail_proto.h.
+
+ Cleanup; stop hard-coding "dict->flags = DICT_FLAG_FIXED"
+ in dict_alloc.c. All tables already overwrote that information.
+
+ Debugging: the default import_environment now also imports
+ XDG_RUNTIME_DIR to support GUI debugging a Postfix daemon
+ process on some platforms (it already imported XAUTHORITY
+ and DISPLAY for X-based debuggers). These environment
+ variables are set only when Postfix is started 'by hand'.
+ File: global/mail_params.h.
+
+ Graceful degradation: when a proxymap or proxywrite server
+ denies access to a table, do not terminate the program.
+ Instead, return a surrogate object that fails all requests
+ with an informative message. File: global/dict_proxy.c.
+
+ Workaround: added an example to the smtp_reply_filter
+ documentation that works around Microsoft SASL server
+ implementations that send a non-empty initial GSSAPI
+ challenge. File: proto/postconf.proto.
+
+ Typo in COMPATIBILITY_README.html. Emmanuel Fusté.
+P\bPo\bos\bst\btf\bfi\bix\bx B\bBa\bac\bck\bkw\bwa\bar\brd\bds\bs-\b-C\bCo\bom\bmp\bpa\bat\bti\bib\bbi\bil\bli\bit\bty\by S\bSa\baf\bfe\bet\bty\by N\bNe\bet\bt
+
+-------------------------------------------------------------------------------
+
+P\bPu\bur\brp\bpo\bos\bse\be o\bof\bf t\bth\bhi\bis\bs d\bdo\boc\bcu\bum\bme\ben\bnt\bt
+
+Postfix 3.0 introduces a safety net that runs Postfix programs with backwards-
+compatible default settings after an upgrade. The safety net will log a warning
+whenever a "new" default setting could have an negative effect on your mail
+flow.
+
+This document provides information on the following topics:
+
+ * Detailed descriptions of Postfix backwards-compatibility warnings.
+
+ * What backwards-compatible settings you may have to make permanent in
+ main.cf or master.cf.
+
+ * How to turn off Postfix backwards-compatibility warnings.
+
+O\bOv\bve\ber\brv\bvi\bie\bew\bw
+
+With backwards compatibility turned on, Postfix logs a message whenever a
+backwards-compatible default setting may be required for continuity of service.
+Based on this logging the system administrator can decide if any backwards-
+compatible settings need to be made permanent in main.cf or master.cf, before
+turning off the backwards-compatibility safety net as described at the end of
+this document.
+
+Logged with compatibility_level < 1:
+
+ * Using backwards-compatible default setting append_dot_mydomain=yes
+
+ * Using backwards-compatible default setting chroot=y
+
+ * Using backwards-compatible default setting "smtpd_relay_restrictions =
+ (empty)"
+
+ * Using backwards-compatible default setting smtputf8_enable=no
+
+Logged with compatibility_level < 2:
+
+ * Using backwards-compatible default setting mynetworks_style=subnet
+
+ * Using backwards-compatible default setting relay_domains=$mydestination
+
+Logged with compatibility_level < 3.6:
+
+ * Using backwards-compatible default setting smtpd_tls_fingerprint_digest=md5
+
+ * Using backwards-compatible default setting smtp_tls_fingerprint_digest=md5
+
+ * Using backwards-compatible default setting lmtp_tls_fingerprint_digest=md5
+
+ * Using backwards-compatible default setting
+ smtpd_relay_before_recipient_restrictions=no
+
+ * Using backwards-compatible default setting respectful_logging=no
+
+Logged with compatibility_level < 3.11:
+
+ * using backwards-compatible default setting
+ smtp_tlsrpt_skip_reused_handshakes=yes
+
+ * using backwards-compatible default setting xxx_security_level=(empty)
+
+If such a message is logged in the context of a legitimate request, the system
+administrator should make the backwards-compatible setting permanent in main.cf
+or master.cf, as detailed in the sections that follow.
+
+When no more backwards-compatible settings need to be made permanent, the
+system administrator should turn off the backwards-compatibility safety net as
+described at the end of this document.
+
+U\bUs\bsi\bin\bng\bg b\bba\bac\bck\bkw\bwa\bar\brd\bds\bs-\b-c\bco\bom\bmp\bpa\bat\bti\bib\bbl\ble\be d\bde\bef\bfa\bau\bul\blt\bt s\bse\bet\btt\bti\bin\bng\bg a\bap\bpp\bpe\ben\bnd\bd_\b_d\bdo\bot\bt_\b_m\bmy\byd\bdo\bom\bma\bai\bin\bn=\b=y\bye\bes\bs
+
+The append_dot_mydomain default value has changed from "yes" to "no". This
+could result in unexpected non-delivery of email after Postfix is updated from
+an older version. The backwards-compatibility safety net is designed to prevent
+such surprises.
+
+As long as the append_dot_mydomain parameter is left unspecified at its
+implicit default value, and the compatibility_level setting is less than 1,
+Postfix may log one of the following messages:
+
+ * Messages about missing "localhost" in mydestination or other address class:
+
+ postfix/trivial-rewrite[14777]: using backwards-compatible
+ default setting append_dot_mydomain=yes to rewrite
+ "localhost" to "localhost.example.com"; please add
+ "localhost" to mydestination or other address class
+
+ If Postfix logs the above message, add "localhost" to mydestination (or
+ virtual_alias_domains, virtual_mailbox_domains, or relay_domains) and
+ execute the command "p\bpo\bos\bst\btf\bfi\bix\bx r\bre\bel\blo\boa\bad\bd".
+
+ * Messages about incomplete domains in email addresses:
+
+ postfix/trivial-rewrite[25835]: using backwards-compatible
+ default setting append_dot_mydomain=yes to rewrite "foo" to
+ "foo.example.com"
+
+ If Postfix logs the above message for domains different from "localhost",
+ and the sender cannot be changed to use complete domain names in email
+ addresses, then the system administrator should make the backwards-
+ compatible setting "append_dot_mydomain = yes" permanent in main.cf:
+
+ # p\bpo\bos\bst\btc\bco\bon\bnf\bf a\bap\bpp\bpe\ben\bnd\bd_\b_d\bdo\bot\bt_\b_m\bmy\byd\bdo\bom\bma\bai\bin\bn=\b=y\bye\bes\bs
+ # p\bpo\bos\bst\btf\bfi\bix\bx r\bre\bel\blo\boa\bad\bd
+
+U\bUs\bsi\bin\bng\bg b\bba\bac\bck\bkw\bwa\bar\brd\bds\bs-\b-c\bco\bom\bmp\bpa\bat\bti\bib\bbl\ble\be d\bde\bef\bfa\bau\bul\blt\bt s\bse\bet\btt\bti\bin\bng\bg c\bch\bhr\bro\boo\bot\bt=\b=y\by
+
+The master.cf chroot default value has changed from "y" (yes) to "n" (no). The
+new default avoids the need for copies of system files under the Postfix queue
+directory. However, sites with strict security requirements may want to keep
+the chroot feature enabled after updating Postfix from an older version. The
+backwards-compatibility safety net is designed allow the administrator to
+choose if they want to keep the old behavior.
+
+As long as a master.cf chroot field is left unspecified at its implicit default
+value, and the compatibility_level setting is less than 1, Postfix may log the
+following message while it reads the master.cf file:
+
+ postfix/master[27664]: /etc/postfix/master.cf: line 72: using
+ backwards-compatible default setting chroot=y
+
+If this service should remain chrooted, then the system administrator should
+make the backwards-compatible setting "chroot = y" permanent in master.cf. For
+example, to update the chroot setting for the "smtp inet" service:
+
+ # p\bpo\bos\bst\btc\bco\bon\bnf\bf -\b-F\bF s\bsm\bmt\btp\bp/\b/i\bin\bne\bet\bt/\b/c\bch\bhr\bro\boo\bot\bt=\b=y\by
+ # p\bpo\bos\bst\btf\bfi\bix\bx r\bre\bel\blo\boa\bad\bd
+
+U\bUs\bsi\bin\bng\bg b\bba\bac\bck\bkw\bwa\bar\brd\bds\bs-\b-c\bco\bom\bmp\bpa\bat\bti\bib\bbl\ble\be d\bde\bef\bfa\bau\bul\blt\bt s\bse\bet\btt\bti\bin\bng\bg s\bsm\bmt\btp\bpd\bd_\b_r\bre\bel\bla\bay\by_\b_r\bre\bes\bst\btr\bri\bic\bct\bti\bio\bon\bns\bs =\b= (\b(e\bem\bmp\bpt\bty\by)\b)
+
+The smtpd_relay_restrictions feature was introduced with Postfix version 2.10,
+as a safety mechanism for configuration errors in smtpd_recipient_restrictions
+that could make Postfix an open relay.
+
+The smtpd_relay_restrictions implicit default setting forbids mail to remote
+destinations from clients that don't match permit_mynetworks or
+permit_sasl_authenticated. This could result in unexpected 'Relay access
+denied' errors after Postfix is updated from an older Postfix version. The
+backwards-compatibility safety net is designed to prevent such surprises.
+
+When the compatibility_level less than 1, and the smtpd_relay_restrictions
+parameter is left unspecified at its implicit default setting, Postfix may log
+the following message:
+
+ postfix/smtpd[38463]: using backwards-compatible default setting
+ "smtpd_relay_restrictions = (empty)" to avoid "Relay access
+ denied" error for recipient "user@example.com" from client
+ "host.example.net[10.0.0.2]"
+
+If this request should not be blocked, then the system administrator should
+make the backwards-compatible setting "smtpd_relay_restrictions=" (i.e. empty)
+permanent in main.cf:
+
+ # p\bpo\bos\bst\btc\bco\bon\bnf\bf s\bsm\bmt\btp\bpd\bd_\b_r\bre\bel\bla\bay\by_\b_r\bre\bes\bst\btr\bri\bic\bct\bti\bio\bon\bns\bs=\b=
+ # p\bpo\bos\bst\btf\bfi\bix\bx r\bre\bel\blo\boa\bad\bd
+
+U\bUs\bsi\bin\bng\bg b\bba\bac\bck\bkw\bwa\bar\brd\bds\bs-\b-c\bco\bom\bmp\bpa\bat\bti\bib\bbl\ble\be d\bde\bef\bfa\bau\bul\blt\bt s\bse\bet\btt\bti\bin\bng\bg s\bsm\bmt\btp\bpu\but\btf\bf8\b8_\b_e\ben\bna\bab\bbl\ble\be=\b=n\bno\bo
+
+The smtputf8_enable default value has changed from "no" to "yes". With the new
+"yes" setting, the Postfix SMTP server rejects non-ASCII addresses from clients
+that don't request SMTPUTF8 support, after Postfix is updated from an older
+version. The backwards-compatibility safety net is designed to prevent such
+surprises.
+
+As long as the smtputf8_enable parameter is left unspecified at its implicit
+default value, and the compatibility_level setting is less than 1, Postfix logs
+a warning each time an SMTP command uses a non-ASCII address localpart without
+requesting SMTPUTF8 support:
+
+ postfix/smtpd[27560]: using backwards-compatible default setting
+ smtputf8_enable=no to accept non-ASCII sender address
+ "??@example.org" from localhost[127.0.0.1]
+
+ postfix/smtpd[27560]: using backwards-compatible default setting
+ smtputf8_enable=no to accept non-ASCII recipient address
+ "??@example.com" from localhost[127.0.0.1]
+
+If the address should not be rejected, and the client cannot be updated to use
+SMTPUTF8, then the system administrator should make the backwards-compatible
+setting "smtputf8_enable = no" permanent in main.cf:
+
+ # p\bpo\bos\bst\btc\bco\bon\bnf\bf s\bsm\bmt\btp\bpu\but\btf\bf8\b8_\b_e\ben\bna\bab\bbl\ble\be=\b=n\bno\bo
+ # p\bpo\bos\bst\btf\bfi\bix\bx r\bre\bel\blo\boa\bad\bd
+
+U\bUs\bsi\bin\bng\bg b\bba\bac\bck\bkw\bwa\bar\brd\bds\bs-\b-c\bco\bom\bmp\bpa\bat\bti\bib\bbl\ble\be d\bde\bef\bfa\bau\bul\blt\bt s\bse\bet\btt\bti\bin\bng\bg m\bmy\byn\bne\bet\btw\bwo\bor\brk\bks\bs_\b_s\bst\bty\byl\ble\be=\b=s\bsu\bub\bbn\bne\bet\bt
+
+The mynetworks_style default value has changed from "subnet" to "host". This
+parameter is used to implement the "permit_mynetworks" feature. The change
+could cause unexpected 'access denied' errors after Postfix is updated from an
+older version. The backwards-compatibility safety net is designed to prevent
+such surprises.
+
+As long as the mynetworks and mynetworks_style parameters are left unspecified
+at their implicit default values, and the compatibility_level setting is less
+than 2, the Postfix SMTP server may log one of the following messages:
+
+ postfix/smtpd[17375]: using backwards-compatible default setting
+ mynetworks_style=subnet to permit request from client
+ "foo.example.com[10.1.1.1]"
+
+ postfix/postscreen[24982]: using backwards-compatible default
+ setting mynetworks_style=subnet to permit request from client
+ "10.1.1.1"
+
+If the client request should not be rejected, then the system administrator
+should make the backwards-compatible setting "mynetworks_style = subnet"
+permanent in main.cf:
+
+ # p\bpo\bos\bst\btc\bco\bon\bnf\bf m\bmy\byn\bne\bet\btw\bwo\bor\brk\bks\bs_\b_s\bst\bty\byl\ble\be=\b=s\bsu\bub\bbn\bne\bet\bt
+ # p\bpo\bos\bst\btf\bfi\bix\bx r\bre\bel\blo\boa\bad\bd
+
+U\bUs\bsi\bin\bng\bg b\bba\bac\bck\bkw\bwa\bar\brd\bds\bs-\b-c\bco\bom\bmp\bpa\bat\bti\bib\bbl\ble\be d\bde\bef\bfa\bau\bul\blt\bt s\bse\bet\btt\bti\bin\bng\bg r\bre\bel\bla\bay\by_\b_d\bdo\bom\bma\bai\bin\bns\bs=\b=$\b$m\bmy\byd\bde\bes\bst\bti\bin\bna\bat\bti\bio\bon\bn
+
+The relay_domains default value has changed from "$mydestination" to the empty
+value. This could result in unexpected 'Relay access denied' errors or ETRN
+errors after Postfix is updated from an older version. The backwards-
+compatibility safety net is designed to prevent such surprises.
+
+As long as the relay_domains parameter is left unspecified at its implicit
+default value, and the compatibility_level setting is less than 2, Postfix may
+log one of the following messages.
+
+ * Messages about accepting mail for a remote domain:
+
+ postfix/smtpd[19052]: using backwards-compatible default setting
+ relay_domains=$mydestination to accept mail for domain
+ "foo.example.com"
+
+ postfix/smtpd[19052]: using backwards-compatible default setting
+ relay_domains=$mydestination to accept mail for address
+ "user@foo.example.com"
+
+ * Messages about providing ETRN service for a remote domain:
+
+ postfix/smtpd[19138]: using backwards-compatible default setting
+ relay_domains=$mydestination to flush mail for domain
+ "bar.example.com"
+
+ postfix/smtp[13945]: using backwards-compatible default setting
+ relay_domains=$mydestination to update fast-flush logfile for
+ domain "bar.example.com"
+
+If Postfix should continue to accept mail for that domain or continue to
+provide ETRN service for that domain, then the system administrator should make
+the backwards-compatible setting "relay_domains = $mydestination" permanent in
+main.cf:
+
+ # p\bpo\bos\bst\btc\bco\bon\bnf\bf '\b'r\bre\bel\bla\bay\by_\b_d\bdo\bom\bma\bai\bin\bns\bs=\b=$\b$m\bmy\byd\bde\bes\bst\bti\bin\bna\bat\bti\bio\bon\bn'\b'
+ # p\bpo\bos\bst\btf\bfi\bix\bx r\bre\bel\blo\boa\bad\bd
+
+Note: quotes are required as indicated above.
+
+Instead of $mydestination, it may be better to specify an explicit list of
+domain names.
+
+U\bUs\bsi\bin\bng\bg b\bba\bac\bck\bkw\bwa\bar\brd\bds\bs-\b-c\bco\bom\bmp\bpa\bat\bti\bib\bbl\ble\be d\bde\bef\bfa\bau\bul\blt\bt s\bse\bet\btt\bti\bin\bng\bg s\bsm\bmt\btp\bpd\bd_\b_t\btl\bls\bs_\b_f\bfi\bin\bng\bge\ber\brp\bpr\bri\bin\bnt\bt_\b_d\bdi\big\bge\bes\bst\bt=\b=m\bmd\bd5\b5
+
+The smtpd_tls_fingerprint_digest default value has changed from "md5" to
+"sha256". With the new "sha256" setting, the Postfix SMTP server avoids using
+the deprecated "md5" algorithm and computes a more secure digest of the client
+certificate.
+
+If you're using the default "md5" setting, or even an explicit "sha1" (also
+deprecated) setting, you should consider switching to "sha256". This will
+require updating any associated lookup table keys with the "sha256" digests of
+the expected client certificate or public key.
+
+As long as the smtpd_tls_fingerprint_digest parameter is left unspecified at
+its implicit default value, and the compatibility_level setting is less than
+3.6, Postfix logs a warning each time a client certificate or public key
+fingerprint is (potentially) used for access control:
+
+ postfix/smtpd[27560]: using backwards-compatible default setting
+ smtpd_tls_fingerprint_digest=md5 to compute certificate fingerprints
+
+Since any client certificate fingerprints are passed in policy service lookups,
+and Postfix doesn't know whether the fingerprint will be used, the warning may
+also be logged when policy lookups are performed for connections that used a
+client certificate, even if the policy service does not in fact examine the
+client certificate. To reduce the noise somewhat, such warnings are issued at
+most once per smtpd(8) process instance.
+
+If you prefer to stick with "md5", you can suppress the warnings by making that
+setting explicit. After addressing any other compatibility warnings, you can
+update your compatibility level.
+
+ # p\bpo\bos\bst\btc\bco\bon\bnf\bf s\bsm\bmt\btp\bpd\bd_\b_t\btl\bls\bs_\b_f\bfi\bin\bng\bge\ber\brp\bpr\bri\bin\bnt\bt_\b_d\bdi\big\bge\bes\bst\bt=\b=m\bmd\bd5\b5
+ # p\bpo\bos\bst\btf\bfi\bix\bx r\bre\bel\blo\boa\bad\bd
+
+U\bUs\bsi\bin\bng\bg b\bba\bac\bck\bkw\bwa\bar\brd\bds\bs-\b-c\bco\bom\bmp\bpa\bat\bti\bib\bbl\ble\be d\bde\bef\bfa\bau\bul\blt\bt s\bse\bet\btt\bti\bin\bng\bg s\bsm\bmt\btp\bp_\b_t\btl\bls\bs_\b_f\bfi\bin\bng\bge\ber\brp\bpr\bri\bin\bnt\bt_\b_d\bdi\big\bge\bes\bst\bt=\b=m\bmd\bd5\b5
+
+The smtp_tls_fingerprint_digest and lmtp_tls_fingerprint_digest default values
+have changed from "md5" to "sha256". With the new "sha256" setting, the Postfix
+SMTP and LMTP client avoids using the deprecated "md5" algorithm and computes a
+more secure digest of the server certificate.
+
+If you're using the default "md5" setting, or even an explicit "sha1" (also
+deprecated) setting, you should consider switching to "sha256". This will
+require updating any "fingerprint" security level policies in the TLS policy
+table to specify matching "sha256" digests of the expected server certificates
+or public keys.
+
+As long as the smtp_tls_fingerprint_digest (or LMTP equivalent) parameter is
+left unspecified at its implicit default value, and the compatibility_level
+setting is less than 3.6, Postfix logs a warning each time the "fingerprint"
+security level is used to specify matching "md5" digests of trusted server
+certificates or public keys:
+
+ postfix/smtp[27560]: using backwards-compatible default setting
+ smtp_tls_fingerprint_digest=md5 to compute certificate fingerprints
+
+If you prefer to stick with "md5", you can suppress the warnings by making that
+setting explicit. After addressing any other compatibility warnings, you can
+update your compatibility level.
+
+ # p\bpo\bos\bst\btc\bco\bon\bnf\bf '\b's\bsm\bmt\btp\bp_\b_t\btl\bls\bs_\b_f\bfi\bin\bng\bge\ber\brp\bpr\bri\bin\bnt\bt_\b_d\bdi\big\bge\bes\bst\bt =\b= m\bmd\bd5\b5'\b' \\b\
+ '\b'l\blm\bmt\btp\bp_\b_t\btl\bls\bs_\b_f\bfi\bin\bng\bge\ber\brp\bpr\bri\bin\bnt\bt_\b_d\bdi\big\bge\bes\bst\bt =\b= m\bmd\bd5\b5'\b'
+ # p\bpo\bos\bst\btf\bfi\bix\bx r\bre\bel\blo\boa\bad\bd
+
+U\bUs\bsi\bin\bng\bg b\bba\bac\bck\bkw\bwa\bar\brd\bds\bs-\b-c\bco\bom\bmp\bpa\bat\bti\bib\bbl\ble\be d\bde\bef\bfa\bau\bul\blt\bt s\bse\bet\btt\bti\bin\bng\bg
+s\bsm\bmt\btp\bpd\bd_\b_r\bre\bel\bla\bay\by_\b_b\bbe\bef\bfo\bor\bre\be_\b_r\bre\bec\bci\bip\bpi\bie\ben\bnt\bt_\b_r\bre\bes\bst\btr\bri\bic\bct\bti\bio\bon\bns\bs=\b=n\bno\bo
+
+The smtpd_relay_before_recipient_restrictions feature was introduced in Postfix
+version 3.6, to evaluate smtpd_relay_restrictions before
+smtpd_recipient_restrictions. Historically, smtpd_relay_restrictions was
+evaluated after smtpd_recipient_restrictions, contradicting documented
+behavior.
+
+ Background: smtpd_relay_restrictions is primarily designed to enforce a
+ mail relaying policy, while smtpd_recipient_restrictions is primarily
+ designed to enforce spam blocking policy. Both are evaluated while replying
+ to the RCPT TO command, and both support the same features.
+
+To maintain compatibility with earlier versions, Postfix will keep evaluating
+smtpd_recipient_restrictions before smtpd_relay_restrictions, as long as the
+compatibility_level is less than 3.6, and the
+smtpd_relay_before_recipient_restrictions parameter is left unspecified at its
+implicit default setting. As a reminder, Postfix may log the following message:
+
+ postfix/smtpd[54696]: using backwards-compatible default setting
+ smtpd_relay_before_recipient_restrictions=no to reject recipient
+ "user@example.com" from client "host.example.net[10.0.0.2]"
+
+If Postfix should keep evaluating smtpd_recipient_restrictions before
+smtpd_relay_restrictions, then the system administrator should make the
+backwards-compatible setting "smtpd_relay_before_recipient_restrictions=no"
+permanent in main.cf:
+
+ # p\bpo\bos\bst\btc\bco\bon\bnf\bf s\bsm\bmt\btp\bpd\bd_\b_r\bre\bel\bla\bay\by_\b_b\bbe\bef\bfo\bor\bre\be_\b_r\bre\bec\bci\bip\bpi\bie\ben\bnt\bt_\b_r\bre\bes\bst\btr\bri\bic\bct\bti\bio\bon\bns\bs=\b=n\bno\bo
+ # p\bpo\bos\bst\btf\bfi\bix\bx r\bre\bel\blo\boa\bad\bd
+
+U\bUs\bsi\bin\bng\bg b\bba\bac\bck\bkw\bwa\bar\brd\bds\bs-\b-c\bco\bom\bmp\bpa\bat\bti\bib\bbl\ble\be d\bde\bef\bfa\bau\bul\blt\bt s\bse\bet\btt\bti\bin\bng\bg r\bre\bes\bsp\bpe\bec\bct\btf\bfu\bul\bl_\b_l\blo\bog\bgg\bgi\bin\bng\bg=\b=n\bno\bo
+
+Postfix version 3.6 deprecates configuration parameter names and logging that
+suggest white is better than black. Instead it prefers 'allowlist, 'denylist',
+and variations of those words. While the renamed configuration parameters have
+backwards-compatible default values, the changes in logging could affect
+logfile analysis tools.
+
+To avoid breaking existing logfile analysis tools, Postfix will keep logging
+the deprecated form, as long as the respectful_logging parameter is left
+unspecified at its implicit default value, and the compatibility_level setting
+is less than 3.6. As a reminder, Postfix may log the following when a remote
+SMTP client is allowlisted or denylisted:
+
+ postfix/postscreen[22642]: Using backwards-compatible default setting
+ respectful_logging=no for client [address]:port
+
+If Postfix should keep logging the deprecated form, then the system
+administrator should make the backwards-compatible setting "respectful_logging
+= no" permanent in main.cf.
+
+ # p\bpo\bos\bst\btc\bco\bon\bnf\bf "\b"r\bre\bes\bsp\bpe\bec\bct\btf\bfu\bul\bl_\b_l\blo\bog\bgg\bgi\bin\bng\bg =\b= n\bno\bo"\b"
+ # p\bpo\bos\bst\btf\bfi\bix\bx r\bre\bel\blo\boa\bad\bd
+
+U\bUs\bsi\bin\bng\bg b\bba\bac\bck\bkw\bwa\bar\brd\bds\bs-\b-c\bco\bom\bmp\bpa\bat\bti\bib\bbl\ble\be d\bde\bef\bfa\bau\bul\blt\bt s\bse\bet\btt\bti\bin\bng\bg
+s\bsm\bmt\btp\bp_\b_t\btl\bls\bsr\brp\bpt\bt_\b_s\bsk\bki\bip\bp_\b_r\bre\beu\bus\bse\bed\bd_\b_h\bha\ban\bnd\bds\bsh\bha\bak\bke\bes\bs=\b=y\bye\bes\bs
+
+Postfix version 3.11 changes the default value for
+smtp_tlsrpt_skip_reused_handshakes from "yes" to "no". The backwards-
+compatibility safety net is designed to prevent an unexpected change in
+reporting behavior when Postfix is updated from an older version.
+
+As long as the smtp_tlsrpt_skip_reused_handshakes parameter is left unspecified
+at its implicit default value, and the compatibility_level setting is less than
+3.11, Postfix will log a reminder that it is using the backwards-compatible
+default:
+
+ postfix/smtp[388157] using backwards-compatible default setting
+ smtp_tlsrpt_skip_reused_handshakes=yes
+
+To keep the old default setting, the system administrator should make the
+backwards-compatible setting "smtp_tlsrpt_skip_reused_handshakes = yes"
+permanent in main.cf:
+
+ # p\bpo\bos\bst\btc\bco\bon\bnf\bf s\bsm\bmt\btp\bp_\b_t\btl\bls\bsr\brp\bpt\bt_\b_s\bsk\bki\bip\bp_\b_r\bre\beu\bus\bse\bed\bd_\b_h\bha\ban\bnd\bds\bsh\bha\bak\bke\bes\bs=\b=y\bye\bes\bs
+ # p\bpo\bos\bst\btf\bfi\bix\bx r\bre\bel\blo\boa\bad\bd
+
+U\bUs\bsi\bin\bng\bg b\bba\bac\bck\bkw\bwa\bar\brd\bds\bs-\b-c\bco\bom\bmp\bpa\bat\bti\bib\bbl\ble\be d\bde\bef\bfa\bau\bul\blt\bt s\bse\bet\btt\bti\bin\bng\bg x\bxx\bxx\bx_\b_s\bse\bec\bcu\bur\bri\bit\bty\by_\b_l\ble\bev\bve\bel\bl=\b=(\b(e\bem\bmp\bpt\bty\by)\b)
+
+Postfix version 3.11 changes the default value for client TLS security levels
+from "empty" to "may". The backwards-compatibility safety net is designed to
+prevent an unexpected change in mail sending behavior when Postfix is updated
+from an older version.
+
+There is no equivalent change for Postfix server TLS security levels, because
+changing the level alone is not sufficient. Server-side TLS requires that at
+least one private key and one public-key certificate chain are configured.
+
+As long as a TLS security level parameter is left unspecified at its implicit
+default value, and the compatibility_level setting is less than 3.11, Postfix
+will log one of the following reminders that it is using the backwards-
+compatible default:
+
+ postfix/smtp[...] using backwards-compatible default setting
+ smtp_tls_security_level=(empty)
+
+ postfix/tlsproxy[...] using backwards-compatible default setting
+ tlsproxy_client_security_level=(empty)
+
+To keep the old default setting, the system administrator should make the
+backwards-compatible empty setting permanent in main.cf:
+
+ # p\bpo\bos\bst\btc\bco\bon\bnf\bf x\bxx\bxx\bx_\b_s\bse\bec\bcu\bur\bri\bit\bty\by_\b_l\ble\bev\bve\bel\bl=\b=
+ # p\bpo\bos\bst\btf\bfi\bix\bx r\bre\bel\blo\boa\bad\bd
+
+where xxx is taken from the above compatibility message.
+
+T\bTu\bur\brn\bni\bin\bng\bg o\bof\bff\bf t\bth\bhe\be b\bba\bac\bck\bkw\bwa\bar\brd\bds\bs-\b-c\bco\bom\bmp\bpa\bat\bti\bib\bbi\bil\bli\bit\bty\by s\bsa\baf\bfe\bet\bty\by n\bne\bet\bt
+
+Backwards compatibility is turned off by updating the compatibility_level
+setting in main.cf.
+
+ # p\bpo\bos\bst\btc\bco\bon\bnf\bf c\bco\bom\bmp\bpa\bat\bti\bib\bbi\bil\bli\bit\bty\by_\b_l\ble\bev\bve\bel\bl=\b=N\bN
+ # p\bpo\bos\bst\btf\bfi\bix\bx r\bre\bel\blo\boa\bad\bd
+
+For N specify the number that is logged in your postfix(1) warning message:
+
+ warning: To disable backwards compatibility use "postconf
+ compatibility_level=N" and "postfix reload"
+
+Sites that don't care about backwards compatibility may set
+"compatibility_level = 9999" at their own risk.
+
+Starting with Postfix version 3.6, the compatibility level in the above warning
+message is the Postfix version that introduced the last incompatible change.
+The level is formatted as major.minor.patch, where patch is usually omitted and
+defaults to zero. Earlier compatibility levels are 0, 1 and 2.
+
+NOTE: Postfix 3.6 also introduces support for the "<level", "<=level", and
+other operators to compare compatibility levels. With the standard operators
+"<", "<=", etc., compatibility level "3.10" would be smaller than "3.9" which
+is undesirable.
+
default setting <i>xxx</i>_security_level=(empty)</a> </h2>
<p> Postfix version 3.11 changes the default value for client TLS
-security levels from "empty" to "yes". The backwards-compatibility
+security levels from "empty" to "may". The backwards-compatibility
safety net is designed to prevent an unexpected change in mail
sending behavior when Postfix is updated from an older version.
</p>
<dd>Needed to make "<b>postfix -c</b>" work. </dd>
-<dt><b>POSTLOG_SERVICE</b></dt>
+<dt><b>POSTLOG_HOSTNAME</b></dt>
<dd>Needed to make "<b><a href="postconf.5.html#maillog_file">maillog_file</a></b>" work during daemon
process initialization. </dd>
-<dt><b>POSTLOG_HOSTNAME</b></dt>
+<dt><b>POSTLOG_SERVICE</b></dt>
<dd>Needed to make "<b><a href="postconf.5.html#maillog_file">maillog_file</a></b>" work during daemon
process initialization. </dd>
+<dt><b>XDG_RUNTIME_DIR</b></dt>
+
+<dd>Needed for debugging Postfix daemons with an XDG-style debugger.
+</dd>
+
</dl>
<p> Specify a list of names and/or name=value pairs, separated by
<pre>
/etc/postfix/reply_filter:
+ # Some Microsoft servers violate <a href="https://tools.ietf.org/html/rfc2554">RFC 2554</a> section 4, causing Postfix
+ # to complain with "non-empty initial GSSAPI challenge from server"
+ /^334\s+GSSAPI\s+supported/ 334
+</pre>
+
+<pre>
# Transform garbage into "250-filler..." so that it looks like
# one line from a multi-line reply. It does not matter what we
# substitute here as long it has the right syntax. The Postfix
.IP "\fBMAIL_CONFIG\fR"
Needed to make "\fBpostfix \-c\fR" work.
.br
-.IP "\fBPOSTLOG_SERVICE\fR"
+.IP "\fBPOSTLOG_HOSTNAME\fR"
Needed to make "\fBmaillog_file\fR" work during daemon
process initialization.
.br
-.IP "\fBPOSTLOG_HOSTNAME\fR"
+.IP "\fBPOSTLOG_SERVICE\fR"
Needed to make "\fBmaillog_file\fR" work during daemon
process initialization.
.br
+.IP "\fBXDG_RUNTIME_DIR\fR"
+Needed for debugging Postfix daemons with an XDG\-style debugger.
+.br
.br
.PP
Specify a list of names and/or name=value pairs, separated by
.nf
.na
/etc/postfix/reply_filter:
+ # Some Microsoft servers violate RFC 2554 section 4, causing Postfix
+ # to complain with "non\-empty initial GSSAPI challenge from server"
+ /^334\es+GSSAPI\es+supported/ 334
+.fi
+.ad
+.PP
+.nf
+.na
# Transform garbage into "250\-filler..." so that it looks like
# one line from a multi\-line reply. It does not matter what we
# substitute here as long it has the right syntax. The Postfix
LANG=C; export LANG
-mantools/dehtml proto/*html proto/*.proto | spell | grep -F -vxf proto/stop | grep -F -vxf proto/stop.spell-proto-html
+mantools/dehtml proto/*html proto/*.proto | tr '+' ' ' | spell | grep -F -vxf proto/stop | grep -F -vxf proto/stop.spell-proto-html
default setting <i>xxx</i>_security_level=(empty)</a> </h2>
<p> Postfix version 3.11 changes the default value for client TLS
-security levels from "empty" to "yes". The backwards-compatibility
+security levels from "empty" to "may". The backwards-compatibility
safety net is designed to prevent an unexpected change in mail
sending behavior when Postfix is updated from an older version.
</p>
<dd>Needed to make "<b>postfix -c</b>" work. </dd>
+<dt><b>POSTLOG_HOSTNAME</b></dt>
+
+<dd>Needed to make "<b>maillog_file</b>" work during daemon
+process initialization. </dd>
+
<dt><b>POSTLOG_SERVICE</b></dt>
<dd>Needed to make "<b>maillog_file</b>" work during daemon
process initialization. </dd>
-<dt><b>POSTLOG_HOSTNAME</b></dt>
+<dt><b>XDG_RUNTIME_DIR</b></dt>
-<dd>Needed to make "<b>maillog_file</b>" work during daemon
-process initialization. </dd>
+<dd>Needed for debugging Postfix daemons with an XDG-style debugger.
+</dd>
</dl>
<pre>
/etc/postfix/reply_filter:
+ # Some Microsoft servers violate RFC 2554 section 4, causing Postfix
+ # to complain with "non-empty initial GSSAPI challenge from server"
+ /^334\s+GSSAPI\s+supported/ 334
+</pre>
+
+<pre>
# Transform garbage into "250-filler..." so that it looks like
# one line from a multi-line reply. It does not matter what we
# substitute here as long it has the right syntax. The Postfix
postconf Makefile in postconf postconf c
dict_open Files util dict hc proxymap proxymap c
proxymap proxymap c
+ postmap postmap c postalias postalias c
+ client Files dict h dict_proxy c proxymap proxymap c
Oleksandr
Bataille
balancers
+Unbroke
+XDG
+ENOTSOCK
+FustÃ
VSTREAM *stream;
int status;
int count = 0;
+ int inst_flags;
int request_flags;
/*
VSTRING_TERMINATE(dict_proxy->reskey);
VSTRING_RESET(dict_proxy->result);
VSTRING_TERMINATE(dict_proxy->result);
- request_flags = dict_proxy->inst_flags
- | (dict->flags & DICT_FLAG_RQST_MASK);
+ inst_flags = dict_proxy->inst_flags;
+ request_flags = dict->flags;
for (;;) {
stream = clnt_stream_access(dict_proxy->clnt);
errno = 0;
|| attr_print(stream, ATTR_FLAG_NONE,
SEND_ATTR_STR(MAIL_ATTR_REQ, PROXY_REQ_SEQUENCE),
SEND_ATTR_STR(MAIL_ATTR_TABLE, dict->name),
+ SEND_ATTR_INT(MAIL_ATTR_INST_FLAGS, inst_flags),
SEND_ATTR_INT(MAIL_ATTR_FLAGS, request_flags),
SEND_ATTR_INT(MAIL_ATTR_FUNC, function),
ATTR_TYPE_END) != 0
|| vstream_fflush(stream)
|| attr_scan(stream, ATTR_FLAG_STRICT,
RECV_ATTR_INT(MAIL_ATTR_STATUS, &status),
+ RECV_ATTR_INT(MAIL_ATTR_FLAGS, &dict->flags),
RECV_ATTR_STR(MAIL_ATTR_KEY, dict_proxy->reskey),
RECV_ATTR_STR(MAIL_ATTR_VALUE, dict_proxy->result),
- ATTR_TYPE_END) != 3) {
+ ATTR_TYPE_END) != 4) {
if (msg_verbose || count > 1 || (errno && errno != EPIPE && errno != ENOENT))
msg_warn("%s: service %s: %m", myname, dict_proxy->service);
} else {
VSTREAM *stream;
int status;
int count = 0;
+ int inst_flags;
int request_flags;
/*
*/
VSTRING_RESET(dict_proxy->result);
VSTRING_TERMINATE(dict_proxy->result);
- request_flags = dict_proxy->inst_flags
- | (dict->flags & DICT_FLAG_RQST_MASK);
+ inst_flags = dict_proxy->inst_flags;
+ request_flags = dict->flags;
for (;;) {
stream = clnt_stream_access(dict_proxy->clnt);
errno = 0;
|| attr_print(stream, ATTR_FLAG_NONE,
SEND_ATTR_STR(MAIL_ATTR_REQ, PROXY_REQ_LOOKUP),
SEND_ATTR_STR(MAIL_ATTR_TABLE, dict->name),
+ SEND_ATTR_INT(MAIL_ATTR_INST_FLAGS, inst_flags),
SEND_ATTR_INT(MAIL_ATTR_FLAGS, request_flags),
SEND_ATTR_STR(MAIL_ATTR_KEY, key),
ATTR_TYPE_END) != 0
|| vstream_fflush(stream)
|| attr_scan(stream, ATTR_FLAG_STRICT,
RECV_ATTR_INT(MAIL_ATTR_STATUS, &status),
+ RECV_ATTR_INT(MAIL_ATTR_FLAGS, &dict->flags),
RECV_ATTR_STR(MAIL_ATTR_VALUE, dict_proxy->result),
- ATTR_TYPE_END) != 2) {
+ ATTR_TYPE_END) != 3) {
if (msg_verbose || count > 1 || (errno && errno != EPIPE && errno != ENOENT))
msg_warn("%s: service %s: %m", myname, dict_proxy->service);
} else {
VSTREAM *stream;
int status;
int count = 0;
+ int inst_flags;
int request_flags;
/*
* associated with a specific connection. Each lookup needs to specify
* the table and the flags that were specified to dict_proxy_open().
*/
- request_flags = dict_proxy->inst_flags
- | (dict->flags & DICT_FLAG_RQST_MASK);
+ inst_flags = dict_proxy->inst_flags;
+ request_flags = dict->flags;
for (;;) {
stream = clnt_stream_access(dict_proxy->clnt);
errno = 0;
|| attr_print(stream, ATTR_FLAG_NONE,
SEND_ATTR_STR(MAIL_ATTR_REQ, PROXY_REQ_UPDATE),
SEND_ATTR_STR(MAIL_ATTR_TABLE, dict->name),
+ SEND_ATTR_INT(MAIL_ATTR_INST_FLAGS, inst_flags),
SEND_ATTR_INT(MAIL_ATTR_FLAGS, request_flags),
SEND_ATTR_STR(MAIL_ATTR_KEY, key),
SEND_ATTR_STR(MAIL_ATTR_VALUE, value),
|| vstream_fflush(stream)
|| attr_scan(stream, ATTR_FLAG_STRICT,
RECV_ATTR_INT(MAIL_ATTR_STATUS, &status),
- ATTR_TYPE_END) != 1) {
+ RECV_ATTR_INT(MAIL_ATTR_FLAGS, &dict->flags),
+ ATTR_TYPE_END) != 2) {
if (msg_verbose || count > 1 || (errno && errno != EPIPE && errno != ENOENT))
msg_warn("%s: service %s: %m", myname, dict_proxy->service);
} else {
VSTREAM *stream;
int status;
int count = 0;
+ int inst_flags;
int request_flags;
/*
* associated with a specific connection. Each lookup needs to specify
* the table and the flags that were specified to dict_proxy_open().
*/
- request_flags = dict_proxy->inst_flags
- | (dict->flags & DICT_FLAG_RQST_MASK);
+ inst_flags = dict_proxy->inst_flags;
+ request_flags = dict->flags;
for (;;) {
stream = clnt_stream_access(dict_proxy->clnt);
errno = 0;
|| attr_print(stream, ATTR_FLAG_NONE,
SEND_ATTR_STR(MAIL_ATTR_REQ, PROXY_REQ_DELETE),
SEND_ATTR_STR(MAIL_ATTR_TABLE, dict->name),
+ SEND_ATTR_INT(MAIL_ATTR_INST_FLAGS, inst_flags),
SEND_ATTR_INT(MAIL_ATTR_FLAGS, request_flags),
SEND_ATTR_STR(MAIL_ATTR_KEY, key),
ATTR_TYPE_END) != 0
|| vstream_fflush(stream)
|| attr_scan(stream, ATTR_FLAG_STRICT,
RECV_ATTR_INT(MAIL_ATTR_STATUS, &status),
- ATTR_TYPE_END) != 1) {
+ RECV_ATTR_INT(MAIL_ATTR_FLAGS, &dict->flags),
+ ATTR_TYPE_END) != 2) {
if (msg_verbose || count > 1 || (errno && errno != EPIPE && errno !=
ENOENT))
msg_warn("%s: service %s: %m", myname, dict_proxy->service);
dict_proxy->dict.delete = dict_proxy_delete;
dict_proxy->dict.sequence = dict_proxy_sequence;
dict_proxy->dict.close = dict_proxy_close;
- dict_proxy->inst_flags = (dict_flags & DICT_FLAG_INST_MASK);
+ dict_proxy->inst_flags = dict_flags;
dict_proxy->reskey = vstring_alloc(10);
dict_proxy->result = vstring_alloc(10);
dict_proxy->clnt = *pstream;
dict_proxy->service = service;
+#define DICT_PROXY_ERR_RETURN(d) do { \
+ DICT *_d = (d); \
+ dict_proxy_close(&dict_proxy->dict); \
+ return (_d); \
+ } while (0)
+
/*
* Establish initial contact and get the map type specific flags.
- *
- * XXX Should retrieve flags from local instance.
*/
for (;;) {
stream = clnt_stream_access(dict_proxy->clnt);
|| attr_print(stream, ATTR_FLAG_NONE,
SEND_ATTR_STR(MAIL_ATTR_REQ, PROXY_REQ_OPEN),
SEND_ATTR_STR(MAIL_ATTR_TABLE, dict_proxy->dict.name),
- SEND_ATTR_INT(MAIL_ATTR_FLAGS, dict_proxy->inst_flags),
+ SEND_ATTR_INT(MAIL_ATTR_INST_FLAGS, dict_proxy->inst_flags),
ATTR_TYPE_END) != 0
|| vstream_fflush(stream)
|| attr_scan(stream, ATTR_FLAG_STRICT,
dict_flags_str(server_flags));
switch (status) {
case PROXY_STAT_BAD:
- msg_fatal("%s open failed for table \"%s\": invalid request",
- dict_proxy->service, dict_proxy->dict.name);
+ DICT_PROXY_ERR_RETURN(dict_surrogate(DICT_TYPE_PROXY,
+ dict_proxy->dict.name, open_flags, dict_flags,
+ "%s open failed for table \"%s\": invalid request",
+ dict_proxy->service, dict_proxy->dict.name));
case PROXY_STAT_DENY:
- msg_fatal("%s service is not configured for table \"%s\"",
- dict_proxy->service, dict_proxy->dict.name);
+ DICT_PROXY_ERR_RETURN(dict_surrogate(DICT_TYPE_PROXY,
+ dict_proxy->dict.name, open_flags, dict_flags,
+ "%s service is not configured for table \"%s\"",
+ dict_proxy->service, dict_proxy->dict.name));
case PROXY_STAT_OK:
- dict_proxy->dict.flags = (dict_flags & ~DICT_FLAG_IMPL_MASK)
- | (server_flags & DICT_FLAG_IMPL_MASK);
+ dict_proxy->dict.flags = server_flags;
return (&dict_proxy->dict);
default:
msg_warn("%s open failed for table \"%s\": unexpected status %d",
#define VAR_IMPORT_ENVIRON "import_environment"
#define DEF_IMPORT_ENVIRON "MAIL_CONFIG MAIL_DEBUG MAIL_LOGTAG " \
"TZ XAUTHORITY DISPLAY LANG=C " \
- "POSTLOG_SERVICE POSTLOG_HOSTNAME"
+ "POSTLOG_SERVICE POSTLOG_HOSTNAME" \
+ "XDG_RUNTIME_DIR"
extern char *var_import_environ;
#define VAR_EXPORT_ENVIRON "export_environment"
#define MAIL_ATTR_COMPAT_LEVEL "compatibility_level"
#define MAIL_ATTR_MAIL_VERSION "mail_version"
+#define MAIL_ATTR_INST_FLAGS "instance_flags"
+
/*
* Suffixes for sender_name, sender_domain etc.
*/
* Patches change both the patchlevel and the release date. Snapshots have no
* patchlevel; they change the release date only.
*/
-#define MAIL_RELEASE_DATE "20250709"
+#define MAIL_RELEASE_DATE "20250713"
#define MAIL_VERSION_NUMBER "3.11"
#ifdef SNAPSHOT
static void (*event_server_pre_disconn) (VSTREAM *, char *, char **);
static void (*event_server_slow_exit) (char *, char **);
static int event_server_watchdog = 1000;
+static int event_server_drain_was_called = 0;
/* event_server_exit - normal termination */
const char *myname = "event_server_drain";
int fd;
+ if (event_server_drain_was_called)
+ return;
+
switch (fork()) {
/* Try again later. */
case -1:
msg_warn("%s: dup2(%d, %d): %m", myname, STDIN_FILENO, fd);
}
var_use_limit = 1;
+ event_server_drain_was_called = 1;
return (0);
/* Let the master start a new process. */
default:
int time_left = -1;
int fd;
+ if (event_server_drain_was_called)
+ return;
+
/*
* Be prepared for accept() to fail because some other process already
* got the connection (the number of processes competing for clients is
if (event_server_pre_accept)
event_server_pre_accept(event_server_name, event_server_argv);
+ if (event_server_drain_was_called)
+ return;
fd = LOCAL_ACCEPT(listen_fd);
if (event_server_lock != 0
&& myflock(vstream_fileno(event_server_lock), INTERNAL_LOCK,
int fd;
HTABLE *attr = 0;
+ if (event_server_drain_was_called)
+ return;
+
/*
* Be prepared for accept() to fail because some other process already
* got the connection (the number of processes competing for clients is
if (event_server_pre_accept)
event_server_pre_accept(event_server_name, event_server_argv);
+ if (event_server_drain_was_called)
+ return;
fd = pass_accept_attr(listen_fd, &attr);
if (event_server_lock != 0
&& myflock(vstream_fileno(event_server_lock), INTERNAL_LOCK,
int time_left = -1;
int fd;
+ if (event_server_drain_was_called)
+ return;
+
/*
* Be prepared for accept() to fail because some other process already
* got the connection (the number of processes competing for clients is
if (event_server_pre_accept)
event_server_pre_accept(event_server_name, event_server_argv);
+ if (event_server_drain_was_called)
+ return;
fd = inet_accept(listen_fd);
if (event_server_lock != 0
&& myflock(vstream_fileno(event_server_lock), INTERNAL_LOCK,
static int multi_server_in_flow_delay;
static unsigned multi_server_generation;
static void (*multi_server_pre_disconn) (VSTREAM *, char *, char **);
+static int multi_server_drain_was_called = 0;
/* multi_server_exit - normal termination */
const char *myname = "multi_server_drain";
int fd;
+ if (multi_server_drain_was_called)
+ return;
+
switch (fork()) {
/* Try again later. */
case -1:
msg_warn("%s: dup2(%d, %d): %m", myname, STDIN_FILENO, fd);
}
var_use_limit = 1;
+ multi_server_drain_was_called = 1;
return (0);
/* Let the master start a new process. */
default:
int time_left = -1;
int fd;
+ if (multi_server_drain_was_called)
+ return;
+
/*
* Be prepared for accept() to fail because some other process already
* got the connection (the number of processes competing for clients is
if (multi_server_pre_accept)
multi_server_pre_accept(multi_server_name, multi_server_argv);
+ if (multi_server_drain_was_called)
+ return;
fd = LOCAL_ACCEPT(listen_fd);
if (multi_server_lock != 0
&& myflock(vstream_fileno(multi_server_lock), INTERNAL_LOCK,
int fd;
HTABLE *attr = 0;
+ if (multi_server_drain_was_called)
+ return;
+
/*
* Be prepared for accept() to fail because some other process already
* got the connection (the number of processes competing for clients is
if (multi_server_pre_accept)
multi_server_pre_accept(multi_server_name, multi_server_argv);
+ if (multi_server_drain_was_called)
+ return;
fd = pass_accept_attr(listen_fd, &attr);
if (multi_server_lock != 0
&& myflock(vstream_fileno(multi_server_lock), INTERNAL_LOCK,
int time_left = -1;
int fd;
+ if (multi_server_drain_was_called)
+ return;
+
/*
* Be prepared for accept() to fail because some other process already
* got the connection (the number of processes competing for clients is
if (multi_server_pre_accept)
multi_server_pre_accept(multi_server_name, multi_server_argv);
+ if (multi_server_drain_was_called)
+ return;
fd = inet_accept(listen_fd);
if (multi_server_lock != 0
&& myflock(vstream_fileno(multi_server_lock), INTERNAL_LOCK,
const char *value;
int func;
- if (strcmp(map_type, DICT_TYPE_PROXY) == 0)
- msg_fatal("can't sequence maps via the proxy service");
dict = dict_open3(map_type, map_name, O_RDONLY, dict_flags);
for (func = DICT_SEQ_FUN_FIRST; /* void */ ; func = DICT_SEQ_FUN_NEXT) {
if (dict_seq(dict, func, &key, &value) != 0)
const char *value;
int func;
- if (strcmp(map_type, DICT_TYPE_PROXY) == 0)
- msg_fatal("can't sequence maps via the proxy service");
dict = dict_open3(map_type, map_name, O_RDONLY, dict_flags);
for (func = DICT_SEQ_FUN_FIRST; /* void */ ; func = DICT_SEQ_FUN_NEXT) {
if (dict_seq(dict, func, &key, &value) != 0)
/* proxy_map_find - look up or open table */
-static DICT *proxy_map_find(const char *map_type_name, int request_flags,
+static DICT *proxy_map_find(const char *map_type_name, int inst_flags,
int *statp)
{
DICT *dict;
/*
* Open one instance of a map for each combination of name+flags.
- *
- * Assume that a map instance can be shared among clients with different
- * paranoia flag settings and with different map lookup flag settings.
- *
- * XXX The open() flags are passed implicitly, via the selection of the
- * service name. For a more sophisticated interface, appropriate subsets
- * of open() flags should be received directly from the client.
*/
- vstring_sprintf(map_type_name_flags, "%s:%s", map_type_name,
- dict_flags_str(request_flags & DICT_FLAG_INST_MASK));
- if (msg_verbose)
- msg_info("proxy_map_find: %s", STR(map_type_name_flags));
- if ((dict = dict_handle(STR(map_type_name_flags))) == 0) {
- dict = dict_open(map_type_name, proxy_writer ?
- WRITE_OPEN_FLAGS : READ_OPEN_FLAGS,
- request_flags);
- if (dict == 0)
- msg_panic("proxy_map_find: dict_open null result");
- dict_register(STR(map_type_name_flags), dict);
- }
+ dict = dict_open(map_type_name, proxy_writer ?
+ WRITE_OPEN_FLAGS : READ_OPEN_FLAGS,
+ inst_flags);
+ if (dict == 0)
+ msg_panic("proxy_map_find: dict_open null result");
dict->error = 0;
return (dict);
}
static void proxymap_sequence_service(VSTREAM *client_stream)
{
+ int inst_flags;
int request_flags;
DICT *dict;
int request_func;
*/
if (attr_scan(client_stream, ATTR_FLAG_STRICT,
RECV_ATTR_STR(MAIL_ATTR_TABLE, request_map),
+ RECV_ATTR_INT(MAIL_ATTR_INST_FLAGS, &inst_flags),
RECV_ATTR_INT(MAIL_ATTR_FLAGS, &request_flags),
RECV_ATTR_INT(MAIL_ATTR_FUNC, &request_func),
- ATTR_TYPE_END) != 3
+ ATTR_TYPE_END) != 4
|| (request_func != DICT_SEQ_FUN_FIRST
&& request_func != DICT_SEQ_FUN_NEXT)) {
reply_status = PROXY_STAT_BAD;
reply_key = reply_value = "";
- } else if ((dict = proxy_map_find(STR(request_map), request_flags,
+ } else if ((dict = proxy_map_find(STR(request_map), inst_flags,
&reply_status)) == 0) {
reply_key = reply_value = "";
} else {
- dict->flags = ((dict->flags & ~DICT_FLAG_RQST_MASK)
- | (request_flags & DICT_FLAG_RQST_MASK));
+ dict->flags = request_flags;
dict_status = dict_seq(dict, request_func, &reply_key, &reply_value);
if (dict_status == 0) {
reply_status = PROXY_STAT_OK;
*/
attr_print(client_stream, ATTR_FLAG_NONE,
SEND_ATTR_INT(MAIL_ATTR_STATUS, reply_status),
+ SEND_ATTR_INT(MAIL_ATTR_FLAGS, dict->flags),
SEND_ATTR_STR(MAIL_ATTR_KEY, reply_key),
SEND_ATTR_STR(MAIL_ATTR_VALUE, reply_value),
ATTR_TYPE_END);
static void proxymap_lookup_service(VSTREAM *client_stream)
{
+ int inst_flags;
int request_flags;
DICT *dict;
const char *reply_value;
*/
if (attr_scan(client_stream, ATTR_FLAG_STRICT,
RECV_ATTR_STR(MAIL_ATTR_TABLE, request_map),
+ RECV_ATTR_INT(MAIL_ATTR_INST_FLAGS, &inst_flags),
RECV_ATTR_INT(MAIL_ATTR_FLAGS, &request_flags),
RECV_ATTR_STR(MAIL_ATTR_KEY, request_key),
- ATTR_TYPE_END) != 3) {
+ ATTR_TYPE_END) != 4) {
reply_status = PROXY_STAT_BAD;
reply_value = "";
- } else if ((dict = proxy_map_find(STR(request_map), request_flags,
+ } else if ((dict = proxy_map_find(STR(request_map), inst_flags,
&reply_status)) == 0) {
reply_value = "";
- } else if (dict->flags = ((dict->flags & ~DICT_FLAG_RQST_MASK)
- | (request_flags & DICT_FLAG_RQST_MASK)),
+ } else if (dict->flags = request_flags,
(reply_value = dict_get(dict, STR(request_key))) != 0) {
reply_status = PROXY_STAT_OK;
} else if (dict->error == 0) {
*/
attr_print(client_stream, ATTR_FLAG_NONE,
SEND_ATTR_INT(MAIL_ATTR_STATUS, reply_status),
+ SEND_ATTR_INT(MAIL_ATTR_FLAGS, dict->flags),
SEND_ATTR_STR(MAIL_ATTR_VALUE, reply_value),
ATTR_TYPE_END);
}
static void proxymap_update_service(VSTREAM *client_stream)
{
+ int inst_flags;
int request_flags;
DICT *dict;
int dict_status;
*/
if (attr_scan(client_stream, ATTR_FLAG_STRICT,
RECV_ATTR_STR(MAIL_ATTR_TABLE, request_map),
+ RECV_ATTR_INT(MAIL_ATTR_INST_FLAGS, &inst_flags),
RECV_ATTR_INT(MAIL_ATTR_FLAGS, &request_flags),
RECV_ATTR_STR(MAIL_ATTR_KEY, request_key),
RECV_ATTR_STR(MAIL_ATTR_VALUE, request_value),
- ATTR_TYPE_END) != 4) {
+ ATTR_TYPE_END) != 5) {
reply_status = PROXY_STAT_BAD;
} else if (proxy_writer == 0) {
msg_warn("refusing %s update request on non-%s service",
STR(request_map), MAIL_SERVICE_PROXYWRITE);
reply_status = PROXY_STAT_DENY;
- } else if ((dict = proxy_map_find(STR(request_map), request_flags,
+ } else if ((dict = proxy_map_find(STR(request_map), inst_flags,
&reply_status)) == 0) {
/* void */ ;
} else {
- dict->flags = ((dict->flags & ~DICT_FLAG_RQST_MASK)
- | (request_flags & DICT_FLAG_RQST_MASK)
+ /* Sync the table now. Don't abort on duplicate update. */
+ dict->flags = (request_flags
| DICT_FLAG_SYNC_UPDATE | DICT_FLAG_DUP_REPLACE);
dict_status = dict_put(dict, STR(request_key), STR(request_value));
if (dict_status == 0) {
*/
attr_print(client_stream, ATTR_FLAG_NONE,
SEND_ATTR_INT(MAIL_ATTR_STATUS, reply_status),
+ SEND_ATTR_INT(MAIL_ATTR_FLAGS, dict->flags),
ATTR_TYPE_END);
}
static void proxymap_delete_service(VSTREAM *client_stream)
{
+ int inst_flags;
int request_flags;
DICT *dict;
int dict_status;
*/
if (attr_scan(client_stream, ATTR_FLAG_STRICT,
RECV_ATTR_STR(MAIL_ATTR_TABLE, request_map),
+ RECV_ATTR_INT(MAIL_ATTR_INST_FLAGS, &inst_flags),
RECV_ATTR_INT(MAIL_ATTR_FLAGS, &request_flags),
RECV_ATTR_STR(MAIL_ATTR_KEY, request_key),
- ATTR_TYPE_END) != 3) {
+ ATTR_TYPE_END) != 4) {
reply_status = PROXY_STAT_BAD;
} else if (proxy_writer == 0) {
msg_warn("refusing %s delete request on non-%s service",
STR(request_map), MAIL_SERVICE_PROXYWRITE);
reply_status = PROXY_STAT_DENY;
- } else if ((dict = proxy_map_find(STR(request_map), request_flags,
+ } else if ((dict = proxy_map_find(STR(request_map), inst_flags,
&reply_status)) == 0) {
/* void */ ;
} else {
- dict->flags = ((dict->flags & ~DICT_FLAG_RQST_MASK)
- | (request_flags & DICT_FLAG_RQST_MASK)
+ /* Sync the table now. There is no close() request. */
+ dict->flags = (request_flags
| DICT_FLAG_SYNC_UPDATE);
dict_status = dict_del(dict, STR(request_key));
if (dict_status == 0) {
*/
attr_print(client_stream, ATTR_FLAG_NONE,
SEND_ATTR_INT(MAIL_ATTR_STATUS, reply_status),
+ SEND_ATTR_INT(MAIL_ATTR_FLAGS, dict->flags),
ATTR_TYPE_END);
}
static void proxymap_open_service(VSTREAM *client_stream)
{
- int request_flags;
+ int inst_flags;
DICT *dict;
int reply_status;
int reply_flags;
*/
if (attr_scan(client_stream, ATTR_FLAG_STRICT,
RECV_ATTR_STR(MAIL_ATTR_TABLE, request_map),
- RECV_ATTR_INT(MAIL_ATTR_FLAGS, &request_flags),
+ RECV_ATTR_INT(MAIL_ATTR_INST_FLAGS, &inst_flags),
ATTR_TYPE_END) != 2) {
reply_status = PROXY_STAT_BAD;
reply_flags = 0;
- } else if ((dict = proxy_map_find(STR(request_map), request_flags,
+ } else if ((dict = proxy_map_find(STR(request_map), inst_flags,
&reply_status)) == 0) {
reply_flags = 0;
} else {
*/
MAIL_VERSION_STAMP_ALLOCATE;
- /*
- * Workaround for programs that make explicit dict_register() calls with
- * a table that is already registered under a different name. This is
- * safe only in programs that do not unregister or close a table that is
- * registered with multiple names.
- */
- dict_allow_multiple_dict_register_names = 1;
-
/*
* XXX When invoked with the master.cf service name "proxywrite", the
* proxymap daemon will allow update requests. To update a table that is
/* const char *name,
/* int open_flags,
/* int dict_flags)
-/*
-/* int dict_allow_multiple_dict_register_names;
/* DESCRIPTION
/* This module maintains a collection of name-value dictionaries.
/* Each dictionary has its own name and has its own methods to read
/* This encourages consistent sharing of dictionary instances that
/* have the exact same type:name and (initial) flags. The result
/* value is the string value of the \fIout\fR VSTRING buffer.
-/*
-/* dict_allow_multiple_dict_register_names enables a temporary
-/* workaround for programs that make explicit dict_register()
-/* calls with a table that is already registered under a different
-/* name. Setting this to non-zero allows a dictionary to be
-/* registered under multiple names. This workaround is safe only
-/* in programs that do not unregister or close a table that is
-/* registered with multiple names.
/* TRUST AND PROVENANCE
/* .ad
/* .fi
dict = node->dict; \
} while (0)
- /*
- * Workaround for programs that make explicit dict_register() calls with
- * tables that are already registered under a different name. This is safe
- * only in programs that do not unregister or close a table that is
- * registered with multiple names.
- */
-int dict_allow_multiple_dict_register_names = 0;
-
#define STR(x) vstring_str(x)
/* dict_register_close - trigger dictionary cleanup */
/*
* Enforce referential integrity.
*/
- if (dict_allow_multiple_dict_register_names == 0
- && dict_info->reg_name && strcmp(dict_name, dict_info->reg_name) != 0)
+ if (dict_info->reg_name && strcmp(dict_name, dict_info->reg_name) != 0)
msg_panic("%s: '%s:%s' is already registered under '%s' and cannot "
"also be registered under '%s'", myname, dict_info->type,
dict_info->name, dict_info->reg_name, dict_name);
* The subsets of flags that control how a map is used. These are relevant
* mainly for proxymap support. Note: some categories overlap.
*
- * DICT_FLAG_IMPL_MASK - flags that are set by the map implementation itself.
- *
* DICT_FLAG_PARANOID - requestor flags that forbid the use of insecure map
* types for security-sensitive operations. These flags are checked by the
* map implementation itself upon open, lookup etc. requests.
- *
- * DICT_FLAG_RQST_MASK - all requestor flags, including paranoid flags, that
- * the requestor may change between open, lookup etc. requests. These
- * specify requestor properties, not map properties.
- *
- * DICT_FLAG_INST_MASK - none of the above flags. The requestor may not change
- * these flags between open, lookup, etc. requests (although a map may make
- * changes to its copy of some of these flags). The proxymap server opens
- * only one map instance for all client requests with the same values of
- * these flags, and the proxymap client uses its own saved copy of these
- * flags. DICT_FLAG_SRC_RHS_IS_FILE is an example of such a flag.
*/
#define DICT_FLAG_PARANOID \
(DICT_FLAG_NO_REGSUB | DICT_FLAG_NO_PROXY | DICT_FLAG_NO_UNAUTH)
-#define DICT_FLAG_IMPL_MASK (DICT_FLAG_FIXED | DICT_FLAG_PATTERN | \
- DICT_FLAG_MULTI_WRITER)
-#define DICT_FLAG_RQST_MASK (DICT_FLAG_FOLD_ANY | DICT_FLAG_LOCK | \
- DICT_FLAG_DUP_REPLACE | DICT_FLAG_DUP_WARN | \
- DICT_FLAG_DUP_IGNORE | DICT_FLAG_SYNC_UPDATE | \
- DICT_FLAG_PARANOID | DICT_FLAG_UTF8_MASK)
-#define DICT_FLAG_INST_MASK ~(DICT_FLAG_IMPL_MASK | DICT_FLAG_RQST_MASK)
/*
* Feature tests.
extern char *dict_make_registered_name(VSTRING *, const char *, int, int);
extern char *dict_make_registered_name4(VSTRING *, const char *, const char *, int, int);
- /*
- * Workaround for programs that make explicit dict_register() calls with a
- * table that is already registered under a different name. This is safe
- * only in programs that do not unregister or close a table that is
- * registered with multiple names.
- */
-extern int dict_allow_multiple_dict_register_names;
-
/*
* This name is reserved for matchlist error handling.
*/
dict->type = mystrdup(dict_type);
dict->name = mystrdup(dict_name);
- dict->flags = DICT_FLAG_FIXED;
+ dict->flags = 0;
dict->lookup = dict_default_lookup;
dict->update = dict_default_update;
dict->delete = dict_default_delete;
vstream_printf("dict flags %s\n",
dict_flags_str(dict->flags));
} else if (strcmp(cmd, "masks") == 0 && !key && !value) {
- vstream_printf("DICT_FLAG_IMPL_MASK %s\n",
- dict_flags_str(DICT_FLAG_IMPL_MASK));
vstream_printf("DICT_FLAG_PARANOID %s\n",
dict_flags_str(DICT_FLAG_PARANOID));
- vstream_printf("DICT_FLAG_RQST_MASK %s\n",
- dict_flags_str(DICT_FLAG_RQST_MASK));
- vstream_printf("DICT_FLAG_INST_MASK %s\n",
- dict_flags_str(DICT_FLAG_INST_MASK));
} else {
vstream_printf("usage: %s\n", USAGE);
}