diff --git a/postfix/HISTORY b/postfix/HISTORY
index 0f2e25b15..8a2ba1794 100644
--- a/postfix/HISTORY
+++ b/postfix/HISTORY
@@ -29345,3 +29345,53 @@ Apologies for any names omitted.
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é.
diff --git a/postfix/README_FILES/COMPATIBILITY_README b/postfix/README_FILES/COMPATIBILITY_README
index e69de29bb..8c3a0b71b 100644
--- a/postfix/README_FILES/COMPATIBILITY_README
+++ b/postfix/README_FILES/COMPATIBILITY_README
@@ -0,0 +1,459 @@
+PPoossttffiixx BBaacckkwwaarrddss--CCoommppaattiibbiilliittyy SSaaffeettyy NNeett
+
+-------------------------------------------------------------------------------
+
+PPuurrppoossee ooff tthhiiss ddooccuummeenntt
+
+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.
+
+OOvveerrvviieeww
+
+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.
+
+UUssiinngg bbaacckkwwaarrddss--ccoommppaattiibbllee ddeeffaauulltt sseettttiinngg aappppeenndd__ddoott__mmyyddoommaaiinn==yyeess
+
+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 "ppoossttffiixx rreellooaadd".
+
+ * 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:
+
+ # ppoossttccoonnff aappppeenndd__ddoott__mmyyddoommaaiinn==yyeess
+ # ppoossttffiixx rreellooaadd
+
+UUssiinngg bbaacckkwwaarrddss--ccoommppaattiibbllee ddeeffaauulltt sseettttiinngg cchhrroooott==yy
+
+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:
+
+ # ppoossttccoonnff --FF ssmmttpp//iinneett//cchhrroooott==yy
+ # ppoossttffiixx rreellooaadd
+
+UUssiinngg bbaacckkwwaarrddss--ccoommppaattiibbllee ddeeffaauulltt sseettttiinngg ssmmttppdd__rreellaayy__rreessttrriiccttiioonnss == ((eemmppttyy))
+
+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:
+
+ # ppoossttccoonnff ssmmttppdd__rreellaayy__rreessttrriiccttiioonnss==
+ # ppoossttffiixx rreellooaadd
+
+UUssiinngg bbaacckkwwaarrddss--ccoommppaattiibbllee ddeeffaauulltt sseettttiinngg ssmmttppuuttff88__eennaabbllee==nnoo
+
+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:
+
+ # ppoossttccoonnff ssmmttppuuttff88__eennaabbllee==nnoo
+ # ppoossttffiixx rreellooaadd
+
+UUssiinngg bbaacckkwwaarrddss--ccoommppaattiibbllee ddeeffaauulltt sseettttiinngg mmyynneettwwoorrkkss__ssttyyllee==ssuubbnneett
+
+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:
+
+ # ppoossttccoonnff mmyynneettwwoorrkkss__ssttyyllee==ssuubbnneett
+ # ppoossttffiixx rreellooaadd
+
+UUssiinngg bbaacckkwwaarrddss--ccoommppaattiibbllee ddeeffaauulltt sseettttiinngg rreellaayy__ddoommaaiinnss==$$mmyyddeessttiinnaattiioonn
+
+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:
+
+ # ppoossttccoonnff ''rreellaayy__ddoommaaiinnss==$$mmyyddeessttiinnaattiioonn''
+ # ppoossttffiixx rreellooaadd
+
+Note: quotes are required as indicated above.
+
+Instead of $mydestination, it may be better to specify an explicit list of
+domain names.
+
+UUssiinngg bbaacckkwwaarrddss--ccoommppaattiibbllee ddeeffaauulltt sseettttiinngg ssmmttppdd__ttllss__ffiinnggeerrpprriinntt__ddiiggeesstt==mmdd55
+
+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.
+
+ # ppoossttccoonnff ssmmttppdd__ttllss__ffiinnggeerrpprriinntt__ddiiggeesstt==mmdd55
+ # ppoossttffiixx rreellooaadd
+
+UUssiinngg bbaacckkwwaarrddss--ccoommppaattiibbllee ddeeffaauulltt sseettttiinngg ssmmttpp__ttllss__ffiinnggeerrpprriinntt__ddiiggeesstt==mmdd55
+
+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.
+
+ # ppoossttccoonnff ''ssmmttpp__ttllss__ffiinnggeerrpprriinntt__ddiiggeesstt == mmdd55'' \\
+ ''llmmttpp__ttllss__ffiinnggeerrpprriinntt__ddiiggeesstt == mmdd55''
+ # ppoossttffiixx rreellooaadd
+
+UUssiinngg bbaacckkwwaarrddss--ccoommppaattiibbllee ddeeffaauulltt sseettttiinngg
+ssmmttppdd__rreellaayy__bbeeffoorree__rreecciippiieenntt__rreessttrriiccttiioonnss==nnoo
+
+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:
+
+ # ppoossttccoonnff ssmmttppdd__rreellaayy__bbeeffoorree__rreecciippiieenntt__rreessttrriiccttiioonnss==nnoo
+ # ppoossttffiixx rreellooaadd
+
+UUssiinngg bbaacckkwwaarrddss--ccoommppaattiibbllee ddeeffaauulltt sseettttiinngg rreessppeeccttffuull__llooggggiinngg==nnoo
+
+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.
+
+ # ppoossttccoonnff ""rreessppeeccttffuull__llooggggiinngg == nnoo""
+ # ppoossttffiixx rreellooaadd
+
+UUssiinngg bbaacckkwwaarrddss--ccoommppaattiibbllee ddeeffaauulltt sseettttiinngg
+ssmmttpp__ttllssrrpptt__sskkiipp__rreeuusseedd__hhaannddsshhaakkeess==yyeess
+
+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:
+
+ # ppoossttccoonnff ssmmttpp__ttllssrrpptt__sskkiipp__rreeuusseedd__hhaannddsshhaakkeess==yyeess
+ # ppoossttffiixx rreellooaadd
+
+UUssiinngg bbaacckkwwaarrddss--ccoommppaattiibbllee ddeeffaauulltt sseettttiinngg xxxxxx__sseeccuurriittyy__lleevveell==((eemmppttyy))
+
+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:
+
+ # ppoossttccoonnff xxxxxx__sseeccuurriittyy__lleevveell==
+ # ppoossttffiixx rreellooaadd
+
+where xxx is taken from the above compatibility message.
+
+TTuurrnniinngg ooffff tthhee bbaacckkwwaarrddss--ccoommppaattiibbiilliittyy ssaaffeettyy nneett
+
+Backwards compatibility is turned off by updating the compatibility_level
+setting in main.cf.
+
+ # ppoossttccoonnff ccoommppaattiibbiilliittyy__lleevveell==NN
+ # ppoossttffiixx rreellooaadd
+
+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 "
/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 ++ +
# 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 diff --git a/postfix/man/man5/postconf.5 b/postfix/man/man5/postconf.5 index 8baada969..8c2039ecb 100644 --- a/postfix/man/man5/postconf.5 +++ b/postfix/man/man5/postconf.5 @@ -2633,13 +2633,16 @@ Needed for debugging Postfix daemons with an X\-windows debugger. .IP "\fBMAIL_CONFIG\fR" Needed to make "\fBpostfix \-c\fR" work. .br +.IP "\fBPOSTLOG_HOSTNAME\fR" +Needed to make "\fBmaillog_file\fR" work during daemon +process initialization. +.br .IP "\fBPOSTLOG_SERVICE\fR" Needed to make "\fBmaillog_file\fR" work during daemon process initialization. .br -.IP "\fBPOSTLOG_HOSTNAME\fR" -Needed to make "\fBmaillog_file\fR" work during daemon -process initialization. +.IP "\fBXDG_RUNTIME_DIR\fR" +Needed for debugging Postfix daemons with an XDG\-style debugger. .br .br .PP @@ -7892,6 +7895,14 @@ Examples: .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 diff --git a/postfix/mantools/check-spell-proto-html b/postfix/mantools/check-spell-proto-html index b81ce1f97..ffd427f96 100755 --- a/postfix/mantools/check-spell-proto-html +++ b/postfix/mantools/check-spell-proto-html @@ -4,4 +4,4 @@ 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 diff --git a/postfix/proto/COMPATIBILITY_README.html b/postfix/proto/COMPATIBILITY_README.html index 000f06269..20d72b423 100644 --- a/postfix/proto/COMPATIBILITY_README.html +++ b/postfix/proto/COMPATIBILITY_README.html @@ -614,7 +614,7 @@ make the backwards-compatible setting "smtp_tlsrpt_skip_reused_handshakes default setting xxx_security_level=(empty)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.
diff --git a/postfix/proto/postconf.proto b/postfix/proto/postconf.proto index e6aa3680b..0a4a9255e 100644 --- a/postfix/proto/postconf.proto +++ b/postfix/proto/postconf.proto @@ -1984,15 +1984,20 @@ environment. Examples of relevant environment variables:
/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 ++ +
# 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 diff --git a/postfix/proto/stop b/postfix/proto/stop index c09b1c34e..5a7a3b12a 100644 --- a/postfix/proto/stop +++ b/postfix/proto/stop @@ -1683,3 +1683,4 @@ typofix LD PRELOAD rhansen +XDG diff --git a/postfix/proto/stop.double-history b/postfix/proto/stop.double-history index cf5da6405..6fb0842b7 100644 --- a/postfix/proto/stop.double-history +++ b/postfix/proto/stop.double-history @@ -179,3 +179,5 @@ proto proto COMPATIBILITY_README html 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 diff --git a/postfix/proto/stop.spell-history b/postfix/proto/stop.spell-history index da067ab97..42b996fab 100644 --- a/postfix/proto/stop.spell-history +++ b/postfix/proto/stop.spell-history @@ -106,3 +106,7 @@ Kozmenko Oleksandr Bataille balancers +Unbroke +XDG +ENOTSOCK +Fustà diff --git a/postfix/src/global/dict_proxy.c b/postfix/src/global/dict_proxy.c index 0157d01a2..a0ce1b2e0 100644 --- a/postfix/src/global/dict_proxy.c +++ b/postfix/src/global/dict_proxy.c @@ -113,6 +113,7 @@ static int dict_proxy_sequence(DICT *dict, int function, VSTREAM *stream; int status; int count = 0; + int inst_flags; int request_flags; /* @@ -126,8 +127,8 @@ static int dict_proxy_sequence(DICT *dict, int function, 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; @@ -136,15 +137,17 @@ static int dict_proxy_sequence(DICT *dict, int function, || 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 { @@ -194,6 +197,7 @@ static const char *dict_proxy_lookup(DICT *dict, const char *key) VSTREAM *stream; int status; int count = 0; + int inst_flags; int request_flags; /* @@ -205,8 +209,8 @@ static const char *dict_proxy_lookup(DICT *dict, const char *key) */ 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; @@ -215,14 +219,16 @@ static const char *dict_proxy_lookup(DICT *dict, const char *key) || 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 { @@ -267,6 +273,7 @@ static int dict_proxy_update(DICT *dict, const char *key, const char *value) VSTREAM *stream; int status; int count = 0; + int inst_flags; int request_flags; /* @@ -276,8 +283,8 @@ static int dict_proxy_update(DICT *dict, const char *key, const char *value) * 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; @@ -286,6 +293,7 @@ static int dict_proxy_update(DICT *dict, const char *key, const char *value) || 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), @@ -293,7 +301,8 @@ static int dict_proxy_update(DICT *dict, const char *key, const char *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 { @@ -337,6 +346,7 @@ static int dict_proxy_delete(DICT *dict, const char *key) VSTREAM *stream; int status; int count = 0; + int inst_flags; int request_flags; /* @@ -346,8 +356,8 @@ static int dict_proxy_delete(DICT *dict, const char *key) * 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; @@ -356,13 +366,15 @@ static int dict_proxy_delete(DICT *dict, const char *key) || 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); @@ -478,16 +490,20 @@ DICT *dict_proxy_open(const char *map, int open_flags, int dict_flags) 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); @@ -496,7 +512,7 @@ DICT *dict_proxy_open(const char *map, int open_flags, int dict_flags) || 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, @@ -512,14 +528,17 @@ DICT *dict_proxy_open(const char *map, int open_flags, int dict_flags) 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", diff --git a/postfix/src/global/mail_params.h b/postfix/src/global/mail_params.h index 690256a60..cf7d791ea 100644 --- a/postfix/src/global/mail_params.h +++ b/postfix/src/global/mail_params.h @@ -2663,7 +2663,8 @@ extern int var_fflush_refresh; #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" diff --git a/postfix/src/global/mail_proto.h b/postfix/src/global/mail_proto.h index 798de6694..d784ce42d 100644 --- a/postfix/src/global/mail_proto.h +++ b/postfix/src/global/mail_proto.h @@ -210,6 +210,8 @@ extern char *mail_pathname(const char *, const char *); #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. */ diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h index f19314a40..a1d0ccefd 100644 --- a/postfix/src/global/mail_version.h +++ b/postfix/src/global/mail_version.h @@ -20,7 +20,7 @@ * Patches change both the patchlevel and the release date. Snapshots have no * patchlevel; they change the release date only. */ -#define MAIL_RELEASE_DATE "20250709" +#define MAIL_RELEASE_DATE "20250713" #define MAIL_VERSION_NUMBER "3.11" #ifdef SNAPSHOT diff --git a/postfix/src/master/event_server.c b/postfix/src/master/event_server.c index 9802bdf2f..30e80ba5e 100644 --- a/postfix/src/master/event_server.c +++ b/postfix/src/master/event_server.c @@ -273,6 +273,7 @@ static unsigned event_server_generation; 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 */ @@ -327,6 +328,9 @@ int event_server_drain(void) const char *myname = "event_server_drain"; int fd; + if (event_server_drain_was_called) + return; + switch (fork()) { /* Try again later. */ case -1: @@ -343,6 +347,7 @@ int event_server_drain(void) 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: @@ -445,6 +450,9 @@ static void event_server_accept_local(int unused_event, void *context) 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 @@ -457,6 +465,8 @@ static void event_server_accept_local(int unused_event, void *context) 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, @@ -483,6 +493,9 @@ static void event_server_accept_pass(int unused_event, void *context) 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 @@ -495,6 +508,8 @@ static void event_server_accept_pass(int unused_event, void *context) 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, @@ -520,6 +535,9 @@ static void event_server_accept_inet(int unused_event, void *context) 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 @@ -532,6 +550,8 @@ static void event_server_accept_inet(int unused_event, void *context) 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, diff --git a/postfix/src/master/multi_server.c b/postfix/src/master/multi_server.c index 6150f229a..51004d6d0 100644 --- a/postfix/src/master/multi_server.c +++ b/postfix/src/master/multi_server.c @@ -260,6 +260,7 @@ static VSTREAM *multi_server_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 */ @@ -295,6 +296,9 @@ int multi_server_drain(void) const char *myname = "multi_server_drain"; int fd; + if (multi_server_drain_was_called) + return; + switch (fork()) { /* Try again later. */ case -1: @@ -311,6 +315,7 @@ int multi_server_drain(void) 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: @@ -429,6 +434,9 @@ static void multi_server_accept_local(int unused_event, void *context) 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 @@ -441,6 +449,8 @@ static void multi_server_accept_local(int unused_event, void *context) 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, @@ -467,6 +477,9 @@ static void multi_server_accept_pass(int unused_event, void *context) 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 @@ -479,6 +492,8 @@ static void multi_server_accept_pass(int unused_event, void *context) 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, @@ -504,6 +519,9 @@ static void multi_server_accept_inet(int unused_event, void *context) 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 @@ -516,6 +534,8 @@ static void multi_server_accept_inet(int unused_event, void *context) 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, diff --git a/postfix/src/postalias/postalias.c b/postfix/src/postalias/postalias.c index c17b6a1d6..9e77a6b20 100644 --- a/postfix/src/postalias/postalias.c +++ b/postfix/src/postalias/postalias.c @@ -690,8 +690,6 @@ static void postalias_seq(const char *map_type, const char *map_name, 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) diff --git a/postfix/src/postmap/postmap.c b/postfix/src/postmap/postmap.c index 9e8c78381..8f4a83c37 100644 --- a/postfix/src/postmap/postmap.c +++ b/postfix/src/postmap/postmap.c @@ -899,8 +899,6 @@ static void postmap_seq(const char *map_type, const char *map_name, 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) diff --git a/postfix/src/proxymap/proxymap.c b/postfix/src/proxymap/proxymap.c index 3a00c33c1..270e491a0 100644 --- a/postfix/src/proxymap/proxymap.c +++ b/postfix/src/proxymap/proxymap.c @@ -320,7 +320,7 @@ static char *get_nested_dict_name(char *type_name) /* 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; @@ -354,26 +354,12 @@ static DICT *proxy_map_find(const char *map_type_name, int request_flags, /* * 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); } @@ -382,6 +368,7 @@ static DICT *proxy_map_find(const char *map_type_name, int request_flags, static void proxymap_sequence_service(VSTREAM *client_stream) { + int inst_flags; int request_flags; DICT *dict; int request_func; @@ -395,19 +382,19 @@ static void proxymap_sequence_service(VSTREAM *client_stream) */ 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; @@ -426,6 +413,7 @@ static void proxymap_sequence_service(VSTREAM *client_stream) */ 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); @@ -435,6 +423,7 @@ static void proxymap_sequence_service(VSTREAM *client_stream) static void proxymap_lookup_service(VSTREAM *client_stream) { + int inst_flags; int request_flags; DICT *dict; const char *reply_value; @@ -445,16 +434,16 @@ static void proxymap_lookup_service(VSTREAM *client_stream) */ 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) { @@ -471,6 +460,7 @@ static void proxymap_lookup_service(VSTREAM *client_stream) */ 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); } @@ -479,6 +469,7 @@ static void proxymap_lookup_service(VSTREAM *client_stream) static void proxymap_update_service(VSTREAM *client_stream) { + int inst_flags; int request_flags; DICT *dict; int dict_status; @@ -495,21 +486,22 @@ static void proxymap_update_service(VSTREAM *client_stream) */ 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) { @@ -527,6 +519,7 @@ static void proxymap_update_service(VSTREAM *client_stream) */ 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); } @@ -534,6 +527,7 @@ static void proxymap_update_service(VSTREAM *client_stream) static void proxymap_delete_service(VSTREAM *client_stream) { + int inst_flags; int request_flags; DICT *dict; int dict_status; @@ -547,20 +541,21 @@ static void proxymap_delete_service(VSTREAM *client_stream) */ 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) { @@ -578,6 +573,7 @@ static void proxymap_delete_service(VSTREAM *client_stream) */ 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); } @@ -585,7 +581,7 @@ static void proxymap_delete_service(VSTREAM *client_stream) static void proxymap_open_service(VSTREAM *client_stream) { - int request_flags; + int inst_flags; DICT *dict; int reply_status; int reply_flags; @@ -595,11 +591,11 @@ static void proxymap_open_service(VSTREAM *client_stream) */ 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 { @@ -843,14 +839,6 @@ int main(int argc, char **argv) */ 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 diff --git a/postfix/src/util/dict.c b/postfix/src/util/dict.c index 87dee602f..943772325 100644 --- a/postfix/src/util/dict.c +++ b/postfix/src/util/dict.c @@ -82,8 +82,6 @@ /* 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 @@ -187,14 +185,6 @@ /* 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 @@ -339,14 +329,6 @@ typedef struct { 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 */ @@ -367,8 +349,7 @@ void dict_register(const char *dict_name, DICT *dict_info) /* * 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); diff --git a/postfix/src/util/dict.h b/postfix/src/util/dict.h index 595f18ed0..36def9b3c 100644 --- a/postfix/src/util/dict.h +++ b/postfix/src/util/dict.h @@ -141,32 +141,12 @@ extern void dict_free(DICT *); * 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. @@ -286,14 +266,6 @@ extern DICT *PRINTFLIKE(5, 6) dict_surrogate(const char *, const char *, int, in 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. */ diff --git a/postfix/src/util/dict_alloc.c b/postfix/src/util/dict_alloc.c index d780ecf97..bea8e7d7e 100644 --- a/postfix/src/util/dict_alloc.c +++ b/postfix/src/util/dict_alloc.c @@ -147,7 +147,7 @@ DICT *dict_alloc(const char *dict_type, const char *dict_name, ssize_t size) 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; diff --git a/postfix/src/util/dict_test.c b/postfix/src/util/dict_test.c index bd2cadd4d..26fcd0975 100644 --- a/postfix/src/util/dict_test.c +++ b/postfix/src/util/dict_test.c @@ -146,14 +146,8 @@ void dict_test(int argc, char **argv) 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); }