]> git.ipfire.org Git - thirdparty/postfix.git/commitdiff
postfix-2.12-20150117
authorWietse Venema <wietse@porcupine.org>
Sat, 17 Jan 2015 05:00:00 +0000 (00:00 -0500)
committerViktor Dukhovni <postfix-users@dukhovni.org>
Sun, 18 Jan 2015 18:49:22 +0000 (13:49 -0500)
143 files changed:
postfix/.indent.pro
postfix/HISTORY
postfix/README_FILES/ADDRESS_VERIFICATION_README
postfix/README_FILES/SMTPUTF8_README
postfix/README_FILES/TLS_README
postfix/WISHLIST
postfix/html/ADDRESS_VERIFICATION_README.html
postfix/html/SMTPUTF8_README.html
postfix/html/TLS_README.html
postfix/html/cleanup.8.html
postfix/html/lmtp.8.html
postfix/html/postalias.1.html
postfix/html/postconf.5.html
postfix/html/postmap.1.html
postfix/html/smtp.8.html
postfix/man/man1/postalias.1
postfix/man/man1/postmap.1
postfix/man/man5/postconf.5
postfix/man/man8/cleanup.8
postfix/man/man8/smtp.8
postfix/mantools/postlink
postfix/proto/ADDRESS_VERIFICATION_README.html
postfix/proto/SMTPUTF8_README.html
postfix/proto/TLS_README.html
postfix/proto/postconf.proto
postfix/src/bounce/Makefile.in
postfix/src/bounce/bounce.c
postfix/src/bounce/bounce_template.c
postfix/src/cleanup/cleanup.c
postfix/src/cleanup/cleanup_init.c
postfix/src/cleanup/cleanup_map1n.c
postfix/src/cleanup/cleanup_masquerade.c
postfix/src/flush/flush.c
postfix/src/global/Makefile.in
postfix/src/global/addr_match_list.c
postfix/src/global/addr_match_list.h
postfix/src/global/cleanup_strflags.c
postfix/src/global/db_common.c
postfix/src/global/debug_peer.c
postfix/src/global/dict_ldap.c
postfix/src/global/dict_proxy.c
postfix/src/global/dict_proxy.h
postfix/src/global/dict_sqlite.c
postfix/src/global/domain_list.c
postfix/src/global/domain_list.h
postfix/src/global/flush_clnt.c
postfix/src/global/mail_addr_find.c
postfix/src/global/mail_addr_map.c
postfix/src/global/mail_params.c
postfix/src/global/mail_params.h
postfix/src/global/mail_version.h
postfix/src/global/match_parent_style.c
postfix/src/global/midna_adomain.c [new file with mode: 0644]
postfix/src/global/midna_adomain.h [new file with mode: 0644]
postfix/src/global/mkmap_open.c
postfix/src/global/namadr_list.c
postfix/src/global/namadr_list.h
postfix/src/global/namadr_list.ref
postfix/src/global/resolve_local.c
postfix/src/global/resolve_local.ref
postfix/src/global/server_acl.c
postfix/src/global/server_acl.ref
postfix/src/global/string_list.c
postfix/src/global/string_list.h
postfix/src/global/tok822_tree.c
postfix/src/global/user_acl.c
postfix/src/global/user_acl.h
postfix/src/local/local.c
postfix/src/local/mailbox.c
postfix/src/local/unknown.c
postfix/src/postalias/postalias.c
postfix/src/postconf/postconf_master.c
postfix/src/postdrop/postdrop.c
postfix/src/postmap/postmap.c
postfix/src/postqueue/postqueue.c
postfix/src/postscreen/postscreen.c
postfix/src/posttls-finger/Makefile.in
postfix/src/posttls-finger/posttls-finger.c
postfix/src/proxymap/proxymap.c
postfix/src/qmqpd/qmqpd.c
postfix/src/sendmail/sendmail.c
postfix/src/smtp/Makefile.in
postfix/src/smtp/lmtp_params.c
postfix/src/smtp/smtp.c
postfix/src/smtp/smtp_addr.c
postfix/src/smtp/smtp_connect.c
postfix/src/smtp/smtp_map11.c
postfix/src/smtp/smtp_params.c
postfix/src/smtp/smtp_proto.c
postfix/src/smtp/smtp_sasl_auth_cache.c
postfix/src/smtp/smtp_sasl_glue.c
postfix/src/smtp/smtp_tls_policy.c
postfix/src/smtpd/Makefile.in
postfix/src/smtpd/smtpd.c
postfix/src/smtpd/smtpd_check.c
postfix/src/smtpd/smtpd_error.ref
postfix/src/tls/Makefile.in
postfix/src/tls/tls.h
postfix/src/tls/tls_client.c
postfix/src/tls/tls_dh.c
postfix/src/tls/tls_misc.c
postfix/src/tls/tls_rsa.c
postfix/src/tls/tls_scache.c
postfix/src/tls/tls_server.c
postfix/src/trivial-rewrite/resolve.c
postfix/src/trivial-rewrite/transport.c
postfix/src/trivial-rewrite/trivial-rewrite.c
postfix/src/util/Makefile.in
postfix/src/util/casefold.c [new file with mode: 0644]
postfix/src/util/casefold_test.in [new file with mode: 0644]
postfix/src/util/casefold_test.ref [new file with mode: 0644]
postfix/src/util/dict.c
postfix/src/util/dict.h
postfix/src/util/dict_alloc.c
postfix/src/util/dict_cdb.c
postfix/src/util/dict_debug.c
postfix/src/util/dict_inline.c
postfix/src/util/dict_inline.ref
postfix/src/util/dict_lmdb.c
postfix/src/util/dict_open.c
postfix/src/util/dict_test.c
postfix/src/util/dict_test.ref
postfix/src/util/dict_thash.c
postfix/src/util/dict_thash.map
postfix/src/util/dict_utf8.c [new file with mode: 0644]
postfix/src/util/dict_utf8_test.in [new file with mode: 0644]
postfix/src/util/dict_utf8_test.ref [new file with mode: 0644]
postfix/src/util/host_port.c
postfix/src/util/match_list.c
postfix/src/util/match_list.h
postfix/src/util/match_ops.c
postfix/src/util/midna.c [deleted file]
postfix/src/util/midna_domain.c [new file with mode: 0644]
postfix/src/util/midna_domain.h [moved from postfix/src/util/midna.h with 54% similarity]
postfix/src/util/midna_domain_test.in [new file with mode: 0644]
postfix/src/util/midna_domain_test.ref [new file with mode: 0644]
postfix/src/util/midna_test.in [deleted file]
postfix/src/util/midna_test.ref [deleted file]
postfix/src/util/printable.c
postfix/src/util/stringops.h
postfix/src/util/valid_utf8_hostname.c
postfix/src/verify/verify.c
postfix/src/virtual/virtual.c

index b9f3a9ea13396362747966a7760e8da152eecf42..c15aa635347ce43fb7e3480b316b0795993d362b 100644 (file)
 -TDICT_THASH
 -TDICT_UNION
 -TDICT_UNIX
+-TDICT_UTF8_BACKUP
 -TDNS_FIXED
 -TDNS_REPLY
 -TDNS_RR
index 4b8cf432ebea3e0917334d4c0dad4c3ae9a2b052..318c8c7542a99fd2b5db5ba9bcf7fd534f2c51cd 100644 (file)
@@ -21209,3 +21209,206 @@ Apologies for any names omitted.
        either their result is a valid ASCII domain name or that
        it converts into a valid ASCII domain name. Files:
        util/midna.c, util/midna_test.in, util/midna_test.ref.
+
+20141230
+
+       Cleanup: s/midna/midna_domain/ for better specificity,
+       because we also need functions that act only on the domain
+       portion of an email address. Files: bounce/bounce_template.c,
+       global/midna_adomain.c, posttls-finger/posttls-finger.c,
+       smtp/smtp_addr.c, smtpd/smtpd_check.c, tls/tls_client.c,
+       util/midna_domain.[hc], util/valid_utf8_hostname.c.
+
+       Infrastructure: function midna_adomain_to_utf8() (and
+       midna_adomain_to_ascii) to convert the domain portion of
+       an email address before table lookup. Files:
+       global/midna_adomain.[hc].
+
+20141230-20140109
+
+       What is described here is the result of four iterations to
+       deal with malformed UTF-8 without massively contaminating
+       every Postfix program with new error-handling code paths,
+       in particular without triggering fatal errors that didn't
+       happen before.
+
+       Infrastructure: function casefold() to support caseless
+       string comparison, primarily for table lookups. This function
+       supports two modes: case folding a la lowercase() for ASCII
+       byte values, and UTF-8 case folding. As recommended at
+       http://www.w3.org/International/wiki/Case_folding for
+       caseless string comparison, this uses the en_US locale to
+       avoid surprises. The implementatin handles
+        the entire RFC 3629 Unicode range (code points U+0000..U+10FFFF
+        including surrogates) and is chroot(2) safe. Files: casefold.c, stringops.h.
+
+       Infrastructure: revised the midna_domain_to_ascii and
+       midna_domain_to_utf8 domain name conversion functions after
+       careful reading of the UTS #46 specification, and after
+       observing that ICU 4.8 library functions indeed implement
+       this spec, at least with default options. In particular,
+       midna_domain_to_utf8 takes an UTF-8 domain name and verifies
+       that its A-label form will pass the valid_hostname() test.
+       File: util/midna_domain.c.
+
+       Infrastructure: handle UTF-8 errors in lookup table keys
+       or values without massively contaminating every Postfix
+       program with new error-handling code paths, in particular
+       without triggering fatal errors that didn't happen before.
+       The lookup/update/delete functions log a warning and ignore
+       a request with a bad key (it cannot exist); the update
+       functions ignore a request to store a bad value (it cannot
+       exist); and the lookup function reports a bad value as a
+       configuration error (it should not exist, but there it is).
+       Table iterators still report all (key, value) pairs in a
+       table. Files: util/dict.h, util/dict_open.c, util/dict_utf8.c,
+       global/mkmap_open.c.
+
+       Note that with SMTPUTF8 turned on, each table-driven mechanism
+       (access, aliases, etc.) needs to make its own decision
+       whether UTF-8 syntax is required. We cannot blindly require
+       that everything has valid UTF-8 syntax. That would make
+       header/body_checks useless for content inspection, because
+       headers may be malformed and bodies may contain legitimate
+       binary content that isn't UTF-8.
+
+       Note that with SMTPUTF8 turned off, Postfix must remain
+       8-bit clean as it always has been. Table operations must
+       not complain that something violates UTF-8 syntax rules.
+
+       UTF-8 sanitization in the Postfix SMTP server.  With
+       smtputf8_enable=yes, SMTP commands with UTF-8 syntax errors
+       are rejected, table lookup results with invalid UTF-8 syntax
+       are handled as configuration errors, and UTF-8 syntax errors
+       in policy server replies result in execution of the policy
+       server's default action.
+
+20150102
+
+       Cleanup: propagate DICT_ERR_CONFIG through the proxymap
+       protocol. Files: global/dict_proxy.[hc], proxymap/proxymap.c.
+
+20150106
+
+       Robustness: don't segfault due to excessive recursion in
+       tok822_free_tree() after a faulty configuration runs into
+       the virtual_alias_recursion_limit.  File: global/tok822_tree.c.
+
+20150109
+
+       Cleanup: the dict debug module now proxies dict flags.
+       File: util/dict_debug.c.
+
+       With "smtputf8_enable = yes", the postmap and postalias
+       commands now enable UTF-8 by default (use "-u" to disable)
+       with one exception: UTF-8 remains disabled for header/body_checks
+       emulation (use "-U" to enable).  Files: postmap/postmap.c,
+       postalias/postalias.c.
+
+20150110
+
+       Cleanup: the "inline" and "texthash" implementations now
+       reuse the "internal" database instead of reinventing the
+       wheel.  Files: util/dict_inline.c, util/dict_thash.c.
+
+       As a first step, with "smtputf8_enable = yes" all features
+       based on Postfix matchlists enable UTF-8 syntax checks and
+       UTF-8 casefolding for table patterns, but NOT YET for string
+       patterns. The list of features includes authorized_flush_users,
+       authorized_mailq_users, authorized_submit_users, debug_peer_list,
+       fast_flush_domains, mydestination, permit_mx_backup_networks,
+       qmqpd_authorized_clients, smtp_connection_cache_destinations,
+       smtpd_authorized_verp_clients, smtpd_authorized_xclient_hosts,
+       smtpd_authorized_xforward_hosts,
+       smtpd_client_event_limit_exceptions,
+       smtpd_log_access_permit_actions, smtpd_sasl_exceptions_networks,
+       the "domains" feature in ldap_table(5), memcache_table(5)
+       mysql_table(5), pgsql_table(5) and sqlite_table(5),
+       virtual_alias_domains, virtual_mailbox_domains.
+
+20150111
+
+       Cleanup: simplified the interposition layer that adds UTF-8
+       support to Postfix lookup tables. Files: util/dict_utf8.c.
+
+       With "smtputf8_enable = yes", Enable UTF-8 syntax checks
+       and UTF-8 casefolding for SMTP server access maps, alias_maps,
+       canonical_maps, fallback_transport_maps,
+       lmtp_tls_session_cache_database, local_recipient_maps,
+       mailbox_command_maps, mailbox_transport_maps, rbl_reply_maps,
+       recipient_bcc_maps, recipient_canonical_maps, relay_recipient_maps,
+       relocated_maps, sender_bcc_maps, sender_canonical_maps,
+       sender_dependent_relayhost_maps, sender_dependent_transport_maps,
+       smtp_generic_maps, smtp_sasl_auth_cache_name,
+       smtp_sasl_password_maps, smtp_tls_per_site, smtp_tls_policy_maps,
+       smtp_tls_session_cache_database, smtpd_sender_login_maps,
+       smtpd_tls_session_cache_database, transport_maps,
+       virtual_alias_maps, virtual_gid_maps, virtual_mailbox_maps,
+       virtual_uid_maps.
+
+20150112
+
+       Infrastructure: support for UTF-8 casefolding in match_lists.
+       Instead of using strcasecmp(), casefold all fixed-string
+       patterns during initialization, casefold a search string
+       at the beginning of the search, and use strcmp() for
+       comparison.  Files: util/casefold.c util/dict.h, util/dict_utf8.c,
+       util/match_list.c, util/match_list.h, util/match_ops.c,
+       util/stringops.h, global/addr_match_list.c, global/domain_list.c,
+       global/namadr_list.c, global/string_list.c.
+
+20150113
+
+       Cleanup: show the configuration parameter name in error
+       messages while parsing or searching match_list-based features
+       such as mydestination, relay_domains and a few dozen more.
+       Files: cleanup/cleanup_init.c, flush/flush.c,
+       global/addr_match_list.c, global/debug_peer.c,
+       global/domain_list.c, global/flush_clnt.c,
+       global/match_parent_style.c, global/namadr_list.c,
+       global/resolve_local.c, global/string_list.c, global/user_acl.[hc],
+       postdrop/postdrop.c, postqueue/postqueue.c,
+       postscreen/postscreen.c, qmqpd/qmqpd.c, sendmail/sendmail.c.,
+       smtp/smtp.c, smtp/smtp_sasl_glue.c, smtpd/smtpd.c,
+       smtpd/smtpd_check.c, trivial-rewrite/resolve.c,
+       util/match_list.[hc], util/match_ops.c.
+
+       Cleanup: apply printable() to all bounce(8) service
+       string-valued protocol fields. File: bounce/bounce.c.
+
+       Apparenly the UCI 4.8 ucasemap_utf8FoldCase() function does
+       not complain about UTF-8 syntax errors, so we add our own
+       redundant check. File: util/casefold.c.
+
+20150115
+
+       Bitrot: prepare for future changes in OpenSSL. Viktor
+       Dukhovni. Files: tls/tls.h, tls/tls_dh.c, tls/tls_misc.c,
+       tls/tls_rsa.c, tls/tls_server.c.
+
+       Documentation: "avoid hash files here, use btree or lmdb
+       instead".  File: proto/ADDRESS_VERIFICATION_README.html.
+
+       Safety: virtual_alias_address_length_limit (default: 1000)
+       to stop aliasing loops that exponentially increase the
+       address length with each iteration. Files: global/mail_params.h,
+       mantools/postlink, proto/postconf.proto, cleanup/cleanup.c,
+       cleanup/cleanup_init.c, cleanup/cleanup_map1n.c.
+
+20150116
+
+       TLS wrappermode in the Postfix smtp(8) client. This introduces
+       a new parameter "smtp_tls_wrappermode" (default: no). Files:
+       global/mail_params.h, mantools/postlink, proto/postconf.proto,
+       smtp/lmtp_params.c, smtp/smtp.[hc], smtp/smtp_connect.c,
+       smtp/smtp_params.c, smtp/smtp_proto.c.
+
+       TLS wrappermode in posttls-finger(1), and some DANE-related
+       cleanups. This introduces a new option "-w". Viktor Dukhovni.
+       Files: posttls-finger/posttls-finger.c, smtp/smtp_tls_policy.c,
+       tls/tls.h, tls/tls_client.c, tls/tls_fprint.c.
+
+20150117
+
+       Cleanup: missing " in \%s\" in postscreen(8) fatal error
+       messages. Iain Hibbert. File: postconf/postconf_master.c.
index 69a5f93d3004069db91494f1833586f77e06ae5c..fb99af30fd4bcdb53c96c97c7af3e4efa88d62a5 100644 (file)
@@ -204,7 +204,7 @@ verification for specific domains that often appear in forged email.
 
         # Default setting for Postfix 2.7 and later.
         # Note 1: Be sure to read the "Caching" section below!
-        # Note 2: Avoid hash files here. Use btree instead.
+        # Note 2: Avoid hash files here. Use btree or lmdb instead.
         address_verify_map = btree:/var/lib/postfix/verify
 
     /etc/postfix/sender_access:
@@ -245,7 +245,7 @@ be blocked:
 
         # Default setting for Postfix 2.7 and later.
         # Note 1: Be sure to read the "Caching" section below!
-        # Note 2: Avoid hash files here. Use btree instead.
+        # Note 2: Avoid hash files here. Use btree or lmdb instead.
         address_verify_map = btree:/var/lib/postfix/verify
 
 This is also a good way to populate your cache with address verification
@@ -297,7 +297,7 @@ verification results. If you specify an empty value, all address verification
 results are lost after "postfix reload" or "postfix stop".
 
     # Example 1: Default setting for Postfix 2.7 and later.
-    # Note: avoid hash files here. Use btree instead.
+    # Note: avoid hash files here. Use btree or lmdb instead.
     /etc/postfix/main.cf:
         address_verify_map = btree:$data_directory/verify_cache
 
index c0aa9ae8b8a20b8a745c4e73b917f9257468913d..dcc13a18970a66677ecb854ee9183bbb7044e80a 100644 (file)
@@ -65,8 +65,25 @@ With SMTPUTF8 support enabled, Postfix changes behavior with respect to earlier
 Postfix releases:
 
   * UTF-8 is permitted in the myorigin parameter value. However, the myhostname
-    and mydomain parameters must specify ASCII-only domain names. This
-    limitation may be removed later.
+    and mydomain parameters must currently specify ASCII-only domain names.
+    This limitation may be removed later.
+
+  * UTF-8 is the only form of non-ASCII text that Postfix supports in access
+    tables, address rewriting tables, and other tables that are indexed with an
+    email address, hostname, or domain name.
+
+  * The header_checks-like and body_checks-like features are not UTF-8 enabled,
+    and therefore they do not enforce UTF-8 syntax rules on inputs and outputs.
+    The reason is that non-ASCII text may be sent in encodings other than UTF-
+    8, and that real email sometimes contains malformed headers. Instead of
+    skipping non-UTF-8 content, Postfix should be able to filter it. You may
+    try to enable UTF-8 processing by starting a PCRE pattern with the sequence
+    (*UTF8), but this is will result in "message not accepted, try again later"
+    errors when the PCRE pattern matcher encounters non-UTF-8 input. Other
+    features that are not UTF-8 enabled are smtpd_command_filter,
+    smtp_reply_filter, the *_delivery_status_filter features, and the
+    *_dns_reply_filter features (the latter because DNS is by definition an
+    ASCII protocol).
 
   * The Postfix SMTP server announces SMTPUTF8 support in the EHLO response.
 
@@ -95,8 +112,8 @@ Postfix releases:
     commands.
 
   * The Postfix SMTP server accepts UTF-8 in email address domains, but only
-    after the remote SMTP client client issues the SMTPUTF8 request in MAIL
-    FROM or VRFY commands.
+    after the remote SMTP client issues the SMTPUTF8 request in MAIL FROM or
+    VRFY commands.
 
 Postfix already permitted UTF-8 in message header values and in address
 localparts. This does not change.
@@ -180,26 +197,38 @@ disabled.
 
 L\bLi\bim\bmi\bit\bta\bat\bti\bio\bon\bns\bs o\bof\bf t\bth\bhe\be c\bcu\bur\brr\bre\ben\bnt\bt i\bim\bmp\bpl\ble\bem\bme\ben\bnt\bta\bat\bti\bio\bon\bn
 
+The Postfix implementation is a work in progress; limitations are steadily
+being removed. The text below describes the situation at one point in time.
+
+N\bNo\bo a\bau\but\bto\bom\bma\bat\bti\bic\bc c\bco\bon\bnv\bve\ber\brs\bsi\bio\bon\bns\bs b\bbe\bet\btw\bwe\bee\ben\bn A\bAS\bSC\bCI\bII\bI a\ban\bnd\bd U\bUT\bTF\bF-\b-8\b8 d\bdo\bom\bma\bai\bin\bn n\bna\bam\bme\bes\bs.\b.
+
+Some background: According to RFC 6530 and related documents,
 "Internationalized" domain names can appear in two forms: the UTF-8 form, and
-the ASCII (xn--mumble) form. The initial Postfix SMTPUTF8 implementation
-performs no automatic conversions on UTF8 strings beyond what is needed to
-perform DNS lookups.
+the ASCII (xn--mumble) form. "Internationalized" address localparts must be
+encoded in UTF-8; the RFCs do not define an ASCII form for the same
+information.
+
+Postfix currently does not convert internationalized domain names from UTF-
+8 into ASCII (or from ASCII into UTF-8) before using domain names in SMTP
+commands and responses, before looking up domain names in mydestination,
+relay_domains, access tables, etc., before using domain names in a policy
+daemon or Milter request, or before logging domain names.
 
-N\bNo\bo c\bch\bha\bar\bra\bac\bct\bte\ber\brs\bse\bet\bt c\bca\ban\bno\bon\bni\bic\bca\bal\bli\biz\bza\bat\bti\bio\bon\bn f\bfo\bor\br n\bno\bon\bn-\b-A\bAS\bSC\bCI\bII\bI d\bdo\bom\bma\bai\bin\bn n\bna\bam\bme\bes\bs.\b.
+Postfix does, however, casefold domain names and email addresses before
+matching them against a Postfix configuration parameter or lookup table.
 
-Postfix currently does not translate domain names from UTF-8 into ASCII (or
-ASCII into UTF-8) before looking up the domain name in mydestination,
-relay_domains, access tables, etc., before logging the domain name, or before
-using the domain name in a policy daemon or Milter request. You will have to
-configure both UTF-8 and ASCII forms in Postfix configuration files; and both
-forms will have to be handled by logfile tools, policy daemons and Milters.
+  * The Postfix parameters myhostname and mydomain must be in ASCII form. One
+    is a substring of the other, and the myhostname value is used in SMTP
+    commands and responses that require ASCII. The parameter myorigin (added to
+    local addresses without domain) supports UTF-8.
 
-N\bNo\bo c\bca\bas\bse\be c\bca\ban\bno\bon\bni\bic\bca\bal\bli\biz\bza\bat\bti\bio\bon\bn f\bfo\bor\br n\bno\bon\bn-\b-A\bAS\bSC\bCI\bII\bI c\bch\bha\bar\bra\bac\bct\bte\ber\brs\bs.\b.
+  * You need to configure both the ASCII and UTF-8 forms of an
+    Internationalized domain name in Postfix parameters such as mydestination
+    and relay_domains, as well as lookup table search keys.
 
-Postfix currently does not case-fold non-ASCII characters when looking up an
-"Internationalized" domain name in mydestination, relay_domains, access maps,
-etc. Some non-ASCII scripts do not distinguish between upper and lower case,
-some have different numbers of upper and lower case characters.
+  * Milters, content filters, policy servers and logfile analysis tools need to
+    be able to handle both the ASCII and UTF-8 forms of Internationalized
+    domain names.
 
 C\bCo\bom\bmp\bpa\bat\bti\bib\bbi\bil\bli\bit\bty\by w\bwi\bit\bth\bh p\bpr\bre\be-\b-S\bSM\bMT\bTP\bPU\bUT\bTF\bF8\b8 e\ben\bnv\bvi\bir\bro\bon\bnm\bme\ben\bnt\bts\bs
 
@@ -209,28 +238,30 @@ With Postfix, there is no need to split mailing lists into UTF-8 and non-UTF-
 8 members. Postfix will try to deliver the non-UTF8 subscribers over
 "traditional" non-SMTPUTF8 sessions, as long as the message has an ASCII
 envelope sender address and all-ASCII header values. The mailing list manager
-will have to apply RFC 2047 encoding to satisfy that last condition.
+may have to apply RFC 2047 encoding to satisfy that last condition.
 
 P\bPr\bre\be-\b-e\bex\bxi\bis\bst\bti\bin\bng\bg n\bno\bon\bn-\b-A\bAS\bSC\bCI\bII\bI e\bem\bma\bai\bil\bl f\bfl\blo\bow\bws\bs
 
-In pre-SMTPUTF8 environments, email with UTF-8 in address localparts (and in
-headers) works just fine. The vast majority of email software including Postfix
-is perfectly capable of handling such email, even if pre-SMTPUTF8 standards do
-not support this.
+With "smtputf8_enable = no", Postfix handles email with non-ASCII in address
+localparts (and in headers) as before. The vast majority of email software is
+perfectly capable of handling such email, even if pre-SMTPUTF8 standards do not
+support such practice.
 
-Therefore, when Postfix SMTPUTF8 support is turned on, Postfix must not
-suddenly start to break pre-existing email flows with UTF-8 in addres
-localparts (and in headers).
-
-Thus, Postfix continues to permit UTF-8 in address localparts (and in headers)
-in email from and to pre-SMTPUTF8 systems. At least, that is the default (see
-autodetection above).
+However, when you specify "smtputf8_enable = yes", Postfix requires that non-
+ASCII address information is encoded in UTF-8 and will reject other encodings
+such as ISO-8859. It is not practical for Postfix to support multiple encodings
+at the same time. There is no problem with RFC 2047 encodings such as "=?ISO-
+8859-1?Q?text?=", because those use only characters from the ASCII
+characterset.
 
 C\bCr\bre\bed\bdi\bit\bts\bs
 
-  * Arnt Gulbrandsen posted his patch for Unicode email support on May 15,
-    2014. This work was sponsored by CNNIC.
+  * May 15, 2014: Arnt Gulbrandsen posted his patch for Unicode email support.
+    This work was sponsored by CNNIC.
+
+  * July 15, 2014: Wietse integrated Arnt Gulbrandsen's code and released
+    Postfix with SMTPUTF8 support.
 
-  * Wietse integrated Arnt Gulbrandsen's code and released Postfix with
-    SMTPUTF8 support on July 15, 2014.
+  * January 2015: Wietse added UTF-8 support for casefolding in Postfix lookup
+    tables and caseless string comparison in Postfix list-based features.
 
index adca4c825557f59f130dfdbc9fd4debe82423a4d..06ab191295b0bb97c0f54b404632e7a894fca55f 100644 (file)
@@ -1,3 +1,5 @@
+-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+
 P\bPo\bos\bst\btf\bfi\bix\bx T\bTL\bLS\bS S\bSu\bup\bpp\bpo\bor\brt\bt
 
 -------------------------------------------------------------------------------
@@ -1811,13 +1813,62 @@ Example:
 
 C\bCl\bli\bie\ben\bnt\bt-\b-s\bsi\bid\bde\be S\bSM\bMT\bTP\bPS\bS s\bsu\bup\bpp\bpo\bor\brt\bt
 
-Although the Postfix SMTP client by itself doesn't support TLS wrapper mode, it
+These sections show how to send mail to a server that does not support
+STARTTLS, but that provides the deprecated SMTPS service on TCP port 465.
+Depending on the Postfix version, some additional tooling may be required.
+
+P\bPo\bos\bst\btf\bfi\bix\bx >\b>=\b= 2\b2.\b.1\b12\b2
+
+The Postfix SMTP client has SMTPS support built-in as of version 2.12. Use one
+of the following examples, to send all remote mail, or to send only some remote
+mail, to an SMTPS server.
+
+P\bPo\bos\bst\btf\bfi\bix\bx >\b>=\b= 2\b2.\b.1\b12\b2:\b: S\bSe\ben\bnd\bdi\bin\bng\bg a\bal\bll\bl r\bre\bem\bmo\bot\bte\be m\bma\bai\bil\bl t\bto\bo a\ban\bn S\bSM\bMT\bTP\bPS\bS s\bse\ber\brv\bve\ber\br
+
+The first example will send all remote mail over SMTPS through a provider's
+server called "mail.example.com":
+
+    /etc/postfix/main.cf:
+        # Client-side SMTPS requires "encrypt" or stronger.
+        smtp_tls_security_level = encrypt
+        smtp_tls_wrappermode = yes
+        # The [] suppress MX lookups.
+        relayhost = [mail.example.com]:465
+
+Use "postfix reload" to make the change effective.
+
+See SOHO_README for additional information about SASL authentication.
+
+P\bPo\bos\bst\btf\bfi\bix\bx >\b>=\b= 2\b2.\b.1\b12\b2:\b: S\bSe\ben\bnd\bdi\bin\bng\bg o\bon\bnl\bly\by m\bma\bai\bil\bl f\bfo\bor\br a\ba s\bsp\bpe\bec\bci\bif\bfi\bic\bc d\bde\bes\bst\bti\bin\bna\bat\bti\bio\bon\bn v\bvi\bia\ba S\bSM\bMT\bTP\bPS\bS
+
+The second example will send only mail for "example.com" via SMTPS. This time,
+Postfix uses a transport map to deliver only mail for "example.com" via SMTPS:
+
+    /etc/postfix/main.cf:
+        transport_maps = hash:/etc/postfix/transport
+
+    /etc/postfix/transport:
+        example.com  relay-smtps:example.com:465
+
+    /etc/postfix/master.cf:
+        relay-smtps  unix  -       -       n       -       -       smtp
+            # Client-side SMTPS requires "encrypt" or stronger.
+            -o smtp_tls_security_level=encrypt
+            -o smtp_tls_wrappermode=yes
+
+Use "postmap hash:/etc/postfix/transport" and "postfix reload" to make the
+change effective.
+
+See SOHO_README for additional information about SASL authentication.
+
+P\bPo\bos\bst\btf\bfi\bix\bx <\b< 2\b2.\b.1\b12\b2
+
+Although older Postfix SMTP client versions do not support TLS wrapper mode, it
 is relatively easy to forward a connection through the stunnel program if
 Postfix needs to deliver mail to some legacy system that doesn't support
-STARTTLS. Use one of the following two examples, to send only some remote mail,
-or to send all remote mail, to an SMTPS server.
+STARTTLS.
 
-S\bSe\ben\bnd\bdi\bin\bng\bg a\bal\bll\bl r\bre\bem\bmo\bot\bte\be m\bma\bai\bil\bl t\bto\bo a\ban\bn S\bSM\bMT\bTP\bPS\bS s\bse\ber\brv\bve\ber\br
+P\bPo\bos\bst\btf\bfi\bix\bx <\b< 2\b2.\b.1\b12\b2:\bS\bSe\ben\bnd\bdi\bin\bng\bg a\bal\bll\bl r\bre\bem\bmo\bot\bte\be m\bma\bai\bil\bl t\bto\bo a\ban\bn S\bSM\bMT\bTP\bPS\bS s\bse\ber\brv\bve\ber\br
 
 The first example uses SMTPS to send all remote mail to a provider's mail
 server called "mail.example.com".
@@ -1847,7 +1898,9 @@ local stunnel listener on port 11125:
 
 Use "postfix reload" to make the change effective.
 
-S\bSe\ben\bnd\bdi\bin\bng\bg o\bon\bnl\bly\by m\bma\bai\bil\bl f\bfo\bor\br a\ba s\bsp\bpe\bec\bci\bif\bfi\bic\bc d\bde\bes\bst\bti\bin\bna\bat\bti\bio\bon\bn v\bvi\bia\ba S\bSM\bMT\bTP\bPS\bS
+See SOHO_README for additional information about SASL authentication.
+
+P\bPo\bos\bst\btf\bfi\bix\bx <\b< 2\b2.\b.1\b12\b2:\b: S\bSe\ben\bnd\bdi\bin\bng\bg o\bon\bnl\bly\by m\bma\bai\bil\bl f\bfo\bor\br a\ba s\bsp\bpe\bec\bci\bif\bfi\bic\bc d\bde\bes\bst\bti\bin\bna\bat\bti\bio\bon\bn v\bvi\bia\ba S\bSM\bMT\bTP\bPS\bS
 
 The second example will use SMTPS to send only mail for "example.com" via
 SMTPS. It uses the same stunnel configuration file as the first example, so it
@@ -1865,6 +1918,8 @@ This time, the Postfix side uses a transport map to direct only mail for
 Use "postmap hash:/etc/postfix/transport" and "postfix reload" to make the
 change effective.
 
+See SOHO_README for additional information about SASL authentication.
+
 M\bMi\bis\bsc\bce\bel\bll\bla\ban\bne\beo\bou\bus\bs c\bcl\bli\bie\ben\bnt\bt c\bco\bon\bnt\btr\bro\bol\bls\bs
 
 The smtp_starttls_timeout parameter limits the time of Postfix SMTP client
index 7621437f48cde2978796ba58c700c123dfa1b739..b83e5f0f3420bdc2a6dd7d09ec1f7058b6a00d6b 100644 (file)
@@ -8,6 +8,20 @@ Wish list:
 
        Things to do after the stable release:
 
+       UTF8 DNS[BW]L domain name.
+
+       Consolidate maps flags in mail_params.h instead of having
+       multiple copies scattered across programs.
+
+       Try to allow UTF-8 myhostname/mydomain, at least in bounce
+       template expansion.
+
+       No enhanced status code when rejecting connection before
+       the HELO handshake is completed.
+
+       Maybe don't whitelist a client that has maxed out its
+       per-MTA connection count limit.
+
        Inline support for pcre:{/pattern/=action, ...} and ditto
        support for regexp: and cidr: tables. Factor out and reuse
        code that already exists in inline: and other tables.
index b0fc5e4611d160d0e5d2f790b851cce21d5e81ff..14648f472aff77856a6b57e5ce55e721a376791d 100644 (file)
@@ -346,7 +346,7 @@ in forged email.  </p>
 
     # Default setting for Postfix 2.7 and later.
     # Note 1: Be sure to read the "<a href="#caching">Caching</a>" section below!
-    # Note 2: Avoid hash files here. Use btree instead.
+    # Note 2: Avoid hash files here. Use btree or lmdb instead.
     <a href="postconf.5.html#address_verify_map">address_verify_map</a> = <a href="DATABASE_README.html#types">btree</a>:/var/lib/postfix/verify
  
 /etc/postfix/sender_access:
@@ -393,7 +393,7 @@ you can see what mail would be blocked: </p>
 
     # Default setting for Postfix 2.7 and later.
     # Note 1: Be sure to read the "<a href="#caching">Caching</a>" section below!
-    # Note 2: Avoid hash files here. Use btree instead.
+    # Note 2: Avoid hash files here. Use btree or lmdb instead.
     <a href="postconf.5.html#address_verify_map">address_verify_map</a> = <a href="DATABASE_README.html#types">btree</a>:/var/lib/postfix/verify
 </pre>
 </blockquote>
@@ -461,7 +461,7 @@ results are lost after "postfix reload" or "postfix stop". </p>
 <blockquote>
 <pre>
 # Example 1: Default setting for Postfix 2.7 and later.
-# Note: avoid hash files here. Use btree instead.
+# Note: avoid hash files here. Use btree or lmdb instead.
 /etc/postfix/<a href="postconf.5.html">main.cf</a>:
     <a href="postconf.5.html#address_verify_map">address_verify_map</a> = <a href="DATABASE_README.html#types">btree</a>:$<a href="postconf.5.html#data_directory">data_directory</a>/verify_cache
 
index 3c3a593e5eb105d85d776779a2dc8a46439785a1..e64f83e9e0969cd6a4f46909b65962b3517294d8 100644 (file)
@@ -110,8 +110,27 @@ respect to earlier Postfix releases: </p>
 <ul>
 
 <li> <p> UTF-8 is permitted in the <a href="postconf.5.html#myorigin">myorigin</a> parameter value. However,
-the <a href="postconf.5.html#myhostname">myhostname</a> and <a href="postconf.5.html#mydomain">mydomain</a> parameters must specify ASCII-only
-domain names. This limitation may be removed later. </p>
+the <a href="postconf.5.html#myhostname">myhostname</a> and <a href="postconf.5.html#mydomain">mydomain</a> parameters must currently specify
+ASCII-only domain names. This limitation may be removed later. </p>
+
+<li> <p> UTF-8 is the only form of non-ASCII text that Postfix
+supports in access tables, address rewriting tables, and other
+tables that are indexed with an email address, hostname, or domain
+name. </p>
+
+<li> <p> The <a href="postconf.5.html#header_checks">header_checks</a>-like and <a href="postconf.5.html#body_checks">body_checks</a>-like features are
+not UTF-8 enabled, and therefore they do not enforce UTF-8 syntax
+rules on inputs and outputs.  The reason is that non-ASCII text may
+be sent in encodings other than UTF-8, and that real email sometimes
+contains malformed headers.  Instead of skipping non-UTF-8 content,
+Postfix should be able to filter it.  You may try to enable UTF-8
+processing by starting a PCRE pattern with the sequence (*UTF8),
+but this is will result in "message not accepted, try again later"
+errors when the PCRE pattern matcher encounters non-UTF-8 input.
+Other features that are not UTF-8 enabled are <a href="postconf.5.html#smtpd_command_filter">smtpd_command_filter</a>,
+<a href="postconf.5.html#smtp_reply_filter">smtp_reply_filter</a>, the *_delivery_status_filter features, and the
+*_dns_reply_filter features (the latter because DNS is by definition
+an ASCII protocol).  </p>
 
 <li> <p> The Postfix SMTP server announces SMTPUTF8 support in the
 EHLO response. </p>
@@ -145,7 +164,7 @@ MAIL FROM and VRFY commands. </p>
 MAIL FROM commands. </p>
 
 <li> <p> The Postfix SMTP server accepts UTF-8 in email address
-domains, but only after the remote SMTP client client issues the
+domains, but only after the remote SMTP client issues the
 SMTPUTF8 request in MAIL FROM or VRFY commands.  </p>
 
 </ul>
@@ -257,29 +276,47 @@ delivered it if SMTPUTF8 support was disabled. </p>
 <h2> <a name="limitations">Limitations of the current implementation</a>
 </h2>
 
-<p> "Internationalized" domain names can appear in two forms: the
-UTF-8 form, and the ASCII (xn--mumble) form. The initial Postfix
-SMTPUTF8 implementation performs no automatic conversions on UTF8
-strings beyond what is needed to perform DNS lookups. </p>
+<p> The Postfix implementation is a work in progress; limitations
+are steadily being removed. The text below describes the situation
+at one point in time. </p>
 
-<h3> No characterset canonicalization for non-ASCII domain names.
-</h3>
+<h3> No automatic conversions between ASCII and UTF-8 domain names. </h3>
 
-<p> Postfix currently does not translate domain names from UTF-8
-into ASCII (or ASCII into UTF-8) before looking up the domain name
-in <a href="postconf.5.html#mydestination">mydestination</a>, <a href="postconf.5.html#relay_domains">relay_domains</a>, access tables, etc., before logging
-the domain name, or before using the domain name in a policy daemon
-or Milter request. You will have to configure both UTF-8 and ASCII
-forms in Postfix configuration files; and both forms will have to
-be handled by logfile tools, policy daemons and Milters. </p>
+<p> Some background: According to <a href="http://tools.ietf.org/html/rfc6530">RFC 6530</a> and related documents,
+"Internationalized" domain names can appear in two forms: the UTF-8
+form, and the ASCII (xn--mumble) form. "Internationalized" address
+localparts must be encoded in UTF-8; the RFCs do not define an ASCII
+form for the same information. </p>
 
-<h3> No case canonicalization for non-ASCII characters. </h3>
+<p> Postfix currently does not convert internationalized domain
+names from UTF-8 into ASCII (or from ASCII into UTF-8) before using
+domain names in SMTP commands and responses, before looking up 
+domain names in <a href="postconf.5.html#mydestination">mydestination</a>, <a href="postconf.5.html#relay_domains">relay_domains</a>, access tables, etc.,
+before using domain names in a policy daemon or Milter request,
+or before logging domain names. </p>
 
-<p> Postfix currently does not case-fold non-ASCII characters when
-looking up an "Internationalized" domain name in <a href="postconf.5.html#mydestination">mydestination</a>,
-<a href="postconf.5.html#relay_domains">relay_domains</a>, access maps, etc. Some non-ASCII scripts do not
-distinguish between upper and lower case, some have different numbers
-of upper and lower case characters. </p>
+<p> Postfix does, however, casefold domain names and email addresses
+before matching them against a Postfix configuration parameter or
+lookup table. </p>
+
+<ul>
+
+<li> <p> The Postfix parameters <a href="postconf.5.html#myhostname">myhostname</a> and <a href="postconf.5.html#mydomain">mydomain</a> must be in
+ASCII form. One is a substring of the other, and the <a href="postconf.5.html#myhostname">myhostname</a>
+value is used in SMTP commands and responses that require ASCII.
+The parameter <a href="postconf.5.html#myorigin">myorigin</a> (added to local addresses without domain)
+supports UTF-8.  </p>
+
+<li> <p> You need to configure both the ASCII and UTF-8 forms of
+an Internationalized domain name in Postfix parameters such as
+<a href="postconf.5.html#mydestination">mydestination</a> and <a href="postconf.5.html#relay_domains">relay_domains</a>, as well as lookup table search
+keys. </p>
+
+<li> <p> Milters, content filters, policy servers and logfile
+analysis tools need to be able to handle both the ASCII and UTF-8
+forms of Internationalized domain names. </p>
+
+</ul>
 
 <h2> <a name="compatibility">Compatibility with pre-SMTPUTF8
 environments</a> </h2>
@@ -290,33 +327,36 @@ environments</a> </h2>
 non-UTF-8 members. Postfix will try to deliver the non-UTF8 subscribers
 over "traditional" non-SMTPUTF8 sessions, as long as the message
 has an ASCII envelope sender address and all-ASCII header values.
-The mailing list manager will have to apply <a href="http://tools.ietf.org/html/rfc2047">RFC 2047</a> encoding to
+The mailing list manager may have to apply <a href="http://tools.ietf.org/html/rfc2047">RFC 2047</a> encoding to
 satisfy that last condition. </p>
 
 <h3> Pre-existing non-ASCII email flows </h3>
 
-<p> In pre-SMTPUTF8 environments, email with UTF-8 in address
-localparts (and in headers) works just fine. The vast majority
-of email software including Postfix is perfectly capable of handling
-such email, even if pre-SMTPUTF8 standards do not support this. </p>
+<p> With "<a href="postconf.5.html#smtputf8_enable">smtputf8_enable</a> = no", Postfix handles email with non-ASCII
+in address localparts (and in headers) as before.  The vast majority
+of email software is perfectly capable of handling such email, even
+if pre-SMTPUTF8 standards do not support such practice. </p>
 
-<p> Therefore, when Postfix SMTPUTF8 support is turned on, Postfix
-must not suddenly start to break pre-existing email flows with UTF-8
-in addres localparts (and in headers). </p>
-
-<p> Thus, Postfix continues to permit UTF-8 in address localparts
-(and in headers) in email from and to pre-SMTPUTF8 systems.  At
-least, that is the default (see autodetection above). </p>
+<p> However, when you specify "<a href="postconf.5.html#smtputf8_enable">smtputf8_enable</a> = yes", Postfix
+requires that non-ASCII address information is encoded in UTF-8 and
+will reject other encodings such as ISO-8859. It is not practical
+for Postfix to support multiple encodings at the same time.  There
+is no problem with <a href="http://tools.ietf.org/html/rfc2047">RFC 2047</a> encodings such as "=?ISO-8859-1?Q?text?=",
+because those use only characters from the ASCII characterset.  </p>
 
 <h2> <a name="credits">Credits</a> </h2>
 
 <ul>
 
-<li> <p> Arnt Gulbrandsen posted his patch for Unicode email support
-on May 15, 2014.  This work was sponsored by CNNIC. </p>
+<li> <p> May 15, 2014: Arnt Gulbrandsen posted his patch for Unicode
+email support.  This work was sponsored by CNNIC. </p>
+
+<li> <p> July 15, 2014: Wietse integrated Arnt Gulbrandsen's code
+and released Postfix with SMTPUTF8 support. </p>
 
-<li> <p> Wietse integrated Arnt Gulbrandsen's code and released
-Postfix with SMTPUTF8 support on July 15, 2014. </p>
+<li> <p> January 2015: Wietse added UTF-8 support for casefolding
+in Postfix lookup tables and caseless string comparison in Postfix
+list-based features. </p>
 
 </ul>
 
index 2548b9f5761584241e216f36b255abe65d66ebc0..e8cd2fcfaa169437170ae101892ee14131984681 100644 (file)
@@ -1,4 +1,4 @@
-<!doctype html public "-//W3C//DTD HTML 4.01 Transitional//EN"
+<doctype html public "-//W3C//DTD HTML 4.01 Transitional//EN"
         "http://www.w3.org/TR/html4/loose.dtd">
 
 <html>
@@ -2370,14 +2370,75 @@ the SSL/TLS protocols used with opportunistic TLS. </p>
 
 <h3> <a name="client_smtps">Client-side SMTPS support </a> </h3>
 
-<p> Although the Postfix SMTP client by itself doesn't support TLS
+<p> These sections show how to send mail to a server that does not
+support STARTTLS, but that provides the deprecated SMTPS service
+on TCP port 465.  Depending on the Postfix version, some additional
+tooling may be required. </p>
+
+<h4> Postfix &ge; 2.12 </h4>
+
+<p> The Postfix SMTP client has SMTPS support built-in as of version
+2.12.  Use one of the following examples, to send all remote mail,
+or to send only some remote mail, to an SMTPS server.  </p> </p>
+
+<h5> Postfix &ge; 2.12: Sending all remote mail to an SMTPS server </h5>
+
+<p> The first example will send all remote mail over SMTPS through
+a provider's server called "mail.example.com": </p>
+
+<blockquote>
+<pre>
+/etc/postfix/<a href="postconf.5.html">main.cf</a>:
+    # Client-side SMTPS requires "encrypt" or stronger.
+    <a href="postconf.5.html#smtp_tls_security_level">smtp_tls_security_level</a> = encrypt
+    <a href="postconf.5.html#smtp_tls_wrappermode">smtp_tls_wrappermode</a> = yes
+    # The [] suppress MX lookups.
+    <a href="postconf.5.html#relayhost">relayhost</a> = [mail.example.com]:465
+</pre>
+</blockquote>
+
+<p> Use "postfix reload" to make the change effective. </p>
+
+<p> See <a href="SOHO_README.html">SOHO_README</a> for additional information about SASL authentication.
+</p>
+
+<h5> Postfix &ge; 2.12: Sending only mail for a specific destination
+via SMTPS </h5>
+
+<p> The second example will send only mail for "example.com" via
+SMTPS.  This time, Postfix uses a transport map to deliver only
+mail for "example.com" via SMTPS: </p>
+
+<blockquote>
+<pre>
+/etc/postfix/<a href="postconf.5.html">main.cf</a>:
+    <a href="postconf.5.html#transport_maps">transport_maps</a> = <a href="DATABASE_README.html#types">hash</a>:/etc/postfix/transport
+
+/etc/postfix/transport:
+    example.com  relay-smtps:example.com:465
+
+/etc/postfix/<a href="master.5.html">master.cf</a>:
+    relay-smtps  unix  -       -       n       -       -       smtp
+        # Client-side SMTPS requires "encrypt" or stronger.
+        -o <a href="postconf.5.html#smtp_tls_security_level">smtp_tls_security_level</a>=encrypt
+        -o <a href="postconf.5.html#smtp_tls_wrappermode">smtp_tls_wrappermode</a>=yes
+</pre>
+</blockquote>
+
+<p> Use "postmap <a href="DATABASE_README.html#types">hash</a>:/etc/postfix/transport" and "postfix reload"
+to make the change effective. </p>
+
+<p> See <a href="SOHO_README.html">SOHO_README</a> for additional information about SASL
+authentication. </p>
+
+<h4> Postfix &lt; 2.12 </h4>
+
+<p> Although older Postfix SMTP client versions do not support TLS
 wrapper mode, it is relatively easy to forward a connection through
 the stunnel program if Postfix needs to deliver mail to some legacy
-system that doesn't support STARTTLS. Use one of the following two
-examples, to send only some remote mail, or to send all remote mail,
-to an SMTPS server.  </p>
+system that doesn't support STARTTLS. </p>
 
-<h4> Sending all remote mail to an SMTPS server </h4>
+<h5> Postfix &lt; 2.12: Sending all remote mail to an SMTPS server </h5>
 
 <p> The first example uses SMTPS to send all remote mail to a
 provider's mail server called "mail.example.com".  </p>
@@ -2420,7 +2481,10 @@ mail through the local stunnel listener on port 11125: </p>
 
 <p> Use "postfix reload" to make the change effective. </p>
 
-<h4> Sending only mail for a specific destination via SMTPS </h4>
+<p> See <a href="SOHO_README.html">SOHO_README</a> for additional information about SASL
+authentication. </p>
+
+<h4> Postfix &lt; 2.12: Sending only mail for a specific destination via SMTPS </h4>
 
 <p> The second example will use SMTPS to send only mail for
 "example.com" via SMTPS. It uses the same stunnel configuration
@@ -2442,6 +2506,9 @@ mail for "example.com" through the tunnel: </p>
 <p> Use "postmap <a href="DATABASE_README.html#types">hash</a>:/etc/postfix/transport" and "postfix reload"
 to make the change effective. </p>
 
+<p> See <a href="SOHO_README.html">SOHO_README</a> for additional information about SASL authentication.
+</p>
+
 <h3> <a name="client_misc"> Miscellaneous client controls </a> </h3>
 
 <p> The <a href="postconf.5.html#smtp_starttls_timeout">smtp_starttls_timeout</a> parameter limits the time of Postfix
index d17607c2d34ac9290cdebd143e8582df9bbace19..6e14e5837cbd7c82674e75fa05e50921c946ad21 100644 (file)
@@ -390,29 +390,35 @@ CLEANUP(8)                                                          CLEANUP(8)
        <b><a href="postconf.5.html#virtual_alias_recursion_limit">virtual_alias_recursion_limit</a> (1000)</b>
               The maximal nesting depth of virtual alias expansion.
 
+       Available in Postfix version 2.12 and later:
+
+       <b>virtual_alias_address_length_limit (1000)</b>
+              The maximal length of  an  email  address  after  virtual  alias
+              expansion.
+
 <b>MISCELLANEOUS CONTROLS</b>
        <b><a href="postconf.5.html#config_directory">config_directory</a> (see 'postconf -d' output)</b>
-              The default location of the Postfix <a href="postconf.5.html">main.cf</a> and  <a href="master.5.html">master.cf</a>  con-
+              The  default  location of the Postfix <a href="postconf.5.html">main.cf</a> and <a href="master.5.html">master.cf</a> con-
               figuration files.
 
        <b><a href="postconf.5.html#daemon_timeout">daemon_timeout</a> (18000s)</b>
-              How  much  time  a  Postfix  daemon process may take to handle a
+              How much time a Postfix daemon process  may  take  to  handle  a
               request before it is terminated by a built-in watchdog timer.
 
        <b><a href="postconf.5.html#delay_logging_resolution_limit">delay_logging_resolution_limit</a> (2)</b>
-              The maximal number of digits after the decimal point  when  log-
+              The  maximal  number of digits after the decimal point when log-
               ging sub-second delay values.
 
        <b><a href="postconf.5.html#delay_warning_time">delay_warning_time</a> (0h)</b>
-              The  time  after which the sender receives a copy of the message
+              The time after which the sender receives a copy of  the  message
               headers of mail that is still queued.
 
        <b><a href="postconf.5.html#ipc_timeout">ipc_timeout</a> (3600s)</b>
-              The time limit for sending  or  receiving  information  over  an
+              The  time  limit  for  sending  or receiving information over an
               internal communication channel.
 
        <b><a href="postconf.5.html#max_idle">max_idle</a> (100s)</b>
-              The  maximum  amount of time that an idle Postfix daemon process
+              The maximum amount of time that an idle Postfix  daemon  process
               waits for an incoming connection before terminating voluntarily.
 
        <b><a href="postconf.5.html#max_use">max_use</a> (100)</b>
@@ -423,7 +429,7 @@ CLEANUP(8)                                                          CLEANUP(8)
               The internet hostname of this mail system.
 
        <b><a href="postconf.5.html#myorigin">myorigin</a> ($<a href="postconf.5.html#myhostname">myhostname</a>)</b>
-              The domain name that locally-posted mail appears to  come  from,
+              The  domain  name that locally-posted mail appears to come from,
               and that locally posted mail is delivered to.
 
        <b><a href="postconf.5.html#process_id">process_id</a> (read-only)</b>
@@ -436,15 +442,15 @@ CLEANUP(8)                                                          CLEANUP(8)
               The location of the Postfix top-level queue directory.
 
        <b><a href="postconf.5.html#soft_bounce">soft_bounce</a> (no)</b>
-              Safety  net to keep mail queued that would otherwise be returned
+              Safety net to keep mail queued that would otherwise be  returned
               to the sender.
 
        <b><a href="postconf.5.html#syslog_facility">syslog_facility</a> (mail)</b>
               The syslog facility of Postfix logging.
 
        <b><a href="postconf.5.html#syslog_name">syslog_name</a> (see 'postconf -d' output)</b>
-              The mail system name that is prepended to the  process  name  in
-              syslog  records,  so  that  "smtpd" becomes, for example, "post-
+              The  mail  system  name that is prepended to the process name in
+              syslog records, so that "smtpd"  becomes,  for  example,  "post-
               fix/smtpd".
 
        Available in Postfix version 2.1 and later:
index 54d6807f149cb8df575b556078748f8bf1797912..705c5ab227545e7975a35f36a0bacf231cb09a9e 100644 (file)
@@ -555,50 +555,56 @@ SMTP(8)                                                                SMTP(8)
        <b><a href="postconf.5.html#tlsmgr_service_name">tlsmgr_service_name</a> (tlsmgr)</b>
               The name of the <a href="tlsmgr.8.html"><b>tlsmgr</b>(8)</a> service entry in <a href="master.5.html">master.cf</a>.
 
+       Available in Postfix version 2.12 and later:
+
+       <b><a href="postconf.5.html#smtp_tls_wrappermode">smtp_tls_wrappermode</a> (no)</b>
+              Request  that  the Postfix SMTP client connects using the legacy
+              SMTPS protocol instead of using the STARTTLS command.
+
 <b>OBSOLETE STARTTLS CONTROLS</b>
-       The  following  configuration  parameters  exist for compatibility with
-       Postfix versions before 2.3. Support for these will  be  removed  in  a
+       The following configuration parameters  exist  for  compatibility  with
+       Postfix  versions  before  2.3.  Support for these will be removed in a
        future release.
 
        <b><a href="postconf.5.html#smtp_use_tls">smtp_use_tls</a> (no)</b>
-              Opportunistic  mode: use TLS when a remote SMTP server announces
+              Opportunistic mode: use TLS when a remote SMTP server  announces
               STARTTLS support, otherwise send the mail in the clear.
 
        <b><a href="postconf.5.html#smtp_enforce_tls">smtp_enforce_tls</a> (no)</b>
-              Enforcement mode: require  that  remote  SMTP  servers  use  TLS
+              Enforcement  mode:  require  that  remote  SMTP  servers use TLS
               encryption, and never send mail in the clear.
 
        <b><a href="postconf.5.html#smtp_tls_enforce_peername">smtp_tls_enforce_peername</a> (yes)</b>
-              With  mandatory  TLS  encryption,  require  that the remote SMTP
-              server hostname matches  the  information  in  the  remote  SMTP
+              With mandatory TLS encryption,  require  that  the  remote  SMTP
+              server  hostname  matches  the  information  in  the remote SMTP
               server certificate.
 
        <b><a href="postconf.5.html#smtp_tls_per_site">smtp_tls_per_site</a> (empty)</b>
-              Optional  lookup  tables  with the Postfix SMTP client TLS usage
-              policy by next-hop destination and by remote SMTP  server  host-
+              Optional lookup tables with the Postfix SMTP  client  TLS  usage
+              policy  by  next-hop destination and by remote SMTP server host-
               name.
 
        <b><a href="postconf.5.html#smtp_tls_cipherlist">smtp_tls_cipherlist</a> (empty)</b>
-              Obsolete  Postfix  &lt; 2.3 control for the Postfix SMTP client TLS
+              Obsolete Postfix &lt; 2.3 control for the Postfix SMTP  client  TLS
               cipher list.
 
 <b>RESOURCE AND RATE CONTROLS</b>
        <b><a href="postconf.5.html#smtp_destination_concurrency_limit">smtp_destination_concurrency_limit</a>        ($<a href="postconf.5.html#default_destination_concurrency_limit">default_destination_concur</a>-</b>
        <b><a href="postconf.5.html#default_destination_concurrency_limit">rency_limit</a>)</b>
-              The maximal number of parallel deliveries to the  same  destina-
+              The  maximal  number of parallel deliveries to the same destina-
               tion via the smtp message delivery transport.
 
        <b><a href="postconf.5.html#smtp_destination_recipient_limit">smtp_destination_recipient_limit</a> ($<a href="postconf.5.html#default_destination_recipient_limit">default_destination_recipient_limit</a>)</b>
-              The  maximal  number of recipients per message for the smtp mes-
+              The maximal number of recipients per message for the  smtp  mes-
               sage delivery transport.
 
        <b><a href="postconf.5.html#smtp_connect_timeout">smtp_connect_timeout</a> (30s)</b>
-              The Postfix SMTP client time limit for completing a TCP  connec-
+              The  Postfix SMTP client time limit for completing a TCP connec-
               tion, or zero (use the operating system built-in time limit).
 
        <b><a href="postconf.5.html#smtp_helo_timeout">smtp_helo_timeout</a> (300s)</b>
-              The  Postfix SMTP client time limit for sending the HELO or EHLO
-              command, and  for  receiving  the  initial  remote  SMTP  server
+              The Postfix SMTP client time limit for sending the HELO or  EHLO
+              command,  and  for  receiving  the  initial  remote  SMTP server
               response.
 
        <b><a href="postconf.5.html#lmtp_lhlo_timeout">lmtp_lhlo_timeout</a> (300s)</b>
@@ -610,19 +616,19 @@ SMTP(8)                                                                SMTP(8)
               mand, and for receiving the remote SMTP server response.
 
        <b><a href="postconf.5.html#smtp_mail_timeout">smtp_mail_timeout</a> (300s)</b>
-              The  Postfix  SMTP  client  time limit for sending the MAIL FROM
+              The Postfix SMTP client time limit for  sending  the  MAIL  FROM
               command, and for receiving the remote SMTP server response.
 
        <b><a href="postconf.5.html#smtp_rcpt_timeout">smtp_rcpt_timeout</a> (300s)</b>
-              The Postfix SMTP client time limit for sending the SMTP RCPT  TO
+              The  Postfix SMTP client time limit for sending the SMTP RCPT TO
               command, and for receiving the remote SMTP server response.
 
        <b><a href="postconf.5.html#smtp_data_init_timeout">smtp_data_init_timeout</a> (120s)</b>
-              The  Postfix  SMTP  client  time limit for sending the SMTP DATA
+              The Postfix SMTP client time limit for  sending  the  SMTP  DATA
               command, and for receiving the remote SMTP server response.
 
        <b><a href="postconf.5.html#smtp_data_xfer_timeout">smtp_data_xfer_timeout</a> (180s)</b>
-              The Postfix SMTP client time limit for sending the SMTP  message
+              The  Postfix SMTP client time limit for sending the SMTP message
               content.
 
        <b><a href="postconf.5.html#smtp_data_done_timeout">smtp_data_done_timeout</a> (600s)</b>
@@ -636,13 +642,13 @@ SMTP(8)                                                                SMTP(8)
        Available in Postfix version 2.1 and later:
 
        <b><a href="postconf.5.html#smtp_mx_address_limit">smtp_mx_address_limit</a> (5)</b>
-              The  maximal number of MX (mail exchanger) IP addresses that can
-              result from Postfix SMTP client mail exchanger lookups, or  zero
+              The maximal number of MX (mail exchanger) IP addresses that  can
+              result  from Postfix SMTP client mail exchanger lookups, or zero
               (no limit).
 
        <b><a href="postconf.5.html#smtp_mx_session_limit">smtp_mx_session_limit</a> (2)</b>
-              The  maximal number of SMTP sessions per delivery request before
-              the Postfix SMTP client gives up  or  delivers  to  a  fall-back
+              The maximal number of SMTP sessions per delivery request  before
+              the  Postfix  SMTP  client  gives  up or delivers to a fall-back
               <a href="postconf.5.html#relayhost">relay host</a>, or zero (no limit).
 
        <b><a href="postconf.5.html#smtp_rset_timeout">smtp_rset_timeout</a> (20s)</b>
@@ -652,17 +658,17 @@ SMTP(8)                                                                SMTP(8)
        Available in Postfix version 2.2 and earlier:
 
        <b><a href="postconf.5.html#lmtp_cache_connection">lmtp_cache_connection</a> (yes)</b>
-              Keep Postfix LMTP client connections open for  up  to  $<a href="postconf.5.html#max_idle">max_idle</a>
+              Keep  Postfix  LMTP  client connections open for up to $<a href="postconf.5.html#max_idle">max_idle</a>
               seconds.
 
        Available in Postfix version 2.2 and later:
 
        <b><a href="postconf.5.html#smtp_connection_cache_destinations">smtp_connection_cache_destinations</a> (empty)</b>
-              Permanently  enable  SMTP  connection  caching for the specified
+              Permanently enable SMTP connection  caching  for  the  specified
               destinations.
 
        <b><a href="postconf.5.html#smtp_connection_cache_on_demand">smtp_connection_cache_on_demand</a> (yes)</b>
-              Temporarily enable SMTP connection caching while  a  destination
+              Temporarily  enable  SMTP connection caching while a destination
               has a high volume of mail in the <a href="QSHAPE_README.html#active_queue">active queue</a>.
 
        <b><a href="postconf.5.html#smtp_connection_reuse_time_limit">smtp_connection_reuse_time_limit</a> (300s)</b>
@@ -676,23 +682,23 @@ SMTP(8)                                                                SMTP(8)
        Available in Postfix version 2.3 and later:
 
        <b><a href="postconf.5.html#connection_cache_protocol_timeout">connection_cache_protocol_timeout</a> (5s)</b>
-              Time  limit for connection cache connect, send or receive opera-
+              Time limit for connection cache connect, send or receive  opera-
               tions.
 
        Available in Postfix version 2.9 and later:
 
        <b><a href="postconf.5.html#smtp_per_record_deadline">smtp_per_record_deadline</a> (no)</b>
-              Change the behavior of the smtp_*_timeout time  limits,  from  a
-              time  limit  per  read  or write system call, to a time limit to
-              send or receive a complete record (an SMTP  command  line,  SMTP
-              response  line,  SMTP message content line, or TLS protocol mes-
+              Change  the  behavior  of the smtp_*_timeout time limits, from a
+              time limit per read or write system call, to  a  time  limit  to
+              send  or  receive  a complete record (an SMTP command line, SMTP
+              response line, SMTP message content line, or TLS  protocol  mes-
               sage).
 
        Available in Postfix version 2.11 and later:
 
        <b><a href="postconf.5.html#smtp_connection_reuse_count_limit">smtp_connection_reuse_count_limit</a> (0)</b>
-              When SMTP connection caching is enabled,  the  number  of  times
-              that  an SMTP session may be reused before it is closed, or zero
+              When  SMTP  connection  caching  is enabled, the number of times
+              that an SMTP session may be reused before it is closed, or  zero
               (no limit).
 
 <b>SMTPUTF8 CONTROLS</b>
@@ -703,21 +709,21 @@ SMTP(8)                                                                SMTP(8)
               in <a href="http://tools.ietf.org/html/rfc6531">RFC 6531</a>..6533.
 
        <b><a href="postconf.5.html#smtputf8_autodetect_classes">smtputf8_autodetect_classes</a> (sendmail, verify)</b>
-              Detect  that  a message requires SMTPUTF8 support for the speci-
+              Detect that a message requires SMTPUTF8 support for  the  speci-
               fied mail origin classes.
 
 <b>TROUBLE SHOOTING CONTROLS</b>
        <b><a href="postconf.5.html#debug_peer_level">debug_peer_level</a> (2)</b>
-              The increment in verbose logging level when a remote  client  or
+              The  increment  in verbose logging level when a remote client or
               server matches a pattern in the <a href="postconf.5.html#debug_peer_list">debug_peer_list</a> parameter.
 
        <b><a href="postconf.5.html#debug_peer_list">debug_peer_list</a> (empty)</b>
-              Optional  list  of  remote  client or server hostname or network
+              Optional list of remote client or  server  hostname  or  network
               address  patterns  that  cause  the  verbose  logging  level  to
               increase by the amount specified in $<a href="postconf.5.html#debug_peer_level">debug_peer_level</a>.
 
        <b><a href="postconf.5.html#error_notice_recipient">error_notice_recipient</a> (postmaster)</b>
-              The  recipient  of  postmaster notifications about mail delivery
+              The recipient of postmaster notifications  about  mail  delivery
               problems that are caused by policy, resource, software or proto-
               col errors.
 
@@ -731,46 +737,46 @@ SMTP(8)                                                                SMTP(8)
 
 <b>MISCELLANEOUS CONTROLS</b>
        <b><a href="postconf.5.html#best_mx_transport">best_mx_transport</a> (empty)</b>
-              Where  the  Postfix  SMTP  client  should  deliver  mail when it
+              Where the Postfix  SMTP  client  should  deliver  mail  when  it
               detects a "mail loops back to myself" error condition.
 
        <b><a href="postconf.5.html#config_directory">config_directory</a> (see 'postconf -d' output)</b>
-              The default location of the Postfix <a href="postconf.5.html">main.cf</a> and  <a href="master.5.html">master.cf</a>  con-
+              The  default  location of the Postfix <a href="postconf.5.html">main.cf</a> and <a href="master.5.html">master.cf</a> con-
               figuration files.
 
        <b><a href="postconf.5.html#daemon_timeout">daemon_timeout</a> (18000s)</b>
-              How  much  time  a  Postfix  daemon process may take to handle a
+              How much time a Postfix daemon process  may  take  to  handle  a
               request before it is terminated by a built-in watchdog timer.
 
        <b><a href="postconf.5.html#delay_logging_resolution_limit">delay_logging_resolution_limit</a> (2)</b>
-              The maximal number of digits after the decimal point  when  log-
+              The  maximal  number of digits after the decimal point when log-
               ging sub-second delay values.
 
        <b><a href="postconf.5.html#disable_dns_lookups">disable_dns_lookups</a> (no)</b>
               Disable DNS lookups in the Postfix SMTP and LMTP clients.
 
        <b><a href="postconf.5.html#inet_interfaces">inet_interfaces</a> (all)</b>
-              The  network  interface addresses that this mail system receives
+              The network interface addresses that this mail  system  receives
               mail on.
 
        <b><a href="postconf.5.html#inet_protocols">inet_protocols</a> (all)</b>
-              The Internet protocols Postfix will attempt to use  when  making
+              The  Internet  protocols Postfix will attempt to use when making
               or accepting connections.
 
        <b><a href="postconf.5.html#ipc_timeout">ipc_timeout</a> (3600s)</b>
-              The  time  limit  for  sending  or receiving information over an
+              The time limit for sending  or  receiving  information  over  an
               internal communication channel.
 
        <b><a href="postconf.5.html#lmtp_assume_final">lmtp_assume_final</a> (no)</b>
-              When a remote LMTP server announces no DSN support, assume  that
-              the  server performs final delivery, and send "delivered" deliv-
+              When  a remote LMTP server announces no DSN support, assume that
+              the server performs final delivery, and send "delivered"  deliv-
               ery status notifications instead of "relayed".
 
        <b><a href="postconf.5.html#lmtp_tcp_port">lmtp_tcp_port</a> (24)</b>
               The default TCP port that the Postfix LMTP client connects to.
 
        <b><a href="postconf.5.html#max_idle">max_idle</a> (100s)</b>
-              The maximum amount of time that an idle Postfix  daemon  process
+              The  maximum  amount of time that an idle Postfix daemon process
               waits for an incoming connection before terminating voluntarily.
 
        <b><a href="postconf.5.html#max_use">max_use</a> (100)</b>
@@ -784,20 +790,20 @@ SMTP(8)                                                                SMTP(8)
               The process name of a Postfix command or daemon process.
 
        <b><a href="postconf.5.html#proxy_interfaces">proxy_interfaces</a> (empty)</b>
-              The  network  interface addresses that this mail system receives
+              The network interface addresses that this mail  system  receives
               mail on by way of a proxy or network address translation unit.
 
        <b><a href="postconf.5.html#smtp_address_preference">smtp_address_preference</a> (any)</b>
               The address type ("ipv6", "ipv4" or "any") that the Postfix SMTP
-              client  will  try  first,  when  a destination has IPv6 and IPv4
+              client will try first, when a  destination  has  IPv6  and  IPv4
               addresses with equal MX preference.
 
        <b><a href="postconf.5.html#smtp_bind_address">smtp_bind_address</a> (empty)</b>
-              An optional numerical network  address  that  the  Postfix  SMTP
+              An  optional  numerical  network  address  that the Postfix SMTP
               client should bind to when making an IPv4 connection.
 
        <b><a href="postconf.5.html#smtp_bind_address6">smtp_bind_address6</a> (empty)</b>
-              An  optional  numerical  network  address  that the Postfix SMTP
+              An optional numerical network  address  that  the  Postfix  SMTP
               client should bind to when making an IPv6 connection.
 
        <b><a href="postconf.5.html#smtp_helo_name">smtp_helo_name</a> ($<a href="postconf.5.html#myhostname">myhostname</a>)</b>
@@ -817,8 +823,8 @@ SMTP(8)                                                                SMTP(8)
               The syslog facility of Postfix logging.
 
        <b><a href="postconf.5.html#syslog_name">syslog_name</a> (see 'postconf -d' output)</b>
-              The  mail  system  name that is prepended to the process name in
-              syslog records, so that "smtpd"  becomes,  for  example,  "post-
+              The mail system name that is prepended to the  process  name  in
+              syslog  records,  so  that  "smtpd" becomes, for example, "post-
               fix/smtpd".
 
        Available with Postfix 2.2 and earlier:
@@ -836,7 +842,7 @@ SMTP(8)                                                                SMTP(8)
        Available with Postfix 2.12 and later:
 
        <b><a href="postconf.5.html#smtp_address_verify_target">smtp_address_verify_target</a> (rcpt)</b>
-              In  the context of email address verification, the SMTP protocol
+              In the context of email address verification, the SMTP  protocol
               stage that determines whether an email address is deliverable.
 
 <b>SEE ALSO</b>
index 124781b057ce3a7fcfeaaac48e3300a500afd057..3e67e0b76ffda5287e2b4f66d2ba82c9d53c506a 100644 (file)
@@ -10,7 +10,7 @@ POSTALIAS(1)                                                      POSTALIAS(1)
        postalias - Postfix alias database maintenance
 
 <b>SYNOPSIS</b>
-       <b>postalias</b> [<b>-Nfinoprsvw</b>] [<b>-c</b> <i>config</i><b>_</b><i>dir</i>] [<b>-d</b> <i>key</i>] [<b>-q</b> <i>key</i>]
+       <b>postalias</b> [<b>-Nfinoprsuvw</b>] [<b>-c</b> <i>config</i><b>_</b><i>dir</i>] [<b>-d</b> <i>key</i>] [<b>-q</b> <i>key</i>]
                [<i>file</i><b>_</b><i>type</i>:]<i>file</i><b>_</b><i>name</i> ...
 
 <b>DESCRIPTION</b>
@@ -95,6 +95,10 @@ POSTALIAS(1)                                                      POSTALIAS(1)
               order.  This feature is available in  Postfix  version  2.2  and
               later, and is not available for all database types.
 
+       <b>-u</b>     Disable  UTF-8 support. UTF-8 support is enabled by default when
+              "<a href="postconf.5.html#smtputf8_enable">smtputf8_enable</a> = yes". It requires that keys  and  values  are
+              valid UTF-8 strings.
+
        <b>-v</b>     Enable  verbose  logging  for  debugging  purposes.  Multiple <b>-v</b>
               options make the software increasingly verbose.
 
@@ -183,12 +187,16 @@ POSTALIAS(1)                                                      POSTALIAS(1)
               The default database type for use in <a href="newaliases.1.html"><b>newaliases</b>(1)</a>, <a href="postalias.1.html"><b>postalias</b>(1)</a>
               and <a href="postmap.1.html"><b>postmap</b>(1)</a> commands.
 
+       <b><a href="postconf.5.html#smtputf8_enable">smtputf8_enable</a> (yes)</b>
+              Enable experimental SMTPUTF8 support for the protocols described
+              in <a href="http://tools.ietf.org/html/rfc6531">RFC 6531</a>..6533.
+
        <b><a href="postconf.5.html#syslog_facility">syslog_facility</a> (mail)</b>
               The syslog facility of Postfix logging.
 
        <b><a href="postconf.5.html#syslog_name">syslog_name</a> (see 'postconf -d' output)</b>
-              The  mail  system  name that is prepended to the process name in
-              syslog records, so that "smtpd"  becomes,  for  example,  "post-
+              The mail system name that is prepended to the  process  name  in
+              syslog  records,  so  that  "smtpd" becomes, for example, "post-
               fix/smtpd".
 
 <b>STANDARDS</b>
index cace24f436ac8b6270398839134a679725766ebf..4aff5f47077d94e58917c695d4403b98ebb02b2c 100644 (file)
@@ -9891,8 +9891,8 @@ SMTP servers that reject recipients after the DATA command. Use
 <blockquote>
 <pre>
 /etc/postfix/transport:
-    smtp-domain_that_verifies_after_data    smtp-data-target:
-    lmtp-domain_that_verifies_after_data    lmtp-data-target:
+    smtp-domain-that-verifies-after-data    smtp-data-target:
+    lmtp-domain-that-verifies-after-data    lmtp-data-target:
 </pre>
 </blockquote>
 
@@ -12916,6 +12916,35 @@ example.com     verify  match=hostname:nexthop
 <p> This feature is available in Postfix 2.3 and later. </p>
 
 
+</DD>
+
+<DT><b><a name="smtp_tls_wrappermode">smtp_tls_wrappermode</a>
+(default: no)</b></DT><DD>
+
+<p> Request that the Postfix SMTP client connects using the
+legacy SMTPS protocol instead of using the STARTTLS command. </p>
+
+<p> This mode requires "<a href="postconf.5.html#smtp_tls_security_level">smtp_tls_security_level</a> = encrypt" or
+stronger. </p>
+
+<p> Example: deliver all remote mail via a provider's server
+"mail.example.com".  </p>
+
+<pre>
+/etc/postfix/<a href="postconf.5.html">main.cf</a>:
+    # Client-side SMTPS requires "encrypt" or stronger.
+    <a href="postconf.5.html#smtp_tls_security_level">smtp_tls_security_level</a> = encrypt
+    <a href="postconf.5.html#smtp_tls_wrappermode">smtp_tls_wrappermode</a> = yes
+    # The [] suppress MX lookups.
+    <a href="postconf.5.html#relayhost">relayhost</a> = [mail.example.com]:465
+</pre>
+
+<p> More examples are in <a href="TLS_README.html">TLS_README</a>, including examples for older
+Postfix versions. </p>
+
+<p> This feature is available in Postfix 2.12 and later.  </p>
+
+
 </DD>
 
 <DT><b><a name="smtp_use_tls">smtp_use_tls</a>
@@ -18869,6 +18898,22 @@ This feature is available in Postfix 1.1 and later.
 </p>
 
 
+</DD>
+
+<DT><b><a name="virtual_alias_address_length_limit">virtual_alias_address_length_limit</a>
+(default: 1000)</b></DT><DD>
+
+<p>
+The maximal length of an email address after virtual alias expansion.
+This stops virtual aliasing loops that increase the address length
+exponentially.
+</p>
+
+<p>
+This feature is available in Postfix 2.12 and later.
+</p>
+
+
 </DD>
 
 <DT><b><a name="virtual_alias_domains">virtual_alias_domains</a>
index 55d1e9b2babbacb3baeb5c77af00ee2649694e43..9822a2dd15f66c003f189fc62f1fe51f66694a57 100644 (file)
@@ -10,7 +10,7 @@ POSTMAP(1)                                                          POSTMAP(1)
        postmap - Postfix lookup table management
 
 <b>SYNOPSIS</b>
-       <b>postmap</b> [<b>-Nbfhimnoprsvw</b>] [<b>-c</b> <i>config</i><b>_</b><i>dir</i>] [<b>-d</b> <i>key</i>] [<b>-q</b> <i>key</i>]
+       <b>postmap</b> [<b>-NbfhimnoprsuUvw</b>] [<b>-c</b> <i>config</i><b>_</b><i>dir</i>] [<b>-d</b> <i>key</i>] [<b>-q</b> <i>key</i>]
                [<i>file</i><b>_</b><i>type</i>:]<i>file</i><b>_</b><i>name</i> ...
 
 <b>DESCRIPTION</b>
@@ -66,6 +66,10 @@ POSTMAP(1)                                                          POSTMAP(1)
               style lookup keys for attachment MIME headers and  for  attached
               message/* headers.
 
+              NOTE:  with  "<a href="postconf.5.html#smtputf8_enable">smtputf8_enable</a>  = yes", the <b>-b</b> option option dis-
+              ables UTF-8 syntax checks on  query  keys  and  lookup  results.
+              Specify the <b>-U</b> option to force UTF-8 syntax checks anyway.
+
               This feature is available in Postfix version 2.6 and later.
 
        <b>-c</b> <i>config</i><b>_</b><i>dir</i>
@@ -99,6 +103,10 @@ POSTMAP(1)                                                          POSTMAP(1)
               also generates header-style  lookup  keys  for  attachment  MIME
               headers and for attached message/* headers.
 
+              NOTE:  with  "<a href="postconf.5.html#smtputf8_enable">smtputf8_enable</a>  = yes", the <b>-b</b> option option dis-
+              ables UTF-8 syntax checks on  query  keys  and  lookup  results.
+              Specify the <b>-U</b> option to force UTF-8 syntax checks anyway.
+
               This feature is available in Postfix version 2.6 and later.
 
        <b>-i</b>     Incremental  mode.  Read  entries from standard input and do not
@@ -145,10 +153,17 @@ POSTMAP(1)                                                          POSTMAP(1)
               This feature is available in Postfix version 2.2 and later,  and
               is not available for all database types.
 
-       <b>-v</b>     Enable  verbose  logging  for  debugging  purposes.  Multiple <b>-v</b>
+       <b>-u</b>     Disable  UTF-8 support. UTF-8 support is enabled by default when
+              "<a href="postconf.5.html#smtputf8_enable">smtputf8_enable</a> = yes". It requires that keys  and  values  are
+              valid UTF-8 strings.
+
+       <b>-U</b>     With "<a href="postconf.5.html#smtputf8_enable">smtputf8_enable</a> = yes", force UTF-8 syntax checks with the
+              <b>-b</b> and <b>-h</b> options.
+
+       <b>-v</b>     Enable verbose  logging  for  debugging  purposes.  Multiple  <b>-v</b>
               options make the software increasingly verbose.
 
-       <b>-w</b>     When updating a table, do not complain about attempts to  update
+       <b>-w</b>     When  updating a table, do not complain about attempts to update
               existing entries, and ignore those attempts.
 
        Arguments:
@@ -160,32 +175,32 @@ POSTMAP(1)                                                          POSTMAP(1)
               The <a href="postmap.1.html"><b>postmap</b>(1)</a> command can query any supported file type, but it
               can create only the following file types:
 
-              <b>btree</b>  The  output  file  is  a  btree file, named <i>file</i><b>_</b><i>name</i><b>.db</b>.
-                     This is available on systems with support  for  <b>db</b>  data-
+              <b>btree</b>  The output file is  a  btree  file,  named  <i>file</i><b>_</b><i>name</i><b>.db</b>.
+                     This  is  available  on systems with support for <b>db</b> data-
                      bases.
 
-              <b>cdb</b>    The  output  consists  of  one file, named <i>file</i><b>_</b><i>name</i><b>.cdb</b>.
-                     This is available on systems with support for  <b>cdb</b>  data-
+              <b>cdb</b>    The output consists of  one  file,  named  <i>file</i><b>_</b><i>name</i><b>.cdb</b>.
+                     This  is  available on systems with support for <b>cdb</b> data-
                      bases.
 
               <b>dbm</b>    The output consists of two files, named <i>file</i><b>_</b><i>name</i><b>.pag</b> and
                      <i>file</i><b>_</b><i>name</i><b>.dir</b>.  This is available on systems with support
                      for <b>dbm</b> databases.
 
-              <b>hash</b>   The  output  file  is  a hashed file, named <i>file</i><b>_</b><i>name</i><b>.db</b>.
-                     This is available on systems with support  for  <b>db</b>  data-
+              <b>hash</b>   The output file is a  hashed  file,  named  <i>file</i><b>_</b><i>name</i><b>.db</b>.
+                     This  is  available  on systems with support for <b>db</b> data-
                      bases.
 
-              <b>fail</b>   A  table that reliably fails all requests. The lookup ta-
-                     ble name is used for logging only. This table  exists  to
+              <b>fail</b>   A table that reliably fails all requests. The lookup  ta-
+                     ble  name  is used for logging only. This table exists to
                      simplify Postfix error tests.
 
               <b>sdbm</b>   The output consists of two files, named <i>file</i><b>_</b><i>name</i><b>.pag</b> and
                      <i>file</i><b>_</b><i>name</i><b>.dir</b>.  This is available on systems with support
                      for <b>sdbm</b> databases.
 
-              When  no  <i>file</i><b>_</b><i>type</i> is specified, the software uses the database
-              type  specified  via  the  <b><a href="postconf.5.html#default_database_type">default_database_type</a></b>   configuration
+              When no <i>file</i><b>_</b><i>type</i> is specified, the software uses  the  database
+              type   specified  via  the  <b><a href="postconf.5.html#default_database_type">default_database_type</a></b>  configuration
               parameter.
 
        <i>file</i><b>_</b><i>name</i>
@@ -194,11 +209,11 @@ POSTMAP(1)                                                          POSTMAP(1)
 
 <b>DIAGNOSTICS</b>
        Problems are logged to the standard error stream and to <b>syslogd</b>(8).  No
-       output  means  that  no  problems  were detected. Duplicate entries are
+       output means that no problems  were  detected.  Duplicate  entries  are
        skipped and are flagged with a warning.
 
        <a href="postmap.1.html"><b>postmap</b>(1)</a> terminates with zero exit status in case of success (includ-
-       ing  successful  "<b>postmap -q</b>" lookup) and terminates with non-zero exit
+       ing successful "<b>postmap -q</b>" lookup) and terminates with  non-zero  exit
        status in case of failure.
 
 <b>ENVIRONMENT</b>
@@ -209,12 +224,12 @@ POSTMAP(1)                                                          POSTMAP(1)
               Enable verbose logging for debugging purposes.
 
 <b>CONFIGURATION PARAMETERS</b>
-       The following <a href="postconf.5.html"><b>main.cf</b></a> parameters are especially relevant to  this  pro-
-       gram.   The  text  below  provides  only a parameter summary. See <a href="postconf.5.html"><b>post-</b></a>
+       The  following  <a href="postconf.5.html"><b>main.cf</b></a> parameters are especially relevant to this pro-
+       gram.  The text below provides only  a  parameter  summary.  See  <a href="postconf.5.html"><b>post-</b></a>
        <a href="postconf.5.html"><b>conf</b>(5)</a> for more details including examples.
 
        <b><a href="postconf.5.html#berkeley_db_create_buffer_size">berkeley_db_create_buffer_size</a> (16777216)</b>
-              The per-table I/O buffer size for programs that create  Berkeley
+              The  per-table I/O buffer size for programs that create Berkeley
               DB hash or btree tables.
 
        <b><a href="postconf.5.html#berkeley_db_read_buffer_size">berkeley_db_read_buffer_size</a> (131072)</b>
@@ -222,13 +237,17 @@ POSTMAP(1)                                                          POSTMAP(1)
               hash or btree tables.
 
        <b><a href="postconf.5.html#config_directory">config_directory</a> (see 'postconf -d' output)</b>
-              The default location of the Postfix <a href="postconf.5.html">main.cf</a> and  <a href="master.5.html">master.cf</a>  con-
+              The  default  location of the Postfix <a href="postconf.5.html">main.cf</a> and <a href="master.5.html">master.cf</a> con-
               figuration files.
 
        <b><a href="postconf.5.html#default_database_type">default_database_type</a> (see 'postconf -d' output)</b>
               The default database type for use in <a href="newaliases.1.html"><b>newaliases</b>(1)</a>, <a href="postalias.1.html"><b>postalias</b>(1)</a>
               and <a href="postmap.1.html"><b>postmap</b>(1)</a> commands.
 
+       <b><a href="postconf.5.html#smtputf8_enable">smtputf8_enable</a> (yes)</b>
+              Enable experimental SMTPUTF8 support for the protocols described
+              in <a href="http://tools.ietf.org/html/rfc6531">RFC 6531</a>..6533.
+
        <b><a href="postconf.5.html#syslog_facility">syslog_facility</a> (mail)</b>
               The syslog facility of Postfix logging.
 
index 54d6807f149cb8df575b556078748f8bf1797912..705c5ab227545e7975a35f36a0bacf231cb09a9e 100644 (file)
@@ -555,50 +555,56 @@ SMTP(8)                                                                SMTP(8)
        <b><a href="postconf.5.html#tlsmgr_service_name">tlsmgr_service_name</a> (tlsmgr)</b>
               The name of the <a href="tlsmgr.8.html"><b>tlsmgr</b>(8)</a> service entry in <a href="master.5.html">master.cf</a>.
 
+       Available in Postfix version 2.12 and later:
+
+       <b><a href="postconf.5.html#smtp_tls_wrappermode">smtp_tls_wrappermode</a> (no)</b>
+              Request  that  the Postfix SMTP client connects using the legacy
+              SMTPS protocol instead of using the STARTTLS command.
+
 <b>OBSOLETE STARTTLS CONTROLS</b>
-       The  following  configuration  parameters  exist for compatibility with
-       Postfix versions before 2.3. Support for these will  be  removed  in  a
+       The following configuration parameters  exist  for  compatibility  with
+       Postfix  versions  before  2.3.  Support for these will be removed in a
        future release.
 
        <b><a href="postconf.5.html#smtp_use_tls">smtp_use_tls</a> (no)</b>
-              Opportunistic  mode: use TLS when a remote SMTP server announces
+              Opportunistic mode: use TLS when a remote SMTP server  announces
               STARTTLS support, otherwise send the mail in the clear.
 
        <b><a href="postconf.5.html#smtp_enforce_tls">smtp_enforce_tls</a> (no)</b>
-              Enforcement mode: require  that  remote  SMTP  servers  use  TLS
+              Enforcement  mode:  require  that  remote  SMTP  servers use TLS
               encryption, and never send mail in the clear.
 
        <b><a href="postconf.5.html#smtp_tls_enforce_peername">smtp_tls_enforce_peername</a> (yes)</b>
-              With  mandatory  TLS  encryption,  require  that the remote SMTP
-              server hostname matches  the  information  in  the  remote  SMTP
+              With mandatory TLS encryption,  require  that  the  remote  SMTP
+              server  hostname  matches  the  information  in  the remote SMTP
               server certificate.
 
        <b><a href="postconf.5.html#smtp_tls_per_site">smtp_tls_per_site</a> (empty)</b>
-              Optional  lookup  tables  with the Postfix SMTP client TLS usage
-              policy by next-hop destination and by remote SMTP  server  host-
+              Optional lookup tables with the Postfix SMTP  client  TLS  usage
+              policy  by  next-hop destination and by remote SMTP server host-
               name.
 
        <b><a href="postconf.5.html#smtp_tls_cipherlist">smtp_tls_cipherlist</a> (empty)</b>
-              Obsolete  Postfix  &lt; 2.3 control for the Postfix SMTP client TLS
+              Obsolete Postfix &lt; 2.3 control for the Postfix SMTP  client  TLS
               cipher list.
 
 <b>RESOURCE AND RATE CONTROLS</b>
        <b><a href="postconf.5.html#smtp_destination_concurrency_limit">smtp_destination_concurrency_limit</a>        ($<a href="postconf.5.html#default_destination_concurrency_limit">default_destination_concur</a>-</b>
        <b><a href="postconf.5.html#default_destination_concurrency_limit">rency_limit</a>)</b>
-              The maximal number of parallel deliveries to the  same  destina-
+              The  maximal  number of parallel deliveries to the same destina-
               tion via the smtp message delivery transport.
 
        <b><a href="postconf.5.html#smtp_destination_recipient_limit">smtp_destination_recipient_limit</a> ($<a href="postconf.5.html#default_destination_recipient_limit">default_destination_recipient_limit</a>)</b>
-              The  maximal  number of recipients per message for the smtp mes-
+              The maximal number of recipients per message for the  smtp  mes-
               sage delivery transport.
 
        <b><a href="postconf.5.html#smtp_connect_timeout">smtp_connect_timeout</a> (30s)</b>
-              The Postfix SMTP client time limit for completing a TCP  connec-
+              The  Postfix SMTP client time limit for completing a TCP connec-
               tion, or zero (use the operating system built-in time limit).
 
        <b><a href="postconf.5.html#smtp_helo_timeout">smtp_helo_timeout</a> (300s)</b>
-              The  Postfix SMTP client time limit for sending the HELO or EHLO
-              command, and  for  receiving  the  initial  remote  SMTP  server
+              The Postfix SMTP client time limit for sending the HELO or  EHLO
+              command,  and  for  receiving  the  initial  remote  SMTP server
               response.
 
        <b><a href="postconf.5.html#lmtp_lhlo_timeout">lmtp_lhlo_timeout</a> (300s)</b>
@@ -610,19 +616,19 @@ SMTP(8)                                                                SMTP(8)
               mand, and for receiving the remote SMTP server response.
 
        <b><a href="postconf.5.html#smtp_mail_timeout">smtp_mail_timeout</a> (300s)</b>
-              The  Postfix  SMTP  client  time limit for sending the MAIL FROM
+              The Postfix SMTP client time limit for  sending  the  MAIL  FROM
               command, and for receiving the remote SMTP server response.
 
        <b><a href="postconf.5.html#smtp_rcpt_timeout">smtp_rcpt_timeout</a> (300s)</b>
-              The Postfix SMTP client time limit for sending the SMTP RCPT  TO
+              The  Postfix SMTP client time limit for sending the SMTP RCPT TO
               command, and for receiving the remote SMTP server response.
 
        <b><a href="postconf.5.html#smtp_data_init_timeout">smtp_data_init_timeout</a> (120s)</b>
-              The  Postfix  SMTP  client  time limit for sending the SMTP DATA
+              The Postfix SMTP client time limit for  sending  the  SMTP  DATA
               command, and for receiving the remote SMTP server response.
 
        <b><a href="postconf.5.html#smtp_data_xfer_timeout">smtp_data_xfer_timeout</a> (180s)</b>
-              The Postfix SMTP client time limit for sending the SMTP  message
+              The  Postfix SMTP client time limit for sending the SMTP message
               content.
 
        <b><a href="postconf.5.html#smtp_data_done_timeout">smtp_data_done_timeout</a> (600s)</b>
@@ -636,13 +642,13 @@ SMTP(8)                                                                SMTP(8)
        Available in Postfix version 2.1 and later:
 
        <b><a href="postconf.5.html#smtp_mx_address_limit">smtp_mx_address_limit</a> (5)</b>
-              The  maximal number of MX (mail exchanger) IP addresses that can
-              result from Postfix SMTP client mail exchanger lookups, or  zero
+              The maximal number of MX (mail exchanger) IP addresses that  can
+              result  from Postfix SMTP client mail exchanger lookups, or zero
               (no limit).
 
        <b><a href="postconf.5.html#smtp_mx_session_limit">smtp_mx_session_limit</a> (2)</b>
-              The  maximal number of SMTP sessions per delivery request before
-              the Postfix SMTP client gives up  or  delivers  to  a  fall-back
+              The maximal number of SMTP sessions per delivery request  before
+              the  Postfix  SMTP  client  gives  up or delivers to a fall-back
               <a href="postconf.5.html#relayhost">relay host</a>, or zero (no limit).
 
        <b><a href="postconf.5.html#smtp_rset_timeout">smtp_rset_timeout</a> (20s)</b>
@@ -652,17 +658,17 @@ SMTP(8)                                                                SMTP(8)
        Available in Postfix version 2.2 and earlier:
 
        <b><a href="postconf.5.html#lmtp_cache_connection">lmtp_cache_connection</a> (yes)</b>
-              Keep Postfix LMTP client connections open for  up  to  $<a href="postconf.5.html#max_idle">max_idle</a>
+              Keep  Postfix  LMTP  client connections open for up to $<a href="postconf.5.html#max_idle">max_idle</a>
               seconds.
 
        Available in Postfix version 2.2 and later:
 
        <b><a href="postconf.5.html#smtp_connection_cache_destinations">smtp_connection_cache_destinations</a> (empty)</b>
-              Permanently  enable  SMTP  connection  caching for the specified
+              Permanently enable SMTP connection  caching  for  the  specified
               destinations.
 
        <b><a href="postconf.5.html#smtp_connection_cache_on_demand">smtp_connection_cache_on_demand</a> (yes)</b>
-              Temporarily enable SMTP connection caching while  a  destination
+              Temporarily  enable  SMTP connection caching while a destination
               has a high volume of mail in the <a href="QSHAPE_README.html#active_queue">active queue</a>.
 
        <b><a href="postconf.5.html#smtp_connection_reuse_time_limit">smtp_connection_reuse_time_limit</a> (300s)</b>
@@ -676,23 +682,23 @@ SMTP(8)                                                                SMTP(8)
        Available in Postfix version 2.3 and later:
 
        <b><a href="postconf.5.html#connection_cache_protocol_timeout">connection_cache_protocol_timeout</a> (5s)</b>
-              Time  limit for connection cache connect, send or receive opera-
+              Time limit for connection cache connect, send or receive  opera-
               tions.
 
        Available in Postfix version 2.9 and later:
 
        <b><a href="postconf.5.html#smtp_per_record_deadline">smtp_per_record_deadline</a> (no)</b>
-              Change the behavior of the smtp_*_timeout time  limits,  from  a
-              time  limit  per  read  or write system call, to a time limit to
-              send or receive a complete record (an SMTP  command  line,  SMTP
-              response  line,  SMTP message content line, or TLS protocol mes-
+              Change  the  behavior  of the smtp_*_timeout time limits, from a
+              time limit per read or write system call, to  a  time  limit  to
+              send  or  receive  a complete record (an SMTP command line, SMTP
+              response line, SMTP message content line, or TLS  protocol  mes-
               sage).
 
        Available in Postfix version 2.11 and later:
 
        <b><a href="postconf.5.html#smtp_connection_reuse_count_limit">smtp_connection_reuse_count_limit</a> (0)</b>
-              When SMTP connection caching is enabled,  the  number  of  times
-              that  an SMTP session may be reused before it is closed, or zero
+              When  SMTP  connection  caching  is enabled, the number of times
+              that an SMTP session may be reused before it is closed, or  zero
               (no limit).
 
 <b>SMTPUTF8 CONTROLS</b>
@@ -703,21 +709,21 @@ SMTP(8)                                                                SMTP(8)
               in <a href="http://tools.ietf.org/html/rfc6531">RFC 6531</a>..6533.
 
        <b><a href="postconf.5.html#smtputf8_autodetect_classes">smtputf8_autodetect_classes</a> (sendmail, verify)</b>
-              Detect  that  a message requires SMTPUTF8 support for the speci-
+              Detect that a message requires SMTPUTF8 support for  the  speci-
               fied mail origin classes.
 
 <b>TROUBLE SHOOTING CONTROLS</b>
        <b><a href="postconf.5.html#debug_peer_level">debug_peer_level</a> (2)</b>
-              The increment in verbose logging level when a remote  client  or
+              The  increment  in verbose logging level when a remote client or
               server matches a pattern in the <a href="postconf.5.html#debug_peer_list">debug_peer_list</a> parameter.
 
        <b><a href="postconf.5.html#debug_peer_list">debug_peer_list</a> (empty)</b>
-              Optional  list  of  remote  client or server hostname or network
+              Optional list of remote client or  server  hostname  or  network
               address  patterns  that  cause  the  verbose  logging  level  to
               increase by the amount specified in $<a href="postconf.5.html#debug_peer_level">debug_peer_level</a>.
 
        <b><a href="postconf.5.html#error_notice_recipient">error_notice_recipient</a> (postmaster)</b>
-              The  recipient  of  postmaster notifications about mail delivery
+              The recipient of postmaster notifications  about  mail  delivery
               problems that are caused by policy, resource, software or proto-
               col errors.
 
@@ -731,46 +737,46 @@ SMTP(8)                                                                SMTP(8)
 
 <b>MISCELLANEOUS CONTROLS</b>
        <b><a href="postconf.5.html#best_mx_transport">best_mx_transport</a> (empty)</b>
-              Where  the  Postfix  SMTP  client  should  deliver  mail when it
+              Where the Postfix  SMTP  client  should  deliver  mail  when  it
               detects a "mail loops back to myself" error condition.
 
        <b><a href="postconf.5.html#config_directory">config_directory</a> (see 'postconf -d' output)</b>
-              The default location of the Postfix <a href="postconf.5.html">main.cf</a> and  <a href="master.5.html">master.cf</a>  con-
+              The  default  location of the Postfix <a href="postconf.5.html">main.cf</a> and <a href="master.5.html">master.cf</a> con-
               figuration files.
 
        <b><a href="postconf.5.html#daemon_timeout">daemon_timeout</a> (18000s)</b>
-              How  much  time  a  Postfix  daemon process may take to handle a
+              How much time a Postfix daemon process  may  take  to  handle  a
               request before it is terminated by a built-in watchdog timer.
 
        <b><a href="postconf.5.html#delay_logging_resolution_limit">delay_logging_resolution_limit</a> (2)</b>
-              The maximal number of digits after the decimal point  when  log-
+              The  maximal  number of digits after the decimal point when log-
               ging sub-second delay values.
 
        <b><a href="postconf.5.html#disable_dns_lookups">disable_dns_lookups</a> (no)</b>
               Disable DNS lookups in the Postfix SMTP and LMTP clients.
 
        <b><a href="postconf.5.html#inet_interfaces">inet_interfaces</a> (all)</b>
-              The  network  interface addresses that this mail system receives
+              The network interface addresses that this mail  system  receives
               mail on.
 
        <b><a href="postconf.5.html#inet_protocols">inet_protocols</a> (all)</b>
-              The Internet protocols Postfix will attempt to use  when  making
+              The  Internet  protocols Postfix will attempt to use when making
               or accepting connections.
 
        <b><a href="postconf.5.html#ipc_timeout">ipc_timeout</a> (3600s)</b>
-              The  time  limit  for  sending  or receiving information over an
+              The time limit for sending  or  receiving  information  over  an
               internal communication channel.
 
        <b><a href="postconf.5.html#lmtp_assume_final">lmtp_assume_final</a> (no)</b>
-              When a remote LMTP server announces no DSN support, assume  that
-              the  server performs final delivery, and send "delivered" deliv-
+              When  a remote LMTP server announces no DSN support, assume that
+              the server performs final delivery, and send "delivered"  deliv-
               ery status notifications instead of "relayed".
 
        <b><a href="postconf.5.html#lmtp_tcp_port">lmtp_tcp_port</a> (24)</b>
               The default TCP port that the Postfix LMTP client connects to.
 
        <b><a href="postconf.5.html#max_idle">max_idle</a> (100s)</b>
-              The maximum amount of time that an idle Postfix  daemon  process
+              The  maximum  amount of time that an idle Postfix daemon process
               waits for an incoming connection before terminating voluntarily.
 
        <b><a href="postconf.5.html#max_use">max_use</a> (100)</b>
@@ -784,20 +790,20 @@ SMTP(8)                                                                SMTP(8)
               The process name of a Postfix command or daemon process.
 
        <b><a href="postconf.5.html#proxy_interfaces">proxy_interfaces</a> (empty)</b>
-              The  network  interface addresses that this mail system receives
+              The network interface addresses that this mail  system  receives
               mail on by way of a proxy or network address translation unit.
 
        <b><a href="postconf.5.html#smtp_address_preference">smtp_address_preference</a> (any)</b>
               The address type ("ipv6", "ipv4" or "any") that the Postfix SMTP
-              client  will  try  first,  when  a destination has IPv6 and IPv4
+              client will try first, when a  destination  has  IPv6  and  IPv4
               addresses with equal MX preference.
 
        <b><a href="postconf.5.html#smtp_bind_address">smtp_bind_address</a> (empty)</b>
-              An optional numerical network  address  that  the  Postfix  SMTP
+              An  optional  numerical  network  address  that the Postfix SMTP
               client should bind to when making an IPv4 connection.
 
        <b><a href="postconf.5.html#smtp_bind_address6">smtp_bind_address6</a> (empty)</b>
-              An  optional  numerical  network  address  that the Postfix SMTP
+              An optional numerical network  address  that  the  Postfix  SMTP
               client should bind to when making an IPv6 connection.
 
        <b><a href="postconf.5.html#smtp_helo_name">smtp_helo_name</a> ($<a href="postconf.5.html#myhostname">myhostname</a>)</b>
@@ -817,8 +823,8 @@ SMTP(8)                                                                SMTP(8)
               The syslog facility of Postfix logging.
 
        <b><a href="postconf.5.html#syslog_name">syslog_name</a> (see 'postconf -d' output)</b>
-              The  mail  system  name that is prepended to the process name in
-              syslog records, so that "smtpd"  becomes,  for  example,  "post-
+              The mail system name that is prepended to the  process  name  in
+              syslog  records,  so  that  "smtpd" becomes, for example, "post-
               fix/smtpd".
 
        Available with Postfix 2.2 and earlier:
@@ -836,7 +842,7 @@ SMTP(8)                                                                SMTP(8)
        Available with Postfix 2.12 and later:
 
        <b><a href="postconf.5.html#smtp_address_verify_target">smtp_address_verify_target</a> (rcpt)</b>
-              In  the context of email address verification, the SMTP protocol
+              In the context of email address verification, the SMTP  protocol
               stage that determines whether an email address is deliverable.
 
 <b>SEE ALSO</b>
index 9dda165f6474f431692d220bc873d210dc4e5937..4a8b0f47eae27a7e94d018ccae8d330d467bba01 100644 (file)
@@ -9,7 +9,7 @@ Postfix alias database maintenance
 .na
 .nf
 .fi
-\fBpostalias\fR [\fB-Nfinoprsvw\fR] [\fB-c \fIconfig_dir\fR]
+\fBpostalias\fR [\fB-Nfinoprsuvw\fR] [\fB-c \fIconfig_dir\fR]
 [\fB-d \fIkey\fR] [\fB-q \fIkey\fR]
         [\fIfile_type\fR:]\fIfile_name\fR ...
 .SH DESCRIPTION
@@ -99,6 +99,10 @@ printed in database order, which is not necessarily the same
 as the original input order.
 This feature is available in Postfix version 2.2 and later,
 and is not available for all database types.
+.IP \fB-u\fR
+Disable UTF-8 support. UTF-8 support is enabled by default
+when "smtputf8_enable = yes". It requires that keys and
+values are valid UTF-8 strings.
 .IP \fB-v\fR
 Enable verbose logging for debugging purposes. Multiple \fB-v\fR
 options make the software increasingly verbose.
@@ -188,6 +192,9 @@ hash or btree tables.
 .IP "\fBdefault_database_type (see 'postconf -d' output)\fR"
 The default database type for use in \fBnewaliases\fR(1), \fBpostalias\fR(1)
 and \fBpostmap\fR(1) commands.
+.IP "\fBsmtputf8_enable (yes)\fR"
+Enable experimental SMTPUTF8 support for the protocols described
+in RFC 6531..6533.
 .IP "\fBsyslog_facility (mail)\fR"
 The syslog facility of Postfix logging.
 .IP "\fBsyslog_name (see 'postconf -d' output)\fR"
index cf6ae52402af935dd441f5585ca1a6710a65fdeb..6acadd528ba0d6df7f6c18f7fcc476f6fb3f1498 100644 (file)
@@ -9,7 +9,7 @@ Postfix lookup table management
 .na
 .nf
 .fi
-\fBpostmap\fR [\fB-Nbfhimnoprsvw\fR] [\fB-c \fIconfig_dir\fR]
+\fBpostmap\fR [\fB-NbfhimnoprsuUvw\fR] [\fB-c \fIconfig_dir\fR]
 [\fB-d \fIkey\fR] [\fB-q \fIkey\fR]
         [\fIfile_type\fR:]\fIfile_name\fR ...
 .SH DESCRIPTION
@@ -81,6 +81,11 @@ parsing with \fB-m\fR. With this, the \fB-b\fR option
 generates no body-style lookup keys for attachment MIME
 headers and for attached message/* headers.
 .sp
+NOTE: with "smtputf8_enable = yes", the \fB-b\fR option
+option disables UTF-8 syntax checks on query keys and
+lookup results. Specify the \fB-U\fR option to force UTF-8
+syntax checks anyway.
+.sp
 This feature is available in Postfix version 2.6 and later.
 .IP "\fB-c \fIconfig_dir\fR"
 Read the \fBmain.cf\fR configuration file in the named directory
@@ -114,6 +119,11 @@ parsing with \fB-m\fR. With this, the \fB-h\fR option also
 generates header-style lookup keys for attachment MIME
 headers and for attached message/* headers.
 .sp
+NOTE: with "smtputf8_enable = yes", the \fB-b\fR option
+option disables UTF-8 syntax checks on query keys and
+lookup results. Specify the \fB-U\fR option to force UTF-8
+syntax checks anyway.
+.sp
 This feature is available in Postfix version 2.6 and later.
 .IP \fB-i\fR
 Incremental mode. Read entries from standard input and do not
@@ -161,6 +171,13 @@ as the original input order.
 .sp
 This feature is available in Postfix version 2.2 and later,
 and is not available for all database types.
+.IP \fB-u\fR
+Disable UTF-8 support. UTF-8 support is enabled by default
+when "smtputf8_enable = yes". It requires that keys and
+values are valid UTF-8 strings.
+.IP \fB-U\fR
+With "smtputf8_enable = yes", force UTF-8 syntax checks
+with the \fB-b\fR and \fB-h\fR options.
 .IP \fB-v\fR
 Enable verbose logging for debugging purposes. Multiple \fB-v\fR
 options make the software increasingly verbose.
@@ -245,6 +262,9 @@ configuration files.
 .IP "\fBdefault_database_type (see 'postconf -d' output)\fR"
 The default database type for use in \fBnewaliases\fR(1), \fBpostalias\fR(1)
 and \fBpostmap\fR(1) commands.
+.IP "\fBsmtputf8_enable (yes)\fR"
+Enable experimental SMTPUTF8 support for the protocols described
+in RFC 6531..6533.
 .IP "\fBsyslog_facility (mail)\fR"
 The syslog facility of Postfix logging.
 .IP "\fBsyslog_name (see 'postconf -d' output)\fR"
index f3cd3947d0fbe4014142378a83b258899267bc70..bf2a43ef941a7e3889e39e8d9734e769e449b30d 100644 (file)
@@ -6113,8 +6113,8 @@ transport_maps to apply this feature selectively:
 .na
 .ft C
 /etc/postfix/transport:
-    smtp-domain_that_verifies_after_data    smtp-data-target:
-    lmtp-domain_that_verifies_after_data    lmtp-data-target:
+    smtp-domain-that-verifies-after-data    smtp-data-target:
+    lmtp-domain-that-verifies-after-data    lmtp-data-target:
 .fi
 .ad
 .ft R
@@ -8538,6 +8538,33 @@ example.com     verify  match=hostname:nexthop
 .ft R
 .PP
 This feature is available in Postfix 2.3 and later.
+.SH smtp_tls_wrappermode (default: no)
+Request that the Postfix SMTP client connects using the
+legacy SMTPS protocol instead of using the STARTTLS command.
+.PP
+This mode requires "smtp_tls_security_level = encrypt" or
+stronger.
+.PP
+Example: deliver all remote mail via a provider's server
+"mail.example.com".
+.PP
+.nf
+.na
+.ft C
+/etc/postfix/main.cf:
+    # Client-side SMTPS requires "encrypt" or stronger.
+    smtp_tls_security_level = encrypt
+    smtp_tls_wrappermode = yes
+    # The [] suppress MX lookups.
+    relayhost = [mail.example.com]:465
+.fi
+.ad
+.ft R
+.PP
+More examples are in TLS_README, including examples for older
+Postfix versions.
+.PP
+This feature is available in Postfix 2.12 and later.
 .SH smtp_use_tls (default: no)
 Opportunistic mode: use TLS when a remote SMTP server announces
 STARTTLS support, otherwise send the mail in the clear. Beware:
@@ -12884,6 +12911,12 @@ The characters Postfix accepts as VERP delimiter characters on the
 Postfix \fBsendmail\fR(1) command line and in SMTP commands.
 .PP
 This feature is available in Postfix 1.1 and later.
+.SH virtual_alias_address_length_limit (default: 1000)
+The maximal length of an email address after virtual alias expansion.
+This stops virtual aliasing loops that increase the address length
+exponentially.
+.PP
+This feature is available in Postfix 2.12 and later.
 .SH virtual_alias_domains (default: $virtual_alias_maps)
 Postfix is final destination for the specified list of virtual
 alias domains, that is, domains for which all addresses are aliased
index 254b4b4d0e90487b21e727854f343acd81b71081..2c358e8a317544747707315bafcc623979ec6cb0 100644 (file)
@@ -348,6 +348,10 @@ The maximal number of addresses that virtual alias expansion produces
 from each original recipient.
 .IP "\fBvirtual_alias_recursion_limit (1000)\fR"
 The maximal nesting depth of virtual alias expansion.
+.PP
+Available in Postfix version 2.12 and later:
+.IP "\fBvirtual_alias_address_length_limit (1000)\fR"
+The maximal length of an email address after virtual alias expansion.
 .SH "MISCELLANEOUS CONTROLS"
 .na
 .nf
index a4d1ead9a8f3a8d14571eb33dc413eb09b89079e..7b4b04bca5bccd2438f134e4820963e26bf8eeab 100644 (file)
@@ -494,6 +494,11 @@ not an alias and its address records lie in an unsigned zone.
 RFC 6698 trust-anchor digest support in the Postfix TLS library.
 .IP "\fBtlsmgr_service_name (tlsmgr)\fR"
 The name of the \fBtlsmgr\fR(8) service entry in master.cf.
+.PP
+Available in Postfix version 2.12 and later:
+.IP "\fBsmtp_tls_wrappermode (no)\fR"
+Request that the Postfix SMTP client connects using the
+legacy SMTPS protocol instead of using the STARTTLS command.
 .SH "OBSOLETE STARTTLS CONTROLS"
 .na
 .nf
index 34234751d3865863252a7a0db279eb93719a3f0e..df47611d01e2832298175f206a760fe7728009ee 100755 (executable)
@@ -614,6 +614,7 @@ while (<>) {
     s;\bunverified_recipient_reject_reason\b;<a href="postconf.5.html#unverified_recipient_reject_reason">$&</a>;g;
     s;\bunverified_sender_reject_reason\b;<a href="postconf.5.html#unverified_sender_reject_reason">$&</a>;g;
     s;\bverp_delimiter_filter\b;<a href="postconf.5.html#verp_delimiter_filter">$&</a>;g;
+    s;\bvir[-</bB>]*\n*[ <bB>]*tual_alias_address_length_limit\b;<a href="postconf.5.html#virtual_alias_address_length_limit">$&</a>;g;
     s;\bvir[-</bB>]*\n*[ <bB>]*tual_alias_domains\b;<a href="postconf.5.html#virtual_alias_domains">$&</a>;g;
     s;\bvir[-</bB>]*\n*[ <bB>]*tual_alias_expansion_limit\b;<a href="postconf.5.html#virtual_alias_expansion_limit">$&</a>;g;
     s;\bvir[-</bB>]*\n*[ <bB>]*tual_alias_maps\b;<a href="postconf.5.html#virtual_alias_maps">$&</a>;g;
@@ -667,6 +668,7 @@ while (<>) {
     s;\bsmtp_tls_session_cache_timeout\b;<a href="postconf.5.html#smtp_tls_session_cache_timeout">$&</a>;g;
     s;\bsmtp_tls_block_early_mail_reply\b;<a href="postconf.5.html#smtp_tls_block_early_mail_reply">$&</a>;g;
     s;\bsmtp_tls_force_insecure_host_tlsa_lookup\b;<a href="postconf.5.html#smtp_tls_force_insecure_host_tlsa_lookup">$&</a>;g;
+    s;\bsmtp_tls_wrappermode\b;<a href="postconf.5.html#smtp_tls_wrappermode">$&</a>;g;
     s;\bsmtp_use_tls\b;<a href="postconf.5.html#smtp_use_tls">$&</a>;g;
     s;\bsmtp_header_checks\b;<a href="postconf.5.html#smtp_header_checks">$&</a>;g;
     s;\bsmtp_mime_header_checks\b;<a href="postconf.5.html#smtp_mime_header_checks">$&</a>;g;
index aabdb833b5fefa5bcb389e217444d169f5a3eaf6..f89befae2f4a5618ca4955f6283e39390086b5e0 100644 (file)
@@ -346,7 +346,7 @@ in forged email.  </p>
 
     # Default setting for Postfix 2.7 and later.
     # Note 1: Be sure to read the "<a href="#caching">Caching</a>" section below!
-    # Note 2: Avoid hash files here. Use btree instead.
+    # Note 2: Avoid hash files here. Use btree or lmdb instead.
     address_verify_map = btree:/var/lib/postfix/verify
  
 /etc/postfix/sender_access:
@@ -393,7 +393,7 @@ you can see what mail would be blocked: </p>
 
     # Default setting for Postfix 2.7 and later.
     # Note 1: Be sure to read the "<a href="#caching">Caching</a>" section below!
-    # Note 2: Avoid hash files here. Use btree instead.
+    # Note 2: Avoid hash files here. Use btree or lmdb instead.
     address_verify_map = btree:/var/lib/postfix/verify
 </pre>
 </blockquote>
@@ -461,7 +461,7 @@ results are lost after "postfix reload" or "postfix stop". </p>
 <blockquote>
 <pre>
 # Example 1: Default setting for Postfix 2.7 and later.
-# Note: avoid hash files here. Use btree instead.
+# Note: avoid hash files here. Use btree or lmdb instead.
 /etc/postfix/main.cf:
     address_verify_map = btree:$data_directory/verify_cache
 
index a68059ff3cbe7e19bf67d2418978dc27033e8fe3..6e4f551396aec65fb0d024841b5eadec6fe0f329 100644 (file)
@@ -110,8 +110,27 @@ respect to earlier Postfix releases: </p>
 <ul>
 
 <li> <p> UTF-8 is permitted in the myorigin parameter value. However,
-the myhostname and mydomain parameters must specify ASCII-only
-domain names. This limitation may be removed later. </p>
+the myhostname and mydomain parameters must currently specify
+ASCII-only domain names. This limitation may be removed later. </p>
+
+<li> <p> UTF-8 is the only form of non-ASCII text that Postfix
+supports in access tables, address rewriting tables, and other
+tables that are indexed with an email address, hostname, or domain
+name. </p>
+
+<li> <p> The header_checks-like and body_checks-like features are
+not UTF-8 enabled, and therefore they do not enforce UTF-8 syntax
+rules on inputs and outputs.  The reason is that non-ASCII text may
+be sent in encodings other than UTF-8, and that real email sometimes
+contains malformed headers.  Instead of skipping non-UTF-8 content,
+Postfix should be able to filter it.  You may try to enable UTF-8
+processing by starting a PCRE pattern with the sequence (*UTF8),
+but this is will result in "message not accepted, try again later"
+errors when the PCRE pattern matcher encounters non-UTF-8 input.
+Other features that are not UTF-8 enabled are smtpd_command_filter,
+smtp_reply_filter, the *_delivery_status_filter features, and the
+*_dns_reply_filter features (the latter because DNS is by definition
+an ASCII protocol).  </p>
 
 <li> <p> The Postfix SMTP server announces SMTPUTF8 support in the
 EHLO response. </p>
@@ -145,7 +164,7 @@ MAIL FROM and VRFY commands. </p>
 MAIL FROM commands. </p>
 
 <li> <p> The Postfix SMTP server accepts UTF-8 in email address
-domains, but only after the remote SMTP client client issues the
+domains, but only after the remote SMTP client issues the
 SMTPUTF8 request in MAIL FROM or VRFY commands.  </p>
 
 </ul>
@@ -257,29 +276,47 @@ delivered it if SMTPUTF8 support was disabled. </p>
 <h2> <a name="limitations">Limitations of the current implementation</a>
 </h2>
 
-<p> "Internationalized" domain names can appear in two forms: the
-UTF-8 form, and the ASCII (xn--mumble) form. The initial Postfix
-SMTPUTF8 implementation performs no automatic conversions on UTF8
-strings beyond what is needed to perform DNS lookups. </p>
+<p> The Postfix implementation is a work in progress; limitations
+are steadily being removed. The text below describes the situation
+at one point in time. </p>
 
-<h3> No characterset canonicalization for non-ASCII domain names.
-</h3>
+<h3> No automatic conversions between ASCII and UTF-8 domain names. </h3>
 
-<p> Postfix currently does not translate domain names from UTF-8
-into ASCII (or ASCII into UTF-8) before looking up the domain name
-in mydestination, relay_domains, access tables, etc., before logging
-the domain name, or before using the domain name in a policy daemon
-or Milter request. You will have to configure both UTF-8 and ASCII
-forms in Postfix configuration files; and both forms will have to
-be handled by logfile tools, policy daemons and Milters. </p>
+<p> Some background: According to RFC 6530 and related documents,
+"Internationalized" domain names can appear in two forms: the UTF-8
+form, and the ASCII (xn--mumble) form. "Internationalized" address
+localparts must be encoded in UTF-8; the RFCs do not define an ASCII
+form for the same information. </p>
 
-<h3> No case canonicalization for non-ASCII characters. </h3>
+<p> Postfix currently does not convert internationalized domain
+names from UTF-8 into ASCII (or from ASCII into UTF-8) before using
+domain names in SMTP commands and responses, before looking up 
+domain names in mydestination, relay_domains, access tables, etc.,
+before using domain names in a policy daemon or Milter request,
+or before logging domain names. </p>
 
-<p> Postfix currently does not case-fold non-ASCII characters when
-looking up an "Internationalized" domain name in mydestination,
-relay_domains, access maps, etc. Some non-ASCII scripts do not
-distinguish between upper and lower case, some have different numbers
-of upper and lower case characters. </p>
+<p> Postfix does, however, casefold domain names and email addresses
+before matching them against a Postfix configuration parameter or
+lookup table. </p>
+
+<ul>
+
+<li> <p> The Postfix parameters myhostname and mydomain must be in
+ASCII form. One is a substring of the other, and the myhostname
+value is used in SMTP commands and responses that require ASCII.
+The parameter myorigin (added to local addresses without domain)
+supports UTF-8.  </p>
+
+<li> <p> You need to configure both the ASCII and UTF-8 forms of
+an Internationalized domain name in Postfix parameters such as
+mydestination and relay_domains, as well as lookup table search
+keys. </p>
+
+<li> <p> Milters, content filters, policy servers and logfile
+analysis tools need to be able to handle both the ASCII and UTF-8
+forms of Internationalized domain names. </p>
+
+</ul>
 
 <h2> <a name="compatibility">Compatibility with pre-SMTPUTF8
 environments</a> </h2>
@@ -290,33 +327,36 @@ environments</a> </h2>
 non-UTF-8 members. Postfix will try to deliver the non-UTF8 subscribers
 over "traditional" non-SMTPUTF8 sessions, as long as the message
 has an ASCII envelope sender address and all-ASCII header values.
-The mailing list manager will have to apply RFC 2047 encoding to
+The mailing list manager may have to apply RFC 2047 encoding to
 satisfy that last condition. </p>
 
 <h3> Pre-existing non-ASCII email flows </h3>
 
-<p> In pre-SMTPUTF8 environments, email with UTF-8 in address
-localparts (and in headers) works just fine. The vast majority
-of email software including Postfix is perfectly capable of handling
-such email, even if pre-SMTPUTF8 standards do not support this. </p>
+<p> With "smtputf8_enable = no", Postfix handles email with non-ASCII
+in address localparts (and in headers) as before.  The vast majority
+of email software is perfectly capable of handling such email, even
+if pre-SMTPUTF8 standards do not support such practice. </p>
 
-<p> Therefore, when Postfix SMTPUTF8 support is turned on, Postfix
-must not suddenly start to break pre-existing email flows with UTF-8
-in addres localparts (and in headers). </p>
-
-<p> Thus, Postfix continues to permit UTF-8 in address localparts
-(and in headers) in email from and to pre-SMTPUTF8 systems.  At
-least, that is the default (see autodetection above). </p>
+<p> However, when you specify "smtputf8_enable = yes", Postfix
+requires that non-ASCII address information is encoded in UTF-8 and
+will reject other encodings such as ISO-8859. It is not practical
+for Postfix to support multiple encodings at the same time.  There
+is no problem with RFC 2047 encodings such as "=?ISO-8859-1?Q?text?=",
+because those use only characters from the ASCII characterset.  </p>
 
 <h2> <a name="credits">Credits</a> </h2>
 
 <ul>
 
-<li> <p> Arnt Gulbrandsen posted his patch for Unicode email support
-on May 15, 2014.  This work was sponsored by CNNIC. </p>
+<li> <p> May 15, 2014: Arnt Gulbrandsen posted his patch for Unicode
+email support.  This work was sponsored by CNNIC. </p>
+
+<li> <p> July 15, 2014: Wietse integrated Arnt Gulbrandsen's code
+and released Postfix with SMTPUTF8 support. </p>
 
-<li> <p> Wietse integrated Arnt Gulbrandsen's code and released
-Postfix with SMTPUTF8 support on July 15, 2014. </p>
+<li> <p> January 2015: Wietse added UTF-8 support for casefolding
+in Postfix lookup tables and caseless string comparison in Postfix
+list-based features. </p>
 
 </ul>
 
index bc492ebd506d2fd8e7dcdbc82aaec3db558af460..23994123a8cff1abad42f8d8d1e14c1a28f384c1 100644 (file)
@@ -1,4 +1,4 @@
-<!doctype html public "-//W3C//DTD HTML 4.01 Transitional//EN"
+<doctype html public "-//W3C//DTD HTML 4.01 Transitional//EN"
         "http://www.w3.org/TR/html4/loose.dtd">
 
 <html>
@@ -2370,14 +2370,75 @@ the SSL/TLS protocols used with opportunistic TLS. </p>
 
 <h3> <a name="client_smtps">Client-side SMTPS support </a> </h3>
 
-<p> Although the Postfix SMTP client by itself doesn't support TLS
+<p> These sections show how to send mail to a server that does not
+support STARTTLS, but that provides the deprecated SMTPS service
+on TCP port 465.  Depending on the Postfix version, some additional
+tooling may be required. </p>
+
+<h4> Postfix &ge; 2.12 </h4>
+
+<p> The Postfix SMTP client has SMTPS support built-in as of version
+2.12.  Use one of the following examples, to send all remote mail,
+or to send only some remote mail, to an SMTPS server.  </p> </p>
+
+<h5> Postfix &ge; 2.12: Sending all remote mail to an SMTPS server </h5>
+
+<p> The first example will send all remote mail over SMTPS through
+a provider's server called "mail.example.com": </p>
+
+<blockquote>
+<pre>
+/etc/postfix/main.cf:
+    # Client-side SMTPS requires "encrypt" or stronger.
+    smtp_tls_security_level = encrypt
+    smtp_tls_wrappermode = yes
+    # The [] suppress MX lookups.
+    relayhost = [mail.example.com]:465
+</pre>
+</blockquote>
+
+<p> Use "postfix reload" to make the change effective. </p>
+
+<p> See SOHO_README for additional information about SASL authentication.
+</p>
+
+<h5> Postfix &ge; 2.12: Sending only mail for a specific destination
+via SMTPS </h5>
+
+<p> The second example will send only mail for "example.com" via
+SMTPS.  This time, Postfix uses a transport map to deliver only
+mail for "example.com" via SMTPS: </p>
+
+<blockquote>
+<pre>
+/etc/postfix/main.cf:
+    transport_maps = hash:/etc/postfix/transport
+
+/etc/postfix/transport:
+    example.com  relay-smtps:example.com:465
+
+/etc/postfix/master.cf:
+    relay-smtps  unix  -       -       n       -       -       smtp
+       # Client-side SMTPS requires "encrypt" or stronger.
+       -o smtp_tls_security_level=encrypt
+       -o smtp_tls_wrappermode=yes
+</pre>
+</blockquote>
+
+<p> Use "postmap hash:/etc/postfix/transport" and "postfix reload"
+to make the change effective. </p>
+
+<p> See SOHO_README for additional information about SASL
+authentication. </p>
+
+<h4> Postfix &lt; 2.12 </h4>
+
+<p> Although older Postfix SMTP client versions do not support TLS
 wrapper mode, it is relatively easy to forward a connection through
 the stunnel program if Postfix needs to deliver mail to some legacy
-system that doesn't support STARTTLS. Use one of the following two
-examples, to send only some remote mail, or to send all remote mail,
-to an SMTPS server.  </p>
+system that doesn't support STARTTLS. </p>
 
-<h4> Sending all remote mail to an SMTPS server </h4>
+<h5> Postfix &lt; 2.12: Sending all remote mail to an SMTPS server </h5>
 
 <p> The first example uses SMTPS to send all remote mail to a
 provider's mail server called "mail.example.com".  </p>
@@ -2420,7 +2481,10 @@ mail through the local stunnel listener on port 11125: </p>
 
 <p> Use "postfix reload" to make the change effective. </p>
 
-<h4> Sending only mail for a specific destination via SMTPS </h4>
+<p> See SOHO_README for additional information about SASL
+authentication. </p>
+
+<h4> Postfix &lt; 2.12: Sending only mail for a specific destination via SMTPS </h4>
 
 <p> The second example will use SMTPS to send only mail for
 "example.com" via SMTPS. It uses the same stunnel configuration
@@ -2442,6 +2506,9 @@ mail for "example.com" through the tunnel: </p>
 <p> Use "postmap hash:/etc/postfix/transport" and "postfix reload"
 to make the change effective. </p>
 
+<p> See SOHO_README for additional information about SASL authentication.
+</p>
+
 <h3> <a name="client_misc"> Miscellaneous client controls </a> </h3>
 
 <p> The smtp_starttls_timeout parameter limits the time of Postfix
index d3783b48d037f3573595bc7731db19a2e71562b3..f830add7a99f96153aa4a457ee34c5e804601d09 100644 (file)
@@ -15437,8 +15437,8 @@ transport_maps to apply this feature selectively:  </p>
 <blockquote>
 <pre>
 /etc/postfix/transport:
-    smtp-domain_that_verifies_after_data    smtp-data-target:
-    lmtp-domain_that_verifies_after_data    lmtp-data-target:
+    smtp-domain-that-verifies-after-data    smtp-data-target:
+    lmtp-domain-that-verifies-after-data    lmtp-data-target:
 </pre>
 </blockquote>
 
@@ -16420,3 +16420,42 @@ with valid PTR etc. records. </p>
 </pre>
 
 <p> This feature is available in Postfix 2.12 and later. </p>
+
+%PARAM smtp_tls_wrappermode no
+
+<p> Request that the Postfix SMTP client connects using the
+legacy SMTPS protocol instead of using the STARTTLS command. </p>
+
+<p> This mode requires "smtp_tls_security_level = encrypt" or
+stronger. </p>
+
+<p> Example: deliver all remote mail via a provider's server
+"mail.example.com".  </p>
+
+<pre>
+/etc/postfix/main.cf:
+    # Client-side SMTPS requires "encrypt" or stronger.
+    smtp_tls_security_level = encrypt
+    smtp_tls_wrappermode = yes
+    # The [] suppress MX lookups.
+    relayhost = [mail.example.com]:465
+</pre>
+
+<p> More examples are in TLS_README, including examples for older
+Postfix versions. </p>
+
+<p> This feature is available in Postfix 2.12 and later.  </p>
+
+%PARAM virtual_alias_address_length_limit 1000
+
+<p>
+The maximal length of an email address after virtual alias expansion.
+This stops virtual aliasing loops that increase the address length
+exponentially.
+</p>
+
+<p>
+This feature is available in Postfix 2.12 and later.
+</p>
+
+
index 7eb5fc3d66a73e87d7e37988f2e45473ce7d5288..ef57d240b7b75d7a211a7a1c688168605d30309c 100644 (file)
@@ -326,7 +326,7 @@ bounce_template.o: ../../include/mac_parse.h
 bounce_template.o: ../../include/mail_conf.h
 bounce_template.o: ../../include/mail_params.h
 bounce_template.o: ../../include/mail_proto.h
-bounce_template.o: ../../include/midna.h
+bounce_template.o: ../../include/midna_domain.h
 bounce_template.o: ../../include/msg.h
 bounce_template.o: ../../include/mymalloc.h
 bounce_template.o: ../../include/nvtable.h
index b33fefc4c846727b21fdfd9a27d1e70c49ed83fe..3b067cb617ba79f861bf58dca0dcd4787833be61 100644 (file)
@@ -319,7 +319,9 @@ static int bounce_notify_proto(char *service_name, VSTREAM *client,
        msg_warn("malformed queue id: %s", printable(STR(queue_id), '?'));
        return (-1);
     }
-    printable(STR(dsn_envid), '?');
+    VS_NEUTER(encoding);
+    VS_NEUTER(sender);
+    VS_NEUTER(dsn_envid);
     if (msg_verbose)
        msg_info("%s: flags=0x%x service=%s queue=%s id=%s encoding=%s smtputf8=%d sender=%s envid=%s ret=0x%x",
                 myname, flags, service_name, STR(queue_name), STR(queue_id),
@@ -380,10 +382,12 @@ static int bounce_verp_proto(char *service_name, VSTREAM *client)
        msg_warn("malformed queue id: %s", printable(STR(queue_id), '?'));
        return (-1);
     }
-    printable(STR(dsn_envid), '?');
+    VS_NEUTER(encoding);
+    VS_NEUTER(sender);
+    VS_NEUTER(dsn_envid);
+    VS_NEUTER(verp_delims);
     if (strlen(STR(verp_delims)) != 2) {
-       msg_warn("malformed verp delimiter string: %s",
-                printable(STR(verp_delims), '?'));
+       msg_warn("malformed verp delimiter string: %s", STR(verp_delims));
        return (-1);
     }
     if (msg_verbose)
@@ -460,7 +464,9 @@ static int bounce_one_proto(char *service_name, VSTREAM *client)
        msg_warn("malformed queue id: %s", printable(STR(queue_id), '?'));
        return (-1);
     }
-    printable(STR(dsn_envid), '?');
+    VS_NEUTER(encoding);
+    VS_NEUTER(sender);
+    VS_NEUTER(dsn_envid);
     VS_NEUTER(rcpt_buf->address);
     VS_NEUTER(rcpt_buf->orig_addr);
     VS_NEUTER(rcpt_buf->dsn_orcpt);
index ca8c8c86f0e6407017259d1ccfe96df0133abb1d..39fef8fdaf7c4920e4750e8ccfe7ce0d39e9f5b3 100644 (file)
 #include <stringops.h>
 #include <mymalloc.h>
 #ifndef NO_EAI
-#include <midna.h>
+#include <midna_domain.h>
 #endif
 
 /* Global library. */
@@ -462,7 +462,7 @@ static const char *bounce_template_lookup(const char *key, int unused_mode,
                             "non-ASCII input value: \"%s\"",
                             tp->origin, key, asc_val);
                    return (asc_val);
-               } else if ((utf8_val = midna_to_utf8(asc_val)) == 0) {
+               } else if ((utf8_val = midna_domain_to_utf8(asc_val)) == 0) {
                    msg_warn("%s: conversion \"%s\" failed: "
                             "input value: \"%s\"",
                             tp->origin, key, asc_val);
index 26392e71e9cd4457cfea82f74cf007bb5c42bd69..b83c5c57d3fe1fba4a4521877583e8684b88ec9b 100644 (file)
 /*     from each original recipient.
 /* .IP "\fBvirtual_alias_recursion_limit (1000)\fR"
 /*     The maximal nesting depth of virtual alias expansion.
+/* .PP
+/*     Available in Postfix version 2.12 and later:
+/* .IP "\fBvirtual_alias_address_length_limit (1000)\fR"
+/*     The maximal length of an email address after virtual alias expansion.
 /* MISCELLANEOUS CONTROLS
 /* .ad
 /* .fi
index a117500d2f846056b2408aacc6c06324c5b7df1f..37ec5b0f7a5adc4551c44649bb2ed8897613a84e 100644 (file)
@@ -164,6 +164,7 @@ char   *var_cleanup_milters;                /* non-SMTP mail */
 char   *var_milt_head_checks;          /* post-Milter header checks */
 int     var_auto_8bit_enc_hdr;         /* auto-detect 8bit encoding header */
 int     var_always_add_hdrs;           /* always add missing headers */
+int     var_virt_addrlen_limit;                /* stop exponential growth */
 
 const CONFIG_INT_TABLE cleanup_int_table[] = {
     VAR_HOPCOUNT_LIMIT, DEF_HOPCOUNT_LIMIT, &var_hopcount_limit, 1, 0,
@@ -171,6 +172,7 @@ const CONFIG_INT_TABLE cleanup_int_table[] = {
     VAR_QATTR_COUNT_LIMIT, DEF_QATTR_COUNT_LIMIT, &var_qattr_count_limit, 1, 0,
     VAR_VIRT_RECUR_LIMIT, DEF_VIRT_RECUR_LIMIT, &var_virt_recur_limit, 1, 0,
     VAR_VIRT_EXPAN_LIMIT, DEF_VIRT_EXPAN_LIMIT, &var_virt_expan_limit, 1, 0,
+    VAR_VIRT_ADDRLEN_LIMIT, DEF_VIRT_ADDRLEN_LIMIT, &var_virt_addrlen_limit, 1, 0,
     VAR_BODY_CHECK_LEN, DEF_BODY_CHECK_LEN, &var_body_check_len, 0, 0,
     0,
 };
@@ -333,20 +335,24 @@ void    cleanup_pre_jail(char *unused_name, char **unused_argv)
     if (*var_canonical_maps)
        cleanup_comm_canon_maps =
            maps_create(VAR_CANONICAL_MAPS, var_canonical_maps,
-                       DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX);
+                       DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX
+                       | DICT_FLAG_UTF8_REQUEST);
     if (*var_send_canon_maps)
        cleanup_send_canon_maps =
            maps_create(VAR_SEND_CANON_MAPS, var_send_canon_maps,
-                       DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX);
+                       DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX
+                       | DICT_FLAG_UTF8_REQUEST);
     if (*var_rcpt_canon_maps)
        cleanup_rcpt_canon_maps =
            maps_create(VAR_RCPT_CANON_MAPS, var_rcpt_canon_maps,
-                       DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX);
+                       DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX
+                       | DICT_FLAG_UTF8_REQUEST);
     if (*var_virt_alias_maps)
        cleanup_virt_alias_maps = maps_create(VAR_VIRT_ALIAS_MAPS,
                                              var_virt_alias_maps,
                                              DICT_FLAG_LOCK
-                                             | DICT_FLAG_FOLD_FIX);
+                                             | DICT_FLAG_FOLD_FIX
+                                             | DICT_FLAG_UTF8_REQUEST);
     if (*var_canon_classes)
        cleanup_comm_canon_flags =
            name_mask(VAR_CANON_CLASSES, canon_class_table,
@@ -375,18 +381,21 @@ void    cleanup_pre_jail(char *unused_name, char **unused_argv)
            maps_create(VAR_BODY_CHECKS, var_body_checks, DICT_FLAG_LOCK);
     if (*var_masq_exceptions)
        cleanup_masq_exceptions =
-           string_list_init(MATCH_FLAG_RETURN, var_masq_exceptions);
+           string_list_init(VAR_MASQ_EXCEPTIONS, MATCH_FLAG_RETURN,
+                            var_masq_exceptions);
     if (*var_masq_classes)
        cleanup_masq_flags = name_mask(VAR_MASQ_CLASSES, masq_class_table,
                                       var_masq_classes);
     if (*var_send_bcc_maps)
        cleanup_send_bcc_maps =
            maps_create(VAR_SEND_BCC_MAPS, var_send_bcc_maps,
-                       DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX);
+                       DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX
+                       | DICT_FLAG_UTF8_REQUEST);
     if (*var_rcpt_bcc_maps)
        cleanup_rcpt_bcc_maps =
            maps_create(VAR_RCPT_BCC_MAPS, var_rcpt_bcc_maps,
-                       DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX);
+                       DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX
+                       | DICT_FLAG_UTF8_REQUEST);
     if (*var_cleanup_milters)
        cleanup_milters = milter_create(var_cleanup_milters,
                                        var_milt_conn_time,
index 7f9919466e195679569033e812a9af3912b7c490..b01971e2daacbbf6be21f39a646815d33feed6c9 100644 (file)
@@ -139,6 +139,15 @@ ARGV   *cleanup_map1n_internal(CLEANUP_STATE *state, const char *addr,
            if ((lookup = mail_addr_map(maps, STR(state->temp1), propagate)) != 0) {
                saved_lhs = mystrdup(argv->argv[arg]);
                for (i = 0; i < lookup->argc; i++) {
+                   if (strlen(lookup->argv[i]) > var_virt_addrlen_limit) {
+                       msg_warn("%s: unreasonable %s result %.300s... -- "
+                                "message not accepted, try again later",
+                                state->queue_id, maps->title, lookup->argv[i]);
+                       state->errs |= CLEANUP_STAT_DEFER;
+                       UPDATE(state->reason, "4.6.0 Alias expansion error");
+                       UNEXPAND(argv, addr);
+                       RETURN(argv);
+                   }
                    unquote_822_local(state->temp1, lookup->argv[i]);
                    if (i == 0) {
                        UPDATE(argv->argv[arg], STR(state->temp1));
index e81bc188c74618b5da809d526c18d102525cea42..58053c063c39bc9fc94605fb2683b191bd6cd257 100644 (file)
@@ -206,7 +206,8 @@ int     main(int argc, char **argv)
 
     var_masq_exceptions = argv[1];
     cleanup_masq_exceptions =
-       string_list_init(MATCH_FLAG_RETURN, var_masq_exceptions);
+       string_list_init(VAR_MASQ_EXCEPTIONS, MATCH_FLAG_RETURN,
+                        var_masq_exceptions);
     masq_domains = argv_split(argv[2], CHARS_COMMA_SP);
     addr = vstring_alloc(1);
     if (strchr(argv[3], '@') == 0)
index 52603cb46b086c3f1275c1f537918e38d5a17d9d..fccb61cd7cbf62a0ae2337127b87d6ed3107d9ad 100644 (file)
@@ -805,7 +805,7 @@ static void flush_service(VSTREAM *client_stream, char *unused_service,
 
 static void pre_jail_init(char *unused_name, char **unused_argv)
 {
-    flush_domains = domain_list_init(MATCH_FLAG_RETURN
+    flush_domains = domain_list_init(VAR_FFLUSH_DOMAINS, MATCH_FLAG_RETURN
                                   | match_parent_style(VAR_FFLUSH_DOMAINS),
                                     var_fflush_domains);
 }
index 2f96f25219c1e6ce4aab9834191304b8a7ed7b50..ab6b8a1ec25d47158332bcf7d948a5ca0c75f113 100644 (file)
@@ -33,7 +33,7 @@ SRCS  = abounce.c anvil_clnt.c been_here.c bounce.c bounce_log.c \
        smtp_reply_footer.c safe_ultostr.c verify_sender_addr.c \
        dict_memcache.c mail_version.c memcache_proto.c server_acl.c \
        mkmap_fail.c haproxy_srvr.c dsn_filter.c dynamicmaps.c uxtext.c \
-       smtputf8.c mail_conf_over.c mail_parm_split.c
+       smtputf8.c mail_conf_over.c mail_parm_split.c midna_adomain.c
 OBJS   = abounce.o anvil_clnt.o been_here.o bounce.o bounce_log.o \
        canon_addr.o cfg_parser.o cleanup_strerror.o cleanup_strflags.o \
        clnt_stream.o conv_time.o db_common.o debug_peer.o debug_process.o \
@@ -68,40 +68,41 @@ OBJS        = abounce.o anvil_clnt.o been_here.o bounce.o bounce_log.o \
        smtp_reply_footer.o safe_ultostr.o verify_sender_addr.o \
        dict_memcache.o mail_version.o memcache_proto.o server_acl.o \
        mkmap_fail.o haproxy_srvr.o dsn_filter.o dynamicmaps.o uxtext.o \
-       smtputf8.o attr_override.o mail_parm_split.o $(NON_PLUGIN_MAP_OBJ)
-# MAP_OBJ is for maps that may be dynamically loaded with dynamicmaps.cf.
-# When hard-linking these maps, makedefs sets NON_PLUGIN_MAP_OBJ=$(MAP_OBJ),
-# otherwise it sets the PLUGIN_* macros.
-MAP_OBJ = dict_ldap.o dict_mysql.o dict_pgsql.o dict_sqlite.o mkmap_cdb.o \
-       mkmap_lmdb.o mkmap_sdbm.o 
-HDRS   = abounce.h anvil_clnt.h been_here.h bounce.h bounce_log.h \
-       canon_addr.h cfg_parser.h cleanup_user.h clnt_stream.h config.h \
-       conv_time.h db_common.h debug_peer.h debug_process.h defer.h \
-       deliver_completed.h deliver_flock.h deliver_pass.h deliver_request.h \
-       dict_ldap.h dict_mysql.h dict_pgsql.h dict_proxy.h dict_sqlite.h domain_list.h \
-       dot_lockfile.h dot_lockfile_as.h dsb_scan.h dsn.h dsn_buf.h \
-       dsn_mask.h dsn_print.h dsn_util.h ehlo_mask.h ext_prop.h \
-       file_id.h flush_clnt.h header_opts.h header_token.h input_transp.h \
-       int_filt.h is_header.h lex_822.h log_adhoc.h mail_addr.h \
-       mail_addr_crunch.h mail_addr_find.h mail_addr_map.h mail_conf.h \
-       mail_copy.h mail_date.h mail_dict.h mail_error.h mail_flush.h \
-       mail_open_ok.h mail_params.h mail_proto.h mail_queue.h mail_run.h \
-       mail_scan_dir.h mail_stream.h mail_task.h mail_version.h maps.h \
-       mark_corrupt.h match_parent_style.h mbox_conf.h mbox_open.h \
-       mime_state.h mkmap.h msg_stats.h mynetworks.h mypwd.h namadr_list.h \
-       off_cvt.h opened.h own_inet_addr.h pipe_command.h post_mail.h \
-       qmgr_user.h qmqp_proto.h quote_821_local.h quote_822_local.h \
-       quote_flags.h rcpt_buf.h rcpt_print.h rec_attr_map.h rec_streamlf.h \
-       rec_type.h recipient_list.h record.h resolve_clnt.h resolve_local.h \
-       rewrite_clnt.h scache.h sent.h smtp_stream.h split_addr.h \
-       string_list.h strip_addr.h sys_exits.h timed_ipc.h tok822.h \
-       trace.h user_acl.h valid_mailhost_addr.h verify.h verify_clnt.h \
-       verp_sender.h wildcard_inet_addr.h xtext.h delivered_hdr.h \
-       fold_addr.h header_body_checks.h data_redirect.h match_service.h \
-       addr_match_list.h smtp_reply_footer.h safe_ultostr.h \
-       verify_sender_addr.h dict_memcache.h memcache_proto.h server_acl.h \
-       haproxy_srvr.h dsn_filter.h dynamicmaps.h uxtext.h smtputf8.h \
-       attr_override.h mail_parm_split.h
+           smtputf8.o attr_override.o mail_parm_split.o midna_adomain.o \
+           $(NON_PLUGIN_MAP_OBJ)
+    # MAP_OBJ is for maps that may be dynamically loaded with dynamicmaps.cf.
+    # When hard-linking these maps, makedefs sets NON_PLUGIN_MAP_OBJ=$(MAP_OBJ),
+    # otherwise it sets the PLUGIN_* macros.
+    MAP_OBJ = dict_ldap.o dict_mysql.o dict_pgsql.o dict_sqlite.o mkmap_cdb.o \
+           mkmap_lmdb.o mkmap_sdbm.o 
+    HDRS       = abounce.h anvil_clnt.h been_here.h bounce.h bounce_log.h \
+           canon_addr.h cfg_parser.h cleanup_user.h clnt_stream.h config.h \
+           conv_time.h db_common.h debug_peer.h debug_process.h defer.h \
+           deliver_completed.h deliver_flock.h deliver_pass.h deliver_request.h \
+           dict_ldap.h dict_mysql.h dict_pgsql.h dict_proxy.h dict_sqlite.h domain_list.h \
+           dot_lockfile.h dot_lockfile_as.h dsb_scan.h dsn.h dsn_buf.h \
+           dsn_mask.h dsn_print.h dsn_util.h ehlo_mask.h ext_prop.h \
+           file_id.h flush_clnt.h header_opts.h header_token.h input_transp.h \
+           int_filt.h is_header.h lex_822.h log_adhoc.h mail_addr.h \
+           mail_addr_crunch.h mail_addr_find.h mail_addr_map.h mail_conf.h \
+           mail_copy.h mail_date.h mail_dict.h mail_error.h mail_flush.h \
+           mail_open_ok.h mail_params.h mail_proto.h mail_queue.h mail_run.h \
+           mail_scan_dir.h mail_stream.h mail_task.h mail_version.h maps.h \
+           mark_corrupt.h match_parent_style.h mbox_conf.h mbox_open.h \
+           mime_state.h mkmap.h msg_stats.h mynetworks.h mypwd.h namadr_list.h \
+           off_cvt.h opened.h own_inet_addr.h pipe_command.h post_mail.h \
+           qmgr_user.h qmqp_proto.h quote_821_local.h quote_822_local.h \
+           quote_flags.h rcpt_buf.h rcpt_print.h rec_attr_map.h rec_streamlf.h \
+           rec_type.h recipient_list.h record.h resolve_clnt.h resolve_local.h \
+           rewrite_clnt.h scache.h sent.h smtp_stream.h split_addr.h \
+           string_list.h strip_addr.h sys_exits.h timed_ipc.h tok822.h \
+           trace.h user_acl.h valid_mailhost_addr.h verify.h verify_clnt.h \
+           verp_sender.h wildcard_inet_addr.h xtext.h delivered_hdr.h \
+           fold_addr.h header_body_checks.h data_redirect.h match_service.h \
+           addr_match_list.h smtp_reply_footer.h safe_ultostr.h \
+           verify_sender_addr.h dict_memcache.h memcache_proto.h server_acl.h \
+           haproxy_srvr.h dsn_filter.h dynamicmaps.h uxtext.h smtputf8.h \
+           attr_override.h mail_parm_split.h midna_adomain.h
 TESTSRC        = rec2stream.c stream2rec.c recdump.c
 DEFS   = -I. -I$(INC_DIR) -D$(SYSTYPE)
 CFLAGS = $(DEBUG) $(OPT) $(DEFS)
@@ -687,8 +688,11 @@ abounce.o: mail_proto.h
 abounce.o: msg_stats.h
 abounce.o: recipient_list.h
 addr_match_list.o: ../../include/argv.h
+addr_match_list.o: ../../include/check_arg.h
 addr_match_list.o: ../../include/match_list.h
 addr_match_list.o: ../../include/sys_defs.h
+addr_match_list.o: ../../include/vbuf.h
+addr_match_list.o: ../../include/vstring.h
 addr_match_list.o: addr_match_list.c
 addr_match_list.o: addr_match_list.h
 anvil_clnt.o: ../../include/attr.h
@@ -877,9 +881,12 @@ db_common.o: db_common.c
 db_common.o: db_common.h
 db_common.o: string_list.h
 debug_peer.o: ../../include/argv.h
+debug_peer.o: ../../include/check_arg.h
 debug_peer.o: ../../include/match_list.h
 debug_peer.o: ../../include/msg.h
 debug_peer.o: ../../include/sys_defs.h
+debug_peer.o: ../../include/vbuf.h
+debug_peer.o: ../../include/vstring.h
 debug_peer.o: debug_peer.c
 debug_peer.o: debug_peer.h
 debug_peer.o: mail_params.h
@@ -1106,8 +1113,11 @@ dict_sqlite.o: dict_sqlite.c
 dict_sqlite.o: dict_sqlite.h
 dict_sqlite.o: string_list.h
 domain_list.o: ../../include/argv.h
+domain_list.o: ../../include/check_arg.h
 domain_list.o: ../../include/match_list.h
 domain_list.o: ../../include/sys_defs.h
+domain_list.o: ../../include/vbuf.h
+domain_list.o: ../../include/vstring.h
 domain_list.o: domain_list.c
 domain_list.o: domain_list.h
 dot_lockfile.o: ../../include/check_arg.h
@@ -1841,8 +1851,11 @@ mark_corrupt.o: mark_corrupt.h
 mark_corrupt.o: msg_stats.h
 mark_corrupt.o: recipient_list.h
 match_parent_style.o: ../../include/argv.h
+match_parent_style.o: ../../include/check_arg.h
 match_parent_style.o: ../../include/match_list.h
 match_parent_style.o: ../../include/sys_defs.h
+match_parent_style.o: ../../include/vbuf.h
+match_parent_style.o: ../../include/vstring.h
 match_parent_style.o: mail_params.h
 match_parent_style.o: match_parent_style.c
 match_parent_style.o: match_parent_style.h
@@ -1895,6 +1908,14 @@ memcache_proto.o: ../../include/vstring.h
 memcache_proto.o: ../../include/vstring_vstream.h
 memcache_proto.o: memcache_proto.c
 memcache_proto.o: memcache_proto.h
+midna_adomain.o: ../../include/check_arg.h
+midna_adomain.o: ../../include/midna_domain.h
+midna_adomain.o: ../../include/stringops.h
+midna_adomain.o: ../../include/sys_defs.h
+midna_adomain.o: ../../include/vbuf.h
+midna_adomain.o: ../../include/vstring.h
+midna_adomain.o: midna_adomain.c
+midna_adomain.o: midna_adomain.h
 mime_state.o: ../../include/check_arg.h
 mime_state.o: ../../include/msg.h
 mime_state.o: ../../include/mymalloc.h
@@ -1994,6 +2015,7 @@ mkmap_open.o: ../../include/msg.h
 mkmap_open.o: ../../include/myflock.h
 mkmap_open.o: ../../include/mymalloc.h
 mkmap_open.o: ../../include/sigdelay.h
+mkmap_open.o: ../../include/stringops.h
 mkmap_open.o: ../../include/sys_defs.h
 mkmap_open.o: ../../include/vbuf.h
 mkmap_open.o: ../../include/vstream.h
@@ -2080,8 +2102,11 @@ mypwd.o: ../../include/sys_defs.h
 mypwd.o: mypwd.c
 mypwd.o: mypwd.h
 namadr_list.o: ../../include/argv.h
+namadr_list.o: ../../include/check_arg.h
 namadr_list.o: ../../include/match_list.h
 namadr_list.o: ../../include/sys_defs.h
+namadr_list.o: ../../include/vbuf.h
+namadr_list.o: ../../include/vstring.h
 namadr_list.o: namadr_list.c
 namadr_list.o: namadr_list.h
 off_cvt.o: ../../include/check_arg.h
@@ -2481,8 +2506,11 @@ stream2rec.o: rec_type.h
 stream2rec.o: record.h
 stream2rec.o: stream2rec.c
 string_list.o: ../../include/argv.h
+string_list.o: ../../include/check_arg.h
 string_list.o: ../../include/match_list.h
 string_list.o: ../../include/sys_defs.h
+string_list.o: ../../include/vbuf.h
+string_list.o: ../../include/vstring.h
 string_list.o: string_list.c
 string_list.o: string_list.h
 strip_addr.o: ../../include/mymalloc.h
index 946cf4ae3b6ac6a9a835c693240427b743100f75..de3d3f9671082329dcabf9ce1ff24b6b43b23fb5 100644 (file)
 
 #ifdef TEST
 
-#include <msg.h>
 #include <stdlib.h>
 #include <unistd.h>
 #include <string.h>
+#include <msg.h>
 #include <vstream.h>
 #include <vstring_vstream.h>
 #include <msg_vstream.h>
+#include <dict.h>
+#include <stringops.h>                 /* util_utf8_enable */
 
 static void usage(char *progname)
 {
@@ -113,7 +115,10 @@ int     main(int argc, char **argv)
     }
     if (argc != optind + 2)
        usage(argv[0]);
-    list = addr_match_list_init(MATCH_FLAG_PARENT | MATCH_FLAG_RETURN, argv[optind]);
+    dict_allow_surrogate = 1;
+    util_utf8_enable = 1;
+    list = addr_match_list_init("command line", MATCH_FLAG_PARENT
+                               | MATCH_FLAG_RETURN, argv[optind]);
     addr = argv[optind + 1];
     if (strcmp(addr, "-") == 0) {
        VSTRING *buf = vstring_alloc(100);
index 67c7e2316d81861c9fabc6b1f5a068c3b64d7ab5..f03c09d95798efb8c0bea02267fe2a9b08429d32 100644 (file)
@@ -21,8 +21,8 @@
   */
 #define ADDR_MATCH_LIST MATCH_LIST
 
-#define addr_match_list_init(f, p) \
-       match_list_init((f), (p), 1, match_hostaddr)
+#define addr_match_list_init(o, f, p) \
+       match_list_init((o), (f), (p), 1, match_hostaddr)
 #define addr_match_list_match(l, a) \
        match_list_match((l), (a))
 #define addr_match_list_free   match_list_free
index 11c7e08814711a59e6bb09b186b4db3f204fa6c5..d281c446245cb9b0e0c524ae286e362515287eca 100644 (file)
@@ -54,6 +54,7 @@ static struct cleanup_flag_map cleanup_flag_map[] = {
     CLEANUP_FLAG_MILTER, "enable_milters",
     CLEANUP_FLAG_SMTP_REPLY, "enable_smtp_reply",
     CLEANUP_FLAG_SMTPUTF8, "smtputf8_requested",
+    CLEANUP_FLAG_AUTOUTF8, "smtputf8_autodetect",
 };
 
 /* cleanup_strflags - map flags code to printable string */
index 1e5a1d4edb236f2a6ebadc279500f101421ef1eb..491a7269010c59c9dfb26756c1f72c119aa74083 100644 (file)
@@ -256,7 +256,8 @@ void    db_common_parse_domain(CFG_PARSER *parser, void *ctxPtr)
 
     domainlist = cfg_get_str(parser, "domain", "", 0, 0);
     if (*domainlist) {
-       ctx->domain = string_list_init(MATCH_FLAG_RETURN, domainlist);
+       ctx->domain = string_list_init(parser->name, MATCH_FLAG_RETURN,
+                                      domainlist);
        if (ctx->domain == 0)
 
            /*
index 023e1ffda771a7e4d68a804d8e6567830530b4ec..06fcfc8632b2a577da75e64cb620ee5c6ab43ce3 100644 (file)
@@ -99,7 +99,7 @@ void    debug_peer_init(void)
      */
     if (*var_debug_peer_list)
        debug_peer_list =
-           namadr_list_init(MATCH_FLAG_RETURN
+           namadr_list_init(VAR_DEBUG_PEER_LIST, MATCH_FLAG_RETURN
                             | match_parent_style(VAR_DEBUG_PEER_LIST),
                             var_debug_peer_list);
 }
index aa8ad6b571ece056dab807beb3d6e741cf4178ad..2acf69fe994da4cdbaf93025014b7813d7e92b90 100644 (file)
@@ -1340,7 +1340,8 @@ static const char *dict_ldap_lookup(DICT *dict, const char *name)
     /*
      * Don't frustrate future attempts to make Postfix UTF-8 transparent.
      */
-    if (!valid_utf8_string(name, strlen(name))) {
+    if ((dict->flags & DICT_FLAG_UTF8_ACTIVE) == 0
+       && !valid_utf8_string(name, strlen(name))) {
        if (msg_verbose)
            msg_info("%s: %s: Skipping lookup of non-UTF-8 key '%s'",
                     myname, dict_ldap->parser->name, name);
@@ -1351,10 +1352,10 @@ static const char *dict_ldap_lookup(DICT *dict, const char *name)
      * Optionally fold the key.
      */
     if (dict->flags & DICT_FLAG_FOLD_FIX) {
-       if (dict->fold_buf == 0)
-           dict->fold_buf = vstring_alloc(10);
-       vstring_strcpy(dict->fold_buf, name);
-       name = lowercase(vstring_str(dict->fold_buf));
+        if (dict->fold_buf == 0)
+            dict->fold_buf = vstring_alloc(10);
+        vstring_strcpy(dict->fold_buf, name);
+        name = lowercase(vstring_str(dict->fold_buf));
     }
 
     /*
index 29410d7851b331bad6f954804dc7274de0303718..0fafdb4bb832ed2f8a929b2d7b82d503b832e0f4 100644 (file)
@@ -156,6 +156,9 @@ static int dict_proxy_sequence(DICT *dict, int function,
            case PROXY_STAT_RETRY:
                *key = *value = 0;
                DICT_ERR_VAL_RETURN(dict, DICT_ERR_RETRY, DICT_STAT_ERROR);
+           case PROXY_STAT_CONFIG:
+               *key = *value = 0;
+               DICT_ERR_VAL_RETURN(dict, DICT_ERR_CONFIG, DICT_STAT_ERROR);
            default:
                msg_warn("%s sequence failed for table \"%s\" function %d: "
                         "unexpected reply status %d",
@@ -226,6 +229,8 @@ static const char *dict_proxy_lookup(DICT *dict, const char *key)
                DICT_ERR_VAL_RETURN(dict, DICT_ERR_NONE, (char *) 0);
            case PROXY_STAT_RETRY:
                DICT_ERR_VAL_RETURN(dict, DICT_ERR_RETRY, (char *) 0);
+           case PROXY_STAT_CONFIG:
+               DICT_ERR_VAL_RETURN(dict, DICT_ERR_CONFIG, (char *) 0);
            default:
                msg_warn("%s lookup failed for table \"%s\" key \"%s\": "
                         "unexpected reply status %d",
@@ -293,6 +298,8 @@ static int dict_proxy_update(DICT *dict, const char *key, const char *value)
                DICT_ERR_VAL_RETURN(dict, DICT_ERR_NONE, DICT_STAT_FAIL);
            case PROXY_STAT_RETRY:
                DICT_ERR_VAL_RETURN(dict, DICT_ERR_RETRY, DICT_STAT_ERROR);
+           case PROXY_STAT_CONFIG:
+               DICT_ERR_VAL_RETURN(dict, DICT_ERR_CONFIG, DICT_STAT_ERROR);
            default:
                msg_warn("%s update failed for table \"%s\" key \"%s\": "
                         "unexpected reply status %d",
@@ -360,6 +367,8 @@ static int dict_proxy_delete(DICT *dict, const char *key)
                DICT_ERR_VAL_RETURN(dict, DICT_ERR_NONE, DICT_STAT_FAIL);
            case PROXY_STAT_RETRY:
                DICT_ERR_VAL_RETURN(dict, DICT_ERR_RETRY, DICT_STAT_ERROR);
+           case PROXY_STAT_CONFIG:
+               DICT_ERR_VAL_RETURN(dict, DICT_ERR_CONFIG, DICT_STAT_ERROR);
            default:
                msg_warn("%s delete failed for table \"%s\" key \"%s\": "
                         "unexpected reply status %d",
index 80dd6e352985d1b5f19ec0f376a9d42ee24d8e19..a5b79243a1f3e4527fc84a441a00279cabed23ca 100644 (file)
@@ -37,6 +37,7 @@ extern DICT *dict_proxy_open(const char *, int, int);
 #define PROXY_STAT_RETRY       2       /* try lookup again later */
 #define PROXY_STAT_BAD         3       /* invalid request parameter */
 #define PROXY_STAT_DENY                4       /* table not approved for proxying */
+#define PROXY_STAT_CONFIG      5       /* DICT_ERR_CONFIG error */
 
 /* LICENSE
 /* .ad
index 3570f337ccda6cd8479b36440c59f7543ca1bf39..3f581abfba9ba56d62eddef94737c0bf0950c033 100644 (file)
@@ -165,7 +165,8 @@ static const char *dict_sqlite_lookup(DICT *dict, const char *name)
     /*
      * Don't frustrate future attempts to make Postfix UTF-8 transparent.
      */
-    if (!valid_utf8_string(name, strlen(name))) {
+    if ((dict->flags & DICT_FLAG_UTF8_ACTIVE) == 0
+       && !valid_utf8_string(name, strlen(name))) {
        if (msg_verbose)
            msg_info("%s: %s: Skipping lookup of non-UTF-8 key '%s'",
                     myname, dict_sqlite->parser->name, name);
index 8433caaf76ce4a8d409bc3a3eb2eace666cb2ac0..2bcd38d2ddf33b2990fe304ba78fa05cf0697417 100644 (file)
 
 #ifdef TEST
 
-#include <msg.h>
 #include <stdlib.h>
 #include <unistd.h>
+#include <msg.h>
 #include <vstream.h>
 #include <msg_vstream.h>
+#include <dict.h>
+#include <stringops.h>                 /* util_utf8_enable */
 
 static void usage(char *progname)
 {
@@ -113,7 +115,10 @@ int     main(int argc, char **argv)
     }
     if (argc != optind + 2)
        usage(argv[0]);
-    list = domain_list_init(MATCH_FLAG_PARENT | MATCH_FLAG_RETURN, argv[optind]);
+    dict_allow_surrogate = 1;
+    util_utf8_enable = 1;
+    list = domain_list_init("command line", MATCH_FLAG_PARENT
+                           | MATCH_FLAG_RETURN, argv[optind]);
     host = argv[optind + 1];
     vstream_printf("%s: %s\n", host, domain_list_match(list, host) ?
                   "YES" : list->error == 0 ? "NO" : "ERROR");
index 8612219d9cd8ca65477fcc221fc062820aa1c3c2..b0dfaec1f67dcf3ee8b3a06f15634aa472fe1961 100644 (file)
@@ -21,7 +21,8 @@
   */
 #define DOMAIN_LIST    MATCH_LIST
 
-#define domain_list_init(f, p) match_list_init((f), (p), 1, match_hostname)
+#define domain_list_init(o, f, p)\
+                       match_list_init((o), (f), (p), 1, match_hostname)
 #define domain_list_match      match_list_match
 #define domain_list_free       match_list_free
 
index 07985dc2073ac7904cc9ab5b070ebbdc375ed7a1..7accaa98abadde8b11bfe7e283351310db492ec6 100644 (file)
@@ -103,7 +103,7 @@ static DOMAIN_LIST *flush_domains;
 
 void    flush_init(void)
 {
-    flush_domains = domain_list_init(MATCH_FLAG_RETURN
+    flush_domains = domain_list_init(VAR_FFLUSH_DOMAINS, MATCH_FLAG_RETURN
                                   | match_parent_style(VAR_FFLUSH_DOMAINS),
                                     var_fflush_domains);
 }
index e21dd360018da3ca0d806276167dd012de90e410..f3a478be099fcdf09894c0a7f9e7e180b54e1dc2 100644 (file)
@@ -202,7 +202,8 @@ int     main(int argc, char **argv)
      * Initialize.
      */
     mail_conf_read();
-    path = maps_create(argv[0], argv[1], DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX);
+    path = maps_create(argv[0], argv[1], DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX \
+                      | DICT_FLAG_UTF8_REQUEST);
     while (vstring_fgets_nonl(buffer, VSTREAM_IN)) {
        extent = 0;
        result = mail_addr_find(path, STR(buffer), &extent);
index e8bc6bc29af27475f5e7ff7566b0c758bc2637dd..6a0479643e61651411539dfa835e3d5ea64f0298 100644 (file)
@@ -175,7 +175,8 @@ int     main(int argc, char **argv)
     msg_verbose = 1;
     if (chdir(var_queue_dir) < 0)
        msg_fatal("chdir %s: %m", var_queue_dir);
-    path = maps_create(argv[0], argv[1], DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX);
+    path = maps_create(argv[0], argv[1], DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX \
+                      | DICT_FLAGS_UTF8_REQUEST);
     while (vstring_fgets_nonl(buffer, VSTREAM_IN)) {
        msg_info("=== Address extension on, extension propagation on ===");
        UPDATE(var_rcpt_delim, "+");
index 435bfd9bd64072eec4d4a0e3b7e09a78041811a2..03c54eb1769545b0269704f5830e588123d878eb 100644 (file)
@@ -636,6 +636,11 @@ void    mail_params_init()
        VAR_DAEMON_OPEN_FATAL, DEF_DAEMON_OPEN_FATAL, &var_daemon_open_fatal,
        0,
     };
+    static const CONFIG_NBOOL_TABLE first_nbool_defaults[] = {
+       /* read and process the following before opening tables. */
+       VAR_SMTPUTF8_ENABLE, DEF_SMTPUTF8_ENABLE, &var_smtputf8_enable,
+       0,
+    };
     static const CONFIG_STR_FN_TABLE function_str_defaults[] = {
        VAR_MYHOSTNAME, check_myhostname, &var_myhostname, 1, 0,
        VAR_MYDOMAIN, check_mydomainname, &var_mydomain, 1, 0,
@@ -758,10 +763,6 @@ void    mail_params_init()
        VAR_STRICT_SMTPUTF8, DEF_STRICT_SMTPUTF8, &var_strict_smtputf8,
        0,
     };
-    static const CONFIG_NBOOL_TABLE nbool_defaults[] = {
-       VAR_SMTPUTF8_ENABLE, DEF_SMTPUTF8_ENABLE, &var_smtputf8_enable,
-       0,
-    };
     const char *cp;
 
     /*
@@ -790,6 +791,23 @@ void    mail_params_init()
     if (var_daemon_open_fatal)
        dict_allow_surrogate = 0;
 
+    /*
+     * Should we open tables with UTF8 support, or in the legacy 8-bit clean
+     * mode with ASCII-only casefolding?
+     */
+    get_mail_conf_nbool_table(first_nbool_defaults);
+
+    /*
+     * Report run-time versus compile-time discrepancies.
+     */
+#ifdef NO_EAI
+    if (var_smtputf8_enable)
+       msg_warn("%s is true, but EAI support is not compiled in",
+                VAR_SMTPUTF8_ENABLE);
+    var_smtputf8_enable = 0;
+#endif
+    util_utf8_enable = var_smtputf8_enable;
+
     /*
      * What protocols should we attempt to support? The result is stored in
      * the global inet_proto_table variable.
@@ -833,7 +851,6 @@ void    mail_params_init()
     get_mail_conf_int_table(other_int_defaults);
     get_mail_conf_long_table(long_defaults);
     get_mail_conf_bool_table(bool_defaults);
-    get_mail_conf_nbool_table(nbool_defaults);
     get_mail_conf_time_table(time_defaults);
     check_default_privs();
     check_mail_owner();
@@ -842,16 +859,6 @@ void    mail_params_init()
     dict_db_cache_size = var_db_read_buf;
     dict_lmdb_map_size = var_lmdb_map_size;
     inet_windowsize = var_inet_windowsize;
-    temp_utf8_kludge = var_smtputf8_enable;
-
-    /*
-     * Report run-time versus compile-time discrepancies.
-     */
-#ifdef NO_EAI
-    if (var_smtputf8_enable)
-       msg_warn("%s is true, but EAI support is not compiled in",
-                VAR_SMTPUTF8_ENABLE);
-#endif
 
     /*
      * Variables whose defaults are determined at runtime, after other
index 4ae37622f27404a259c82eefa5ebc13c5b4e0bc9..8d419e0fcbf4ee664c22f100c6d06611df942aa3 100644 (file)
@@ -1396,6 +1396,12 @@ extern bool var_smtp_enforce_tls;
 #define DEF_LMTP_TLS_ENFORCE_PN        1
 extern bool var_smtp_tls_enforce_peername;
 
+#define VAR_SMTP_TLS_WRAPPER   "smtp_tls_wrappermode"
+#define DEF_SMTP_TLS_WRAPPER   0
+#define VAR_LMTP_TLS_WRAPPER   "lmtp_tls_wrappermode"
+#define DEF_LMTP_TLS_WRAPPER   0
+extern bool var_smtp_tls_wrappermode;
+
 #define VAR_SMTP_TLS_LEVEL     "smtp_tls_security_level"
 #define DEF_SMTP_TLS_LEVEL     ""
 #define VAR_LMTP_TLS_LEVEL     "lmtp_tls_security_level"
@@ -1874,6 +1880,10 @@ extern int var_virt_recur_limit;
 #define DEF_VIRT_EXPAN_LIMIT   1000
 extern int var_virt_expan_limit;
 
+#define VAR_VIRT_ADDRLEN_LIMIT "virtual_alias_address_length_limit"
+#define DEF_VIRT_ADDRLEN_LIMIT 1000
+extern int var_virt_addrlen_limit;
+
  /*
   * Message/queue size limits.
   */
index 3fd05a693d22496140172b3d0fe84e209efa3e49..14f2f9bb1b9fe2819ce2f997afafe4762fafc632 100644 (file)
@@ -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      "20141228"
+#define MAIL_RELEASE_DATE      "20150117"
 #define MAIL_VERSION_NUMBER    "2.12"
 
 #ifdef SNAPSHOT
index b07a4fdcd05c064fa01456d8ea6fb51f86a5bda5..c3c9db66f97d849ed29466833183618cb4ef8da8 100644 (file)
@@ -60,7 +60,8 @@ int     match_parent_style(const char *name)
      */
     if (match_par_dom_list == 0)
        match_par_dom_list =
-           string_list_init(MATCH_FLAG_NONE, var_par_dom_match);
+           string_list_init(VAR_PAR_DOM_MATCH, MATCH_FLAG_NONE,
+                            var_par_dom_match);
 
     /*
      * Look up the parent domain matching policy.
diff --git a/postfix/src/global/midna_adomain.c b/postfix/src/global/midna_adomain.c
new file mode 100644 (file)
index 0000000..81c98d4
--- /dev/null
@@ -0,0 +1,119 @@
+/*++
+/* NAME
+/*     midna_adomain 3
+/* SUMMARY
+/*     address domain part conversion
+/* SYNOPSIS
+/*     #include <midna_adomain.h>
+/*
+/*     char    *midna_adomain_to_ascii(
+/*     VSTRING *dest,
+/*     const char *name)
+/*
+/*     char    *midna_adomain_to_utf8(
+/*     VSTRING *dest,
+/*     const char *name)
+/* DESCRIPTION
+/*     The functions in this module transform the domain portion
+/*     of an email address between ASCII and UTF-8 form.  Both
+/*     functions tolerate a missing domain, and both functions
+/*     return a copy of the input when the domain portion requires
+/*     no conversion.
+/*
+/*     midna_adomain_to_ascii() converts an UTF-8 or ASCII domain
+/*     portion to ASCII.  The result is a null pointer when
+/*     conversion fails.  This function verifies that the resulting
+/*     domain passes valid_hostname().
+/*
+/*     midna_adomain_to_utf8() converts an UTF-8 or ASCII domain
+/*     name to UTF-8.  The result is a null pointer when conversion
+/*     fails.  This function verifies that the resulting domain,
+/*     after conversion to ASCII, passes valid_hostname().
+/* SEE ALSO
+/*     midna_domain(3), Postfix ASCII/UTF-8 domain name conversion
+/* DIAGNOSTICS
+/*     Fatal errors: memory allocation problem.
+/*     Warnings: conversion error or result validation error.
+/* LICENSE
+/* .ad
+/* .fi
+/*     The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/*     Wietse Venema
+/*     IBM T.J. Watson Research
+/*     P.O. Box 704
+/*     Yorktown Heights, NY 10598, USA
+/*--*/
+
+ /*
+  * System library.
+  */
+#include <sys_defs.h>
+#include <string.h>
+
+#ifndef NO_EAI
+#include <unicode/uidna.h>
+
+ /*
+  * Utility library.
+  */
+#include <vstring.h>
+#include <stringops.h>
+#include <midna_domain.h>
+
+ /*
+  * Global library.
+  */
+#include <midna_adomain.h>
+
+#define STR(x) vstring_str(x)
+
+/* midna_adomain_to_utf8 - convert address domain portion to UTF8 */
+
+char   *midna_adomain_to_utf8(VSTRING *dest, const char *src)
+{
+    const char *cp;
+    const char *domain_utf8;
+
+    if ((cp = strrchr(src, '@')) == 0) {
+       vstring_strcpy(dest, src);
+    } else {
+       vstring_sprintf(dest, "%*s@", (int) (cp - src), src);
+       if (*(cp += 1)) {
+           if (allascii(cp) && strstr(cp, "--") == 0) {
+               vstring_strcat(dest, cp);
+           } else if ((domain_utf8 = midna_domain_to_utf8(cp)) == 0) {
+               return (0);
+           } else {
+               vstring_strcat(dest, domain_utf8);
+           }
+       }
+    }
+    return (STR(dest));
+}
+
+/* midna_adomain_to_ascii - convert address domain portion to ASCII */
+
+char   *midna_adomain_to_ascii(VSTRING *dest, const char *src)
+{
+    const char *cp;
+    const char *domain_ascii;
+
+    if ((cp = strrchr(src, '@')) == 0) {
+       vstring_strcpy(dest, src);
+    } else {
+       vstring_sprintf(dest, "%*s@", (int) (cp - src), src);
+       if (*(cp += 1)) {
+           if (allascii(cp)) {
+               vstring_strcat(dest, cp);
+           } else if ((domain_ascii = midna_domain_to_ascii(cp + 1)) == 0) {
+               return (0);
+           } else {
+               vstring_strcat(dest, domain_ascii);
+           }
+       }
+    }
+    return (STR(dest));
+}
+
+#endif                                 /* NO_IDNA */
diff --git a/postfix/src/global/midna_adomain.h b/postfix/src/global/midna_adomain.h
new file mode 100644 (file)
index 0000000..14f02fe
--- /dev/null
@@ -0,0 +1,36 @@
+#ifndef _MIDNA_ADOMAIN_H_INCLUDED_
+#define _MIDNA_ADOMAIN_H_INCLUDED_
+
+/*++
+/* NAME
+/*     midna_adomain 3h
+/* SUMMARY
+/*     domain name conversion
+/* SYNOPSIS
+/*     #include <midna_adomain.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+  * Utility library.
+  */
+#include <vstring.h>
+
+ /*
+  * External interface.
+  */
+extern char *midna_adomain_to_utf8(VSTRING *, const char *);
+extern char *midna_adomain_to_ascii(VSTRING *, const char *);
+
+/* LICENSE
+/* .ad
+/* .fi
+/*     The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/*     Wietse Venema
+/*     IBM T.J. Watson Research
+/*     P.O. Box 704
+/*     Yorktown Heights, NY 10598, USA
+/*--*/
+
+#endif
index 9374f8b455cf09257a8432de96706b9ad9459165..9d15eec30a27f0557c590458e1cddc165ad45e01 100644 (file)
@@ -99,6 +99,7 @@
 #include <dict_fail.h>
 #include <sigdelay.h>
 #include <mymalloc.h>
+#include <stringops.h>
 
 /* Global library. */
 
@@ -295,6 +296,13 @@ MKMAP  *mkmap_open(const char *type, const char *path,
     if (mkmap->after_open)
        mkmap->after_open(mkmap);
 
+    /*
+     * Wrap the dictionary for UTF-8 syntax checks and casefolding.
+     */
+    if ((mkmap->dict->flags & DICT_FLAG_UTF8_ACTIVE) == 0
+       && DICT_NEED_UTF8_ACTIVATION(util_utf8_enable, dict_flags))
+       mkmap->dict = dict_utf8_activate(mkmap->dict);
+
     /*
      * Resume signal delivery if multi-writer safe.
      */
index fc7f681ed239706f3c7b950faddd049eca589212..1be7d54c9ebcb199b503cc99dd1a3b2d9d235ac0 100644 (file)
 
 #ifdef TEST
 
-#include <msg.h>
 #include <stdlib.h>
 #include <unistd.h>
+#include <msg.h>
 #include <vstream.h>
 #include <msg_vstream.h>
 #include <dict.h>
+#include <stringops.h>                 /* util_utf8_enable */
 
 static void usage(char *progname)
 {
@@ -122,7 +123,9 @@ int     main(int argc, char **argv)
     if (argc != optind + 3)
        usage(argv[0]);
     dict_allow_surrogate = 1;
-    list = namadr_list_init(MATCH_FLAG_PARENT | MATCH_FLAG_RETURN, argv[optind]);
+    util_utf8_enable = 1;
+    list = namadr_list_init("command line", MATCH_FLAG_PARENT
+                           | MATCH_FLAG_RETURN, argv[optind]);
     host = argv[optind + 1];
     addr = argv[optind + 2];
     vstream_printf("%s/%s: %s\n", host, addr,
index b2322edf86305e977e512647294045600a33756d..e327784a34eab5dd9d274ed827ba075d477b0e92 100644 (file)
@@ -21,8 +21,8 @@
   */
 #define NAMADR_LIST MATCH_LIST
 
-#define namadr_list_init(f, p) \
-       match_list_init((f), (p), 2, match_hostname, match_hostaddr)
+#define namadr_list_init(o, f, p) \
+       match_list_init((o), (f), (p), 2, match_hostname, match_hostaddr)
 #define namadr_list_match      match_list_match
 #define namadr_list_free       match_list_free
 
index b21fe54eaa7f1a891cbf68c759fb94468d5a7ae5..7df05be912c960d3ca5b0fc560d4103994b80c78 100644 (file)
@@ -2,18 +2,18 @@ dummy/168.100.189.2: YES
 dummy/168.100.189.2: NO
 dummy/168.100.189.3: YES
 dummy/168.100.189.16: NO
-./namadr_list: warning: bad net/mask pattern: "168.100.189.0/98"
+./namadr_list: warning: command line: bad net/mask pattern: "168.100.189.0/98"
 dummy/168.100.189.16: ERROR
-./namadr_list: warning: bad net/mask pattern: "168.100.589.0/28"
+./namadr_list: warning: command line: bad net/mask pattern: "168.100.589.0/28"
 dummy/168.100.189.16: ERROR
 dummy/168.100.989.16: NO
 ./namadr_list: error: unsupported dictionary type: 2001
 ./namadr_list: warning: 2001:240:5c7:0:2d0:b7ff:fe88:2ca7 is unavailable. unsupported dictionary type: 2001
-./namadr_list: warning: 2001:240:5c7:0:2d0:b7ff:fe88:2ca7: table lookup problem
+./namadr_list: warning: command line: 2001:240:5c7:0:2d0:b7ff:fe88:2ca7: table lookup problem
 dummy/2001:240:5c7:0:2d0:b7ff:fe88:2ca7: ERROR
 dummy/2001:240:5c7:0:2d0:b7ff:fe88:2ca7: YES
 dummy/2001:240:5c7:0:2d0:b7ff:fe88:2ca8: NO
-./namadr_list: warning: non-null host address bits in "2001:240:5c7:0:2d0:b7ff:fe88:2ca7/64", perhaps you should use "2001:240:5c7::/64" instead
+./namadr_list: warning: command line: non-null host address bits in "2001:240:5c7:0:2d0:b7ff:fe88:2ca7/64", perhaps you should use "2001:240:5c7::/64" instead
 dummy/2001:240:5c7:0:2d0:b7ff:fe88:2ca8: ERROR
 dummy/2001:240:5c7:0:2d0:b7ff:fe88:2ca8: YES
 dummy/2001:24:5c7:0:2d0:b7ff:fe88:2ca8: NO
@@ -32,10 +32,10 @@ foo/168.100.189.3: YES
 bar/168.100.189.3: NO
 baz/168.100.189.3: YES
 x.x.x/127.0.0.1: NO
-./namadr_list: warning: bad net/mask pattern: "be/be"
+./namadr_list: warning: command line: bad net/mask pattern: "be/be"
 x.x.x/127.0.0.1: ERROR
 x.x.x/127.0.0.1: NO
-./namadr_list: warning: bad address pattern: "be:be"
+./namadr_list: warning: command line: bad address pattern: "be:be"
 x.x.x/::1: ERROR
 foo/168.100.189.3: YES
 bar/168.100.189.3: NO
@@ -43,11 +43,11 @@ foo/168.100.189.3: NO
 bar/168.100.189.3: NO
 foo/168.100.189.3: YES
 bar/168.100.189.3: NO
-./namadr_list: warning: fail:1: table lookup problem
+./namadr_list: warning: command line: fail:1: table lookup problem
 bar/168.100.189.3: ERROR
-./namadr_list: warning: fail:1: table lookup problem
+./namadr_list: warning: command line: fail:1: table lookup problem
 bar/168.100.189.3: ERROR
 ./namadr_list: error: open file /tmp/nosuchfile: No such file or directory
 ./namadr_list: warning: non-existent:/tmp/nosuchfile is unavailable. open file /tmp/nosuchfile: No such file or directory
-./namadr_list: warning: non-existent:/tmp/nosuchfile: table lookup problem
+./namadr_list: warning: command line: non-existent:/tmp/nosuchfile: table lookup problem
 bar/168.100.189.3: ERROR
index 98d04b2a8c8e283a578178e75e756c72a4a01c58..c6ef84808c0155f70f29481eef22d2b8b304bcbd 100644 (file)
@@ -69,7 +69,8 @@ void    resolve_local_init(void)
     /* Allow on-the-fly update to make testing easier. */
     if (resolve_local_list)
        string_list_free(resolve_local_list);
-    resolve_local_list = string_list_init(MATCH_FLAG_RETURN, var_mydest);
+    resolve_local_list = string_list_init(VAR_MYDEST, MATCH_FLAG_RETURN,
+                                         var_mydest);
 }
 
 /* resolve_local - match domain against list of local destinations */
index ce46b616f5a8f949ec9fe524b3aa8fe50c9e21f4..e77146999b11e0bebff3c05b2e6f59b840e3c78a 100644 (file)
@@ -1,6 +1,6 @@
 mydestination=example.com destination=example.com YES
 mydestination=example.net destination=example.com NO
-unknown: warning: fail:1_resolve_local: table lookup problem
+unknown: warning: mydestination: fail:1_resolve_local: table lookup problem
 mydestination=fail:1_resolve_local destination=example.com ERROR
 mydestination=fail:1_resolve_local destination=example.com.. NO
 mydestination=fail:1_resolve_local destination= NO
index 7605e5f5976fdbd31c0f208c22ab77fa6d3ec649..daa2c3ec2b83637f5c614e939d15610f7fdf2d81 100644 (file)
@@ -102,12 +102,12 @@ void    server_acl_pre_jail_init(const char *mynetworks, const char *origin)
            addr_match_list_free(server_acl_mynetworks_host);
     }
     server_acl_mynetworks =
-       addr_match_list_init(MATCH_FLAG_RETURN | match_parent_style(origin),
-                            mynetworks);
+       addr_match_list_init(origin, MATCH_FLAG_RETURN
+                            | match_parent_style(origin), mynetworks);
     if (warn_compat_break_mynetworks_style)
        server_acl_mynetworks_host =
-           addr_match_list_init(MATCH_FLAG_RETURN | match_parent_style(origin),
-                                mynetworks_host());
+           addr_match_list_init(origin, MATCH_FLAG_RETURN
+                                | match_parent_style(origin), mynetworks_host());
 }
 
 /* server_acl_parse - parse access list */
@@ -138,7 +138,8 @@ SERVER_ACL *server_acl_parse(const char *extern_acl, const char *origin)
            } else {
                if (dict_handle(acl) == 0)
                    dict_register(acl, dict_open(acl, O_RDONLY, DICT_FLAG_LOCK
-                                                | DICT_FLAG_FOLD_FIX));
+                                                | DICT_FLAG_FOLD_FIX
+                                                | DICT_FLAG_UTF8_REQUEST));
            }
        }
        argv_add(intern_acl, acl, (char *) 0);
@@ -278,7 +279,7 @@ int     main(void)
        } else if (STREQ(cmd, VAR_SERVER_ACL)) {
            UPDATE_VAR(var_server_acl, value);
        } else if (STREQ(cmd, "address")) {
-           server_acl_pre_jail_init(var_mynetworks, VAR_SERVER_ACL);
+           server_acl_pre_jail_init(var_mynetworks, VAR_MYNETWORKS);
            argv = server_acl_parse(var_server_acl, VAR_SERVER_ACL);
            ret = server_acl_eval(value, argv, VAR_SERVER_ACL);
            argv_free(argv);
index 442fda128914e94b761aa174ec70b96affc6219e..b70f3c654fb6fe09543f6407ecac1612dc32275a 100644 (file)
@@ -9,7 +9,7 @@
 168.100.189.3: permit
 > mynetworks=fail:1
 > address=168.100.189.4
-unknown: warning: fail:1: table lookup problem
+unknown: warning: mynetworks: fail:1: table lookup problem
 unknown: warning: server_acl: permit_mynetworks: mynetworks lookup error -- ignoring the remainder of this access list
 168.100.189.4: error
 > server_acl=fail:1,reject
index a24e4e179da534db2f5d3dd428a1fa505d6222bb..076556783ffd28ace06fe0da6b059fa1154717fa 100644 (file)
 
 #ifdef TEST
 
-#include <msg.h>
 #include <stdlib.h>
 #include <unistd.h>
+#include <msg.h>
 #include <vstream.h>
 #include <vstring.h>
 #include <msg_vstream.h>
+#include <dict.h>
+#include <stringops.h>                 /* util_utf8_enable */
 
 static void usage(char *progname)
 {
@@ -106,7 +108,9 @@ int     main(int argc, char **argv)
     }
     if (argc != optind + 2)
        usage(argv[0]);
-    list = string_list_init(MATCH_FLAG_RETURN, argv[optind]);
+    dict_allow_surrogate = 1;
+    util_utf8_enable = 1;
+    list = string_list_init("command line", MATCH_FLAG_RETURN, argv[optind]);
     string = argv[optind + 1];
     vstream_printf("%s: %s\n", string, string_list_match(list, string) ?
                   "YES" : list->error == 0 ? "NO" : "ERROR");
index 0a96bacfeb017bd11cb706fd1e914a6ec1840f9c..1079a7623061a2f087e9433064a9d04a2625bc36 100644 (file)
@@ -21,7 +21,8 @@
   */
 #define STRING_LIST    MATCH_LIST
 
-#define string_list_init(f, p) match_list_init((f), (p), 1, match_string)
+#define string_list_init(o, f, p) \
+                               match_list_init((o), (f), (p), 1, match_string)
 #define string_list_match      match_list_match
 #define string_list_free       match_list_free
 
index 16cec946a5afe588dc6bf7d037834939ce69545c..a66cf115675471fe3ef2a8e4bb8a60f239483faa 100644 (file)
@@ -209,6 +209,7 @@ TOK822 *tok822_sub_append(TOK822 *t1, TOK822 *t2)
        return (t1->tail = tok822_append(t1->tail, t2));
     } else {
        t1->head = t2;
+       t2->owner = t1;
        while (t2->next)
            (t2 = t2->next)->owner = t1;
        return (t1->tail = t2);
@@ -227,6 +228,7 @@ TOK822 *tok822_sub_prepend(TOK822 *t1, TOK822 *t2)
        return (tp);
     } else {
        t1->head = t2;
+       t2->owner = t1;
        while (t2->next)
            (t2 = t2->next)->owner = t1;
        return (t1->tail = t2);
@@ -259,11 +261,12 @@ TOK822 *tok822_sub_keep_after(TOK822 *t1, TOK822 *t2)
 
 TOK822 *tok822_free_tree(TOK822 *tp)
 {
-    if (tp) {
-       if (tp->next)
-           tok822_free_tree(tp->next);
+    TOK822 *next;
+
+    for (/* void */; tp != 0; tp = next) {
        if (tp->head)
            tok822_free_tree(tp->head);
+       next = tp->next;
        tok822_free(tp);
     }
     return (0);
index 9dd19c0f7e91dea9a988f1f745509c320bbc539e..ecdde9f15b1ee94c76e2fcf9dad43a3880cfc571 100644 (file)
@@ -6,7 +6,8 @@
 /* SYNOPSIS
 /*     #include <user_acl.h>
 /*
-/*     const char *check_user_acl_byuid(acl, uid)
+/*     const char *check_user_acl_byuid(pname, acl, uid)
+/*     cobnst char *pname;
 /*     const char *acl;
 /*     uid_t   uid;
 /* DESCRIPTION
@@ -20,6 +21,8 @@
 /*     calls.
 /*
 /*     Arguments:
+/* .IP pname
+/*     The parameter name of the acl.
 /* .IP acl
 /*     Authorized user name list suitable for input to string_list_init(3).
 /* .IP uid
@@ -59,7 +62,7 @@
 
 /* check_user_acl_byuid - check user authorization */
 
-const char *check_user_acl_byuid(char *acl, uid_t uid)
+const char *check_user_acl_byuid(const char *pname, const char *acl, uid_t uid)
 {
     struct mypasswd *mypwd;
     STRING_LIST *list;
@@ -101,7 +104,7 @@ const char *check_user_acl_byuid(char *acl, uid_t uid)
        name = mypwd->pw_name;
     }
 
-    list = string_list_init(MATCH_FLAG_NONE, acl);
+    list = string_list_init(pname, MATCH_FLAG_NONE, acl);
     if ((matched = string_list_match(list, name)) == 0) {
        if (!who)
            who = vstring_alloc(10);
index 8a9afb2b7b3e46e9ff3672eb936403e30a268243..4e36fbfcb36ec0557cfa6d954495edf6c1e944cf 100644 (file)
@@ -25,7 +25,7 @@
  /*
   * External interface
   */
-extern const char *check_user_acl_byuid(char *, uid_t);
+extern const char *check_user_acl_byuid(const char *, const char *, uid_t);
 
 /* AUTHOR(S)
 /*     Wietse Venema
index 8a517a06183a6024a7f298236613eea800d37b95..bd77fbc9d69c26fc4e26cef8be828b15c07c9e77 100644 (file)
@@ -865,7 +865,8 @@ static void pre_init(char *unused_name, char **unused_argv)
     }
     alias_maps = maps_create("aliases", var_alias_maps,
                             DICT_FLAG_LOCK | DICT_FLAG_PARANOID
-                            | DICT_FLAG_FOLD_FIX);
+                            | DICT_FLAG_FOLD_FIX
+                            | DICT_FLAG_UTF8_REQUEST);
 
     flush_init();
 }
index b46018e9ee15c3b1113b46adb71cec10014a1fbb..887333c62791d0fb87389d5a48c4f33da17c9b5f 100644 (file)
@@ -277,7 +277,8 @@ int     deliver_mailbox(LOCAL_STATE state, USER_ATTR usr_attr, int *statusp)
      */
     if (*var_mbox_transp_maps && transp_maps == 0)
        transp_maps = maps_create(VAR_MBOX_TRANSP_MAPS, var_mbox_transp_maps,
-                                 DICT_FLAG_LOCK | DICT_FLAG_NO_REGSUB);
+                                 DICT_FLAG_LOCK | DICT_FLAG_NO_REGSUB
+                                 | DICT_FLAG_UTF8_REQUEST);
     /* The -1 is a hint for the down-stream deliver_completed() function. */
     if (transp_maps
        && (map_transport = maps_find(transp_maps, state.msg_attr.user,
@@ -332,10 +333,11 @@ int     deliver_mailbox(LOCAL_STATE state, USER_ATTR usr_attr, int *statusp)
 
     if (*var_mailbox_cmd_maps && cmd_maps == 0)
        cmd_maps = maps_create(VAR_MAILBOX_CMD_MAPS, var_mailbox_cmd_maps,
-                              DICT_FLAG_LOCK | DICT_FLAG_PARANOID);
+                              DICT_FLAG_LOCK | DICT_FLAG_PARANOID
+                              | DICT_FLAG_UTF8_REQUEST);
 
     if (cmd_maps && (map_command = maps_find(cmd_maps, state.msg_attr.user,
-                                   DICT_FLAG_NONE)) != 0) {
+                                            DICT_FLAG_NONE)) != 0) {
        status = deliver_command(state, usr_attr, map_command);
     } else if (cmd_maps && cmd_maps->error != 0) {
        /* Details in the logfile. */
index 9fbe6ea5b2bfd352f30bac0e84f103043fc5127c..733aa1350dbf00fb85048bbf1719b28b2ab9df3a 100644 (file)
@@ -109,7 +109,8 @@ int     deliver_unknown(LOCAL_STATE state, USER_ATTR usr_attr)
      */
     if (*var_fbck_transp_maps && transp_maps == 0)
        transp_maps = maps_create(VAR_FBCK_TRANSP_MAPS, var_fbck_transp_maps,
-                                 DICT_FLAG_LOCK | DICT_FLAG_NO_REGSUB);
+                                 DICT_FLAG_LOCK | DICT_FLAG_NO_REGSUB
+                                 | DICT_FLAG_UTF8_REQUEST);
     /* The -1 is a hint for the down-stream deliver_completed() function. */
     if (transp_maps
        && (map_transport = maps_find(transp_maps, state.msg_attr.user,
index 633bdf770aaeabfd966eb7293b6e78da03dea93c..7826725c6ec63eed537569082c954cd8683ade87 100644 (file)
@@ -5,7 +5,7 @@
 /*     Postfix alias database maintenance
 /* SYNOPSIS
 /* .fi
-/*     \fBpostalias\fR [\fB-Nfinoprsvw\fR] [\fB-c \fIconfig_dir\fR]
+/*     \fBpostalias\fR [\fB-Nfinoprsuvw\fR] [\fB-c \fIconfig_dir\fR]
 /*     [\fB-d \fIkey\fR] [\fB-q \fIkey\fR]
 /*             [\fIfile_type\fR:]\fIfile_name\fR ...
 /* DESCRIPTION
 /*     as the original input order.
 /*     This feature is available in Postfix version 2.2 and later,
 /*     and is not available for all database types.
+/* .IP \fB-u\fR
+/*     Disable UTF-8 support. UTF-8 support is enabled by default
+/*     when "smtputf8_enable = yes". It requires that keys and
+/*     values are valid UTF-8 strings.
 /* .IP \fB-v\fR
 /*     Enable verbose logging for debugging purposes. Multiple \fB-v\fR
 /*     options make the software increasingly verbose.
 /* .IP "\fBdefault_database_type (see 'postconf -d' output)\fR"
 /*     The default database type for use in \fBnewaliases\fR(1), \fBpostalias\fR(1)
 /*     and \fBpostmap\fR(1) commands.
+/* .IP "\fBsmtputf8_enable (yes)\fR"
+/*     Enable experimental SMTPUTF8 support for the protocols described
+/*     in RFC 6531..6533.
 /* .IP "\fBsyslog_facility (mail)\fR"
 /*     The syslog facility of Postfix logging.
 /* .IP "\fBsyslog_name (see 'postconf -d' output)\fR"
 /* Application-specific. */
 
 #define STR    vstring_str
+#define LEN    VSTRING_LEN
 
 #define POSTALIAS_FLAG_AS_OWNER        (1<<0)  /* open dest as owner of source */
 #define POSTALIAS_FLAG_SAVE_PERM       (1<<1)  /* copy access permission
@@ -309,7 +317,6 @@ static void postalias(char *map_type, char *path_name, int postalias_flags,
        && (st.st_uid != geteuid() || st.st_gid != getegid()))
        set_eugid(st.st_uid, st.st_gid);
 
-
     /*
      * Open the database, create it when it does not exist, truncate it when
      * it does exist, and lock out any spectators.
@@ -338,6 +345,17 @@ static void postalias(char *map_type, char *path_name, int postalias_flags,
        last_line = 0;
        while (readllines(line_buffer, source_fp, &last_line, &lineno)) {
 
+           /*
+            * First some UTF-8 checks sans casefolding.
+            */
+           if ((mkmap->dict->flags & DICT_FLAG_UTF8_ACTIVE)
+               && !allascii(STR(line_buffer))
+               && !valid_utf8_string(STR(line_buffer), LEN(line_buffer))) {
+               msg_warn("%s, line %d: non-UTF-8 input \"%s\"",
+                        VSTREAM_PATH(source_fp), lineno, STR(line_buffer));
+               continue;
+           }
+
            /*
             * Tokenize the input, so that we do the right thing when a
             * quoted localpart contains special characters such as "@", ":"
@@ -655,7 +673,7 @@ static void postalias_seq(const char *map_type, const char *map_name,
 
 static NORETURN usage(char *myname)
 {
-    msg_fatal("usage: %s [-Nfinoprsvw] [-c config_dir] [-d key] [-q key] [map_type:]file...",
+    msg_fatal("usage: %s [-Nfinoprsuvw] [-c config_dir] [-d key] [-q key] [map_type:]file...",
              myname);
 }
 
@@ -670,7 +688,8 @@ int     main(int argc, char **argv)
     struct stat st;
     int     postalias_flags = POSTALIAS_FLAG_AS_OWNER | POSTALIAS_FLAG_SAVE_PERM;
     int     open_flags = O_RDWR | O_CREAT | O_TRUNC;
-    int     dict_flags = DICT_FLAG_DUP_WARN | DICT_FLAG_FOLD_FIX;
+    int     dict_flags = (DICT_FLAG_DUP_WARN | DICT_FLAG_FOLD_FIX
+                         | DICT_FLAG_UTF8_REQUEST);
     char   *query = 0;
     char   *delkey = 0;
     int     sequence = 0;
@@ -720,7 +739,7 @@ int     main(int argc, char **argv)
     /*
      * Parse JCL.
      */
-    while ((ch = GETOPT(argc, argv, "Nc:d:finopq:rsvw")) > 0) {
+    while ((ch = GETOPT(argc, argv, "Nc:d:finopq:rsuvw")) > 0) {
        switch (ch) {
        default:
            usage(argv[0]);
@@ -768,6 +787,9 @@ int     main(int argc, char **argv)
                msg_fatal("specify only one of -s or -q or -d");
            sequence = 1;
            break;
+       case 'u':
+           dict_flags &= ~DICT_FLAG_UTF8_REQUEST;
+           break;
        case 'v':
            msg_verbose++;
            break;
index 8445edcf47a349629bcd4b19106544ce024017aa..4842d1a8ecdbd380634f3416eaa16fd213d5a230 100644 (file)
@@ -321,7 +321,7 @@ static void pcf_check_master_entry(ARGV *argv, const char *raw_text)
     for (field = PCF_MASTER_FLD_PRIVATE; field <= PCF_MASTER_FLD_CHROOT; field++) {
        cp = argv->argv[field];
        if (cp[1] != 0 || strchr(pcf_valid_bool_types, *cp) == 0)
-           pcf_fix_fatal("invalid %s field \%s\" in \"%s\"",
+           pcf_fix_fatal("invalid %s field \"%s\" in \"%s\"",
                          pcf_str_field_pattern(field), cp, raw_text);
     }
 
@@ -330,12 +330,12 @@ static void pcf_check_master_entry(ARGV *argv, const char *raw_text)
     if (len > 0 && cp[len - 1] == '?')
        len--;
     if (!(cp[0] == '-' && len == 1) && strspn(cp, "0123456789") != len)
-       pcf_fix_fatal("invalid " PCF_MASTER_NAME_WAKEUP " field \%s\" in \"%s\"",
+       pcf_fix_fatal("invalid " PCF_MASTER_NAME_WAKEUP " field \"%s\" in \"%s\"",
                      cp, raw_text);
 
     cp = argv->argv[PCF_MASTER_FLD_MAXPROC];
     if (strcmp("-", cp) != 0 && cp[strspn(cp, "0123456789")] != 0)
-       pcf_fix_fatal("invalid " PCF_MASTER_NAME_MAXPROC " field \%s\" in \"%s\"",
+       pcf_fix_fatal("invalid " PCF_MASTER_NAME_MAXPROC " field \"%s\" in \"%s\"",
                      cp, raw_text);
 }
 
index 9c435fbf58ef36015badf7fd2a6742df965d9b4d..1a4096c7c647b119a9c108c54fdefb9b7263beb5 100644 (file)
@@ -315,7 +315,8 @@ int     main(int argc, char **argv)
      * or in the daemon process?
      */
     mail_dict_init();
-    if ((errstr = check_user_acl_byuid(var_submit_acl, uid)) != 0)
+    if ((errstr = check_user_acl_byuid(VAR_SUBMIT_ACL, var_submit_acl,
+                                      uid)) != 0)
        msg_fatal("User %s(%ld) is not allowed to submit mail",
                  errstr, (long) uid);
 
index af3e5a440ed18534e755ee52c34e0c298f343d93..b1e0eab2d91462a4cb77b6f9d4adc056e6da41a2 100644 (file)
@@ -5,7 +5,7 @@
 /*     Postfix lookup table management
 /* SYNOPSIS
 /* .fi
-/*     \fBpostmap\fR [\fB-Nbfhimnoprsvw\fR] [\fB-c \fIconfig_dir\fR]
+/*     \fBpostmap\fR [\fB-NbfhimnoprsuUvw\fR] [\fB-c \fIconfig_dir\fR]
 /*     [\fB-d \fIkey\fR] [\fB-q \fIkey\fR]
 /*             [\fIfile_type\fR:]\fIfile_name\fR ...
 /* DESCRIPTION
 /*     generates no body-style lookup keys for attachment MIME
 /*     headers and for attached message/* headers.
 /* .sp
+/*     NOTE: with "smtputf8_enable = yes", the \fB-b\fR option
+/*     option disables UTF-8 syntax checks on query keys and
+/*     lookup results. Specify the \fB-U\fR option to force UTF-8
+/*     syntax checks anyway.
+/* .sp
 /*     This feature is available in Postfix version 2.6 and later.
 /* .IP "\fB-c \fIconfig_dir\fR"
 /*     Read the \fBmain.cf\fR configuration file in the named directory
 /*     generates header-style lookup keys for attachment MIME
 /*     headers and for attached message/* headers.
 /* .sp
+/*     NOTE: with "smtputf8_enable = yes", the \fB-b\fR option
+/*     option disables UTF-8 syntax checks on query keys and
+/*     lookup results. Specify the \fB-U\fR option to force UTF-8
+/*     syntax checks anyway.
+/* .sp
 /*     This feature is available in Postfix version 2.6 and later.
 /* .IP \fB-i\fR
 /*     Incremental mode. Read entries from standard input and do not
 /* .sp
 /*     This feature is available in Postfix version 2.2 and later,
 /*     and is not available for all database types.
+/* .IP \fB-u\fR
+/*     Disable UTF-8 support. UTF-8 support is enabled by default
+/*     when "smtputf8_enable = yes". It requires that keys and
+/*     values are valid UTF-8 strings.
+/* .IP \fB-U\fR
+/*     With "smtputf8_enable = yes", force UTF-8 syntax checks
+/*     with the \fB-b\fR and \fB-h\fR options.
 /* .IP \fB-v\fR
 /*     Enable verbose logging for debugging purposes. Multiple \fB-v\fR
 /*     options make the software increasingly verbose.
 /* .IP "\fBdefault_database_type (see 'postconf -d' output)\fR"
 /*     The default database type for use in \fBnewaliases\fR(1), \fBpostalias\fR(1)
 /*     and \fBpostmap\fR(1) commands.
+/* .IP "\fBsmtputf8_enable (yes)\fR"
+/*     Enable experimental SMTPUTF8 support for the protocols described
+/*     in RFC 6531..6533.
 /* .IP "\fBsyslog_facility (mail)\fR"
 /*     The syslog facility of Postfix logging.
 /* .IP "\fBsyslog_name (see 'postconf -d' output)\fR"
@@ -401,6 +421,17 @@ static void postmap(char *map_type, char *path_name, int postmap_flags,
        last_line = 0;
        while (readllines(line_buffer, source_fp, &last_line, &lineno)) {
 
+           /*
+            * First some UTF-8 checks sans casefolding.
+            */
+           if ((mkmap->dict->flags & DICT_FLAG_UTF8_ACTIVE)
+               && !allascii(STR(line_buffer))
+               && !valid_utf8_string(STR(line_buffer), LEN(line_buffer))) {
+               msg_warn("%s, line %d: non-UTF-8 input \"%s\"",
+                        VSTREAM_PATH(source_fp), lineno, STR(line_buffer));
+               continue;
+           }
+
            /*
             * Split on the first whitespace character, then trim leading and
             * trailing whitespace from key and value.
@@ -769,7 +800,7 @@ static void postmap_seq(const char *map_type, const char *map_name,
 
 static NORETURN usage(char *myname)
 {
-    msg_fatal("usage: %s [-Nfinoprsvw] [-c config_dir] [-d key] [-q key] [map_type:]file...",
+    msg_fatal("usage: %s [-NfinoprsuUvw] [-c config_dir] [-d key] [-q key] [map_type:]file...",
              myname);
 }
 
@@ -784,11 +815,13 @@ int     main(int argc, char **argv)
     struct stat st;
     int     postmap_flags = POSTMAP_FLAG_AS_OWNER | POSTMAP_FLAG_SAVE_PERM;
     int     open_flags = O_RDWR | O_CREAT | O_TRUNC;
-    int     dict_flags = DICT_FLAG_DUP_WARN | DICT_FLAG_FOLD_FIX;
+    int     dict_flags = (DICT_FLAG_DUP_WARN | DICT_FLAG_FOLD_FIX
+                         | DICT_FLAG_UTF8_REQUEST);
     char   *query = 0;
     char   *delkey = 0;
     int     sequence = 0;
     int     found;
+    int     force_utf8 = 0;
 
     /*
      * Fingerprint executables and core dumps.
@@ -834,7 +867,7 @@ int     main(int argc, char **argv)
     /*
      * Parse JCL.
      */
-    while ((ch = GETOPT(argc, argv, "Nbc:d:fhimnopq:rsvw")) > 0) {
+    while ((ch = GETOPT(argc, argv, "Nbc:d:fhimnopq:rsuUvw")) > 0) {
        switch (ch) {
        default:
            usage(argv[0]);
@@ -891,6 +924,12 @@ int     main(int argc, char **argv)
                msg_fatal("specify only one of -s or -q or -d");
            sequence = 1;
            break;
+       case 'u':
+           dict_flags &= ~DICT_FLAG_UTF8_REQUEST;
+           break;
+       case 'U':
+           force_utf8 = 1;
+           break;
        case 'v':
            msg_verbose++;
            break;
@@ -911,6 +950,9 @@ int     main(int argc, char **argv)
        && (postmap_flags & POSTMAP_FLAG_ANY_KEY)
        == (postmap_flags & POSTMAP_FLAG_MIME_KEY))
        msg_warn("ignoring -m option without -b or -h");
+    if ((postmap_flags & (POSTMAP_FLAG_ANY_KEY & ~POSTMAP_FLAG_MIME_KEY))
+       && force_utf8 == 0)
+       dict_flags &= ~DICT_FLAG_UTF8_MASK;
 
     /*
      * Use the map type specified by the user, or fall back to a default
index caa0e059b58118638ec1def630c65ae4c3a5b707..c90285db73119b0eb23e705bc40c51c0b34c3258 100644 (file)
@@ -270,7 +270,8 @@ static void show_queue(void)
     uid_t   uid = getuid();
 
     if (uid != 0 && uid != var_owner_uid
-       && (errstr = check_user_acl_byuid(var_showq_acl, uid)) != 0)
+       && (errstr = check_user_acl_byuid(VAR_SHOWQ_ACL, var_showq_acl,
+                                         uid)) != 0)
        msg_fatal_status(EX_NOPERM,
                       "User %s(%ld) is not allowed to view the mail queue",
                         errstr, (long) uid);
@@ -344,7 +345,8 @@ static void flush_queue(void)
     uid_t   uid = getuid();
 
     if (uid != 0 && uid != var_owner_uid
-       && (errstr = check_user_acl_byuid(var_flush_acl, uid)) != 0)
+       && (errstr = check_user_acl_byuid(VAR_FLUSH_ACL, var_flush_acl,
+                                         uid)) != 0)
        msg_fatal_status(EX_NOPERM,
                      "User %s(%ld) is not allowed to flush the mail queue",
                         errstr, (long) uid);
@@ -370,7 +372,8 @@ static void flush_site(const char *site)
     uid_t   uid = getuid();
 
     if (uid != 0 && uid != var_owner_uid
-       && (errstr = check_user_acl_byuid(var_flush_acl, uid)) != 0)
+       && (errstr = check_user_acl_byuid(VAR_FLUSH_ACL, var_flush_acl,
+                                         uid)) != 0)
        msg_fatal_status(EX_NOPERM,
                      "User %s(%ld) is not allowed to flush the mail queue",
                         errstr, (long) uid);
@@ -404,7 +407,8 @@ static void flush_file(const char *queue_id)
     uid_t   uid = getuid();
 
     if (uid != 0 && uid != var_owner_uid
-       && (errstr = check_user_acl_byuid(var_flush_acl, uid)) != 0)
+       && (errstr = check_user_acl_byuid(VAR_FLUSH_ACL, var_flush_acl,
+                                         uid)) != 0)
        msg_fatal_status(EX_NOPERM,
                      "User %s(%ld) is not allowed to flush the mail queue",
                         errstr, (long) uid);
index d7dbaaa94caed21103264df332963f17f235e1c0..e4bf8e4865473e44f004e49a367b973bff0b2d19 100644 (file)
@@ -852,7 +852,8 @@ static void pre_jail_init(char *unused_name, char **unused_argv)
        psc_acl = psc_acl_parse(var_psc_acl, VAR_PSC_ACL);
     /* Ignore smtpd_forbid_cmds lookup errors. Non-critical feature. */
     if (*var_psc_forbid_cmds)
-       psc_forbid_cmds = string_list_init(MATCH_FLAG_RETURN,
+       psc_forbid_cmds = string_list_init(VAR_PSC_FORBID_CMDS,
+                                          MATCH_FLAG_RETURN,
                                           var_psc_forbid_cmds);
     if (*var_psc_dnsbl_reply)
        psc_dnsbl_reply = dict_open(var_psc_dnsbl_reply, O_RDONLY,
@@ -998,7 +999,8 @@ static void post_jail_init(char *unused_name, char **unused_argv)
        msg_fatal("bad %s value: %s", VAR_PSC_BARLF_ACTION,
                  var_psc_barlf_action);
     /* Fail "closed" on error. */
-    psc_wlist_if = addr_match_list_init(MATCH_FLAG_RETURN, var_psc_wlist_if);
+    psc_wlist_if = addr_match_list_init(VAR_PSC_WLIST_IF, MATCH_FLAG_RETURN,
+                                       var_psc_wlist_if);
 
     /*
      * Start the cache maintenance pseudo thread last. Early cleanup makes
index 02b4261ab76e3a38b3c72f87e05ee7d68f51fd61..e693e4795e7ac3a78147bf60c554cb06ee4cd598 100644 (file)
@@ -74,7 +74,7 @@ posttls-finger.o: ../../include/iostuff.h
 posttls-finger.o: ../../include/mail_conf.h
 posttls-finger.o: ../../include/mail_params.h
 posttls-finger.o: ../../include/mail_server.h
-posttls-finger.o: ../../include/midna.h
+posttls-finger.o: ../../include/midna_domain.h
 posttls-finger.o: ../../include/msg.h
 posttls-finger.o: ../../include/msg_vstream.h
 posttls-finger.o: ../../include/myaddrinfo.h
index ac4bc8770bac4a513698e0ca78a816238130629c..8be46e40bd1564f54e3b114516d0814ad42af811 100644 (file)
 #include <sane_connect.h>
 #include <myaddrinfo.h>
 #include <sock_addr.h>
-#include <midna.h>
+#include <midna_domain.h>
 
 #define STR(x)         vstring_str(x)
 
@@ -1103,7 +1103,7 @@ static DNS_RR *domain_addr(STATE *state, char *domain)
      * IDNA support.
      */
 #ifndef NO_EAI
-    if (!allascii(domain) && (aname = midna_to_ascii(domain)) != 0) {
+    if (!allascii(domain) && (aname = midna_domain_to_ascii(domain)) != 0) {
        msg_info("%s asciified to %s", domain, aname);
     } else
 #endif
@@ -1168,7 +1168,7 @@ static DNS_RR *host_addr(STATE *state, const char *host)
      * IDNA support.
      */
 #ifndef NO_EAI
-    if (!allascii(host) && (ahost = midna_to_ascii(host)) != 0) {
+    if (!allascii(host) && (ahost = midna_domain_to_ascii(host)) != 0) {
        msg_info("%s asciified to %s", host, ahost);
     } else
 #endif
index 416f523cb01d320a4730d65d2508d0bcfc69646b..262aeeb14b5c29d02a90189f8ce21bd6a660de19 100644 (file)
@@ -382,7 +382,8 @@ static void proxymap_sequence_service(VSTREAM *client_stream)
            reply_status = PROXY_STAT_NOKEY;
            reply_key = reply_value = "";
        } else {
-           reply_status = PROXY_STAT_RETRY;
+           reply_status = (dict->error == DICT_ERR_RETRY ?
+                           PROXY_STAT_RETRY : PROXY_STAT_CONFIG);
            reply_key = reply_value = "";
        }
     }
@@ -427,7 +428,8 @@ static void proxymap_lookup_service(VSTREAM *client_stream)
        reply_status = PROXY_STAT_NOKEY;
        reply_value = "";
     } else {
-       reply_status = PROXY_STAT_RETRY;
+       reply_status = (dict->error == DICT_ERR_RETRY ?
+                       PROXY_STAT_RETRY : PROXY_STAT_CONFIG);
        reply_value = "";
     }
 
@@ -482,7 +484,8 @@ static void proxymap_update_service(VSTREAM *client_stream)
        } else if (dict->error == 0) {
            reply_status = PROXY_STAT_NOKEY;
        } else {
-           reply_status = PROXY_STAT_RETRY;
+           reply_status = (dict->error == DICT_ERR_RETRY ?
+                           PROXY_STAT_RETRY : PROXY_STAT_CONFIG);
        }
     }
 
@@ -532,7 +535,8 @@ static void proxymap_delete_service(VSTREAM *client_stream)
        } else if (dict->error == 0) {
            reply_status = PROXY_STAT_NOKEY;
        } else {
-           reply_status = PROXY_STAT_RETRY;
+           reply_status = (dict->error == DICT_ERR_RETRY ?
+                           PROXY_STAT_RETRY : PROXY_STAT_CONFIG);
        }
     }
 
index d1864ab2da851a94152a2448aa28d06c1c7f8ad3..2f1347187878a41891dd1e05931b3ee07ff0575f 100644 (file)
@@ -785,7 +785,7 @@ static void pre_jail_init(char *unused_name, char **unused_argv)
 {
     debug_peer_init();
     qmqpd_clients =
-       namadr_list_init(MATCH_FLAG_RETURN
+       namadr_list_init(VAR_QMQPD_CLIENTS, MATCH_FLAG_RETURN
                         | match_parent_style(VAR_QMQPD_CLIENTS),
                         var_qmqpd_clients);
 }
index f8ce7dfdbc579f118ea02caa7a09e25852c9c534..4a91aa48e07b43a114d87e0f4e1b8d12251e4f05 100644 (file)
@@ -646,7 +646,8 @@ static void enqueue(const int flags, const char *encoding,
      * Access control is enforced in the postdrop command. The code here
      * merely produces a more user-friendly interface.
      */
-    if ((errstr = check_user_acl_byuid(var_submit_acl, uid)) != 0)
+    if ((errstr = check_user_acl_byuid(VAR_SUBMIT_ACL,
+                                      var_submit_acl, uid)) != 0)
        msg_fatal_status(EX_NOPERM,
          "User %s(%ld) is not allowed to submit mail", errstr, (long) uid);
 
@@ -1404,7 +1405,8 @@ int     main(int argc, char **argv)
            msg_fatal_status(EX_USAGE,
                             "stand-alone mode requires no recipient");
        /* The actual enforcement happens in the postdrop command. */
-       if ((errstr = check_user_acl_byuid(var_submit_acl, uid = getuid())) != 0)
+       if ((errstr = check_user_acl_byuid(VAR_SUBMIT_ACL, var_submit_acl,
+                                          uid = getuid())) != 0)
            msg_fatal_status(EX_NOPERM,
                             "User %s(%ld) is not allowed to submit mail",
                             errstr, (long) uid);
index bcbd2f2fad857a35be3cbad4801980e942cab590..7edc54ef7ef4212bc2ecab93006097b13682353f 100644 (file)
@@ -154,7 +154,7 @@ smtp_addr.o: ../../include/inet_proto.h
 smtp_addr.o: ../../include/mail_params.h
 smtp_addr.o: ../../include/maps.h
 smtp_addr.o: ../../include/match_list.h
-smtp_addr.o: ../../include/midna.h
+smtp_addr.o: ../../include/midna_domain.h
 smtp_addr.o: ../../include/mime_state.h
 smtp_addr.o: ../../include/msg.h
 smtp_addr.o: ../../include/msg_stats.h
@@ -380,12 +380,14 @@ smtp_proto.o: ../../include/mail_queue.h
 smtp_proto.o: ../../include/maps.h
 smtp_proto.o: ../../include/mark_corrupt.h
 smtp_proto.o: ../../include/match_list.h
+smtp_proto.o: ../../include/match_parent_style.h
 smtp_proto.o: ../../include/mime_state.h
 smtp_proto.o: ../../include/msg.h
 smtp_proto.o: ../../include/msg_stats.h
 smtp_proto.o: ../../include/myaddrinfo.h
 smtp_proto.o: ../../include/myflock.h
 smtp_proto.o: ../../include/mymalloc.h
+smtp_proto.o: ../../include/namadr_list.h
 smtp_proto.o: ../../include/name_code.h
 smtp_proto.o: ../../include/name_mask.h
 smtp_proto.o: ../../include/nvtable.h
index 9f405c4a47c82171abd143633692e7cb943bee7d..5f47a2a9be6144ce50099f6268bc8c7c58264302 100644 (file)
        VAR_LMTP_TLS_BLK_EARLY_MAIL_REPLY, DEF_LMTP_TLS_BLK_EARLY_MAIL_REPLY, &var_smtp_tls_blk_early_mail_reply,
        VAR_LMTP_TLS_FORCE_TLSA, DEF_LMTP_TLS_FORCE_TLSA, &var_smtp_tls_force_tlsa,
 #endif
+       VAR_LMTP_TLS_WRAPPER, DEF_LMTP_TLS_WRAPPER, &var_smtp_tls_wrappermode,
        VAR_LMTP_SENDER_AUTH, DEF_LMTP_SENDER_AUTH, &var_smtp_sender_auth,
        VAR_LMTP_CNAME_OVERR, DEF_LMTP_CNAME_OVERR, &var_smtp_cname_overr,
        VAR_LMTP_SASL_AUTH_SOFT_BOUNCE, DEF_LMTP_SASL_AUTH_SOFT_BOUNCE, &var_smtp_sasl_auth_soft_bounce,
index 977f8eb955e3644ee63c176b5f12d33f11dd45df..5692f5d65f358ae5bc86588ea1693df7dac1daca 100644 (file)
 /*     RFC 6698 trust-anchor digest support in the Postfix TLS library.
 /* .IP "\fBtlsmgr_service_name (tlsmgr)\fR"
 /*     The name of the \fBtlsmgr\fR(8) service entry in master.cf.
+/* .PP
+/*     Available in Postfix version 2.12 and later:
+/* .IP "\fBsmtp_tls_wrappermode (no)\fR"
+/*     Request that the Postfix SMTP client connects using the
+/*     legacy SMTPS protocol instead of using the STARTTLS command.
 /* OBSOLETE STARTTLS CONTROLS
 /* .ad
 /* .fi
@@ -850,6 +855,7 @@ bool    var_smtp_use_tls;
 bool    var_smtp_enforce_tls;
 char   *var_smtp_tls_per_site;
 char   *var_smtp_tls_policy;
+bool    var_smtp_tls_wrappermode;
 
 #ifdef USE_TLS
 char   *var_smtp_sasl_tls_opts;
@@ -1186,7 +1192,9 @@ static void pre_init(char *unused_name, char **unused_argv)
      * Session cache domain list.
      */
     if (*var_smtp_cache_dest)
-       smtp_cache_dest = string_list_init(MATCH_FLAG_RETURN, var_smtp_cache_dest);
+       smtp_cache_dest = string_list_init(VAR_SMTP_CACHE_DEST,
+                                          MATCH_FLAG_RETURN,
+                                          var_smtp_cache_dest);
 
     /*
      * EHLO keyword filter.
@@ -1213,7 +1221,8 @@ static void pre_init(char *unused_name, char **unused_argv)
     if (*var_smtp_generic_maps)
        smtp_generic_maps =
            maps_create(VAR_LMTP_SMTP(GENERIC_MAPS), var_smtp_generic_maps,
-                       DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX);
+                       DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX
+                       | DICT_FLAG_UTF8_REQUEST);
 
     /*
      * Header/body checks.
index 7ac4607f47ee963782b08c055b974fc600c73aa7..d02b2531918e89b639b95d699c4b11b1395c3102 100644 (file)
@@ -85,7 +85,7 @@
 #include <stringops.h>
 #include <myaddrinfo.h>
 #include <inet_proto.h>
-#include <midna.h>
+#include <midna_domain.h>
 
 /* Global library. */
 
@@ -378,7 +378,7 @@ DNS_RR *smtp_domain_addr(const char *name, DNS_RR **mxrr, int misc_flags,
      * IDNA support.
      */
 #ifndef NO_EAI
-    if (!allascii(name) && (aname = midna_to_ascii(name)) != 0) {
+    if (!allascii(name) && (aname = midna_domain_to_ascii(name)) != 0) {
        if (msg_verbose)
            msg_info("%s asciified to %s", name, aname);
     } else
@@ -524,7 +524,7 @@ DNS_RR *smtp_host_addr(const char *host, int misc_flags, DSN_BUF *why)
      * IDNA support.
      */
 #ifndef NO_EAI
-    if (!allascii(host) && (ahost = midna_to_ascii(host)) != 0) {
+    if (!allascii(host) && (ahost = midna_domain_to_ascii(host)) != 0) {
        if (msg_verbose)
            msg_info("%s asciified to %s", host, ahost);
     } else
index 02571d929d05ac3d74a2bacc5073f67e580f16e0..b81ead92a0867b854542f7c050eb60c5b428d980 100644 (file)
@@ -820,9 +820,11 @@ static void smtp_connect_inet(SMTP_STATE *state, const char *nexthop,
         * specified, or when DNS lookups are disabled.
         */
        dest_buf = smtp_parse_destination(dest, def_service, &domain, &port);
-       if (var_helpful_warnings && ntohs(port) == 465) {
-           msg_info("CLIENT wrappermode (port smtps/465) is unimplemented");
-           msg_info("instead, send to (port submission/587) with STARTTLS");
+       if (var_helpful_warnings && var_smtp_tls_wrappermode == 0
+           && ntohs(port) == 465) {
+           msg_info("SMTPS wrappermode (TCP port 465) requires setting "
+                    "\"%s = yes\", and \"%s = encrypt\" (or stronger)",
+                    VAR_LMTP_SMTP(TLS_WRAPPER), VAR_LMTP_SMTP(TLS_LEVEL));
        }
 #define NO_HOST        ""                              /* safety */
 #define NO_ADDR        ""                              /* safety */
@@ -957,6 +959,13 @@ static void smtp_connect_inet(SMTP_STATE *state, const char *nexthop,
                continue;
                /* XXX Assume there is no code at the end of this loop. */
            }
+           if (var_smtp_tls_wrappermode
+               && state->tls->level < TLS_LEV_ENCRYPT) {
+               msg_warn("%s requires \"%s = encrypt\" (or stronger)",
+                     VAR_LMTP_SMTP(TLS_WRAPPER), VAR_LMTP_SMTP(TLS_LEVEL));
+               continue;
+               /* XXX Assume there is no code at the end of this loop. */
+           }
            /* Disable TLS when retrying after a handshake failure */
            if (retry_plain) {
                state->tls->level = TLS_LEV_NONE;
index 96326ebe66b7e817787f1ad407aca5fa7eb26415..8579f915eb77b8e498c690e4dc77d51cca254770 100644 (file)
@@ -142,7 +142,9 @@ int     main(int argc, char **argv)
     if (argc < 3)
        msg_fatal("usage: %s maptype:mapname address...", argv[0]);
 
-    maps = maps_create(argv[1], argv[1], DICT_FLAG_FOLD_FIX);
+    util_utf8_enable = 1;
+    maps = maps_create(argv[1], argv[1], DICT_FLAG_FOLD_FIX
+                      | DICT_FLAG_UTF8_REQUEST);
     mail_params_init();
     if (chdir(var_queue_dir) < 0)
        msg_fatal("chdir(%s): %m", var_queue_dir);
index cb66a6f279aa3ff24b7aac3619e02aebd42fbd5b..234644137903e0dab611702fbe9383754f6aed87 100644 (file)
        VAR_SMTP_TLS_BLK_EARLY_MAIL_REPLY, DEF_SMTP_TLS_BLK_EARLY_MAIL_REPLY, &var_smtp_tls_blk_early_mail_reply,
        VAR_SMTP_TLS_FORCE_TLSA, DEF_SMTP_TLS_FORCE_TLSA, &var_smtp_tls_force_tlsa,
 #endif
+       VAR_SMTP_TLS_WRAPPER, DEF_SMTP_TLS_WRAPPER, &var_smtp_tls_wrappermode,
        VAR_SMTP_SENDER_AUTH, DEF_SMTP_SENDER_AUTH, &var_smtp_sender_auth,
        VAR_SMTP_CNAME_OVERR, DEF_SMTP_CNAME_OVERR, &var_smtp_cname_overr,
        VAR_SMTP_SASL_AUTH_SOFT_BOUNCE, DEF_SMTP_SASL_AUTH_SOFT_BOUNCE, &var_smtp_sasl_auth_soft_bounce,
index 9bf9926ff2cc5f8fcf54e80d42ea53913b819ec7..e55110895c0d966ef1a44a307d5e3db84a9e5058 100644 (file)
@@ -324,6 +324,20 @@ int     smtp_helo(SMTP_STATE *state)
 #endif
     const char *NOCLOBBER where;
 
+    /*
+     * Skip the plaintext SMTP handshake when connecting in SMTPS mode.
+     */
+#ifdef USE_TLS
+    if (var_smtp_tls_wrappermode
+       && (state->misc_flags & SMTP_MISC_FLAG_IN_STARTTLS) == 0) {
+       /* XXX Mix-up of per-session and per-request flags. */
+       state->misc_flags |= SMTP_MISC_FLAG_IN_STARTTLS;
+       tls_helo_status = smtp_start_tls(state);
+       state->misc_flags &= ~SMTP_MISC_FLAG_IN_STARTTLS;
+       return (tls_helo_status);
+    }
+#endif
+
     /*
      * Prepare for disaster.
      */
@@ -336,7 +350,8 @@ int     smtp_helo(SMTP_STATE *state)
      * If not recursing after STARTTLS, examine the server greeting banner
      * and decide if we are going to send EHLO as the next command.
      */
-    if ((state->misc_flags & SMTP_MISC_FLAG_IN_STARTTLS) == 0) {
+    if (var_smtp_tls_wrappermode
+       || (state->misc_flags & SMTP_MISC_FLAG_IN_STARTTLS) == 0) {
 
        /*
         * Read and parse the server's SMTP greeting banner.
index 29ad938cb8c6a01f279f564f0176e040ed916c5e..b0ae35730b09b0a88aea21171cadb29f50c2e0cc 100644 (file)
@@ -131,7 +131,7 @@ SMTP_SASL_AUTH_CACHE *smtp_sasl_auth_cache_init(const char *map, int ttl)
      * dict_proxy module one level down in the build dependency hierachy.
      */
 #define CACHE_DICT_OPEN_FLAGS \
-       (DICT_FLAG_DUP_REPLACE | DICT_FLAG_SYNC_UPDATE)
+       (DICT_FLAG_DUP_REPLACE | DICT_FLAG_SYNC_UPDATE | DICT_FLAG_UTF8_REQUEST)
 #define PROXY_COLON    DICT_TYPE_PROXY ":"
 #define PROXY_COLON_LEN        (sizeof(PROXY_COLON) - 1)
 
index 951369466b8ad6462c908a281923b49fb1cc3b65..d2c1c3c59e9123c2b7f3a1e42e77e5c22d69c312 100644 (file)
@@ -234,9 +234,10 @@ void    smtp_sasl_initialize(void)
      * Open the per-host password table and initialize the SASL library. Use
      * shared locks for reading, just in case someone updates the table.
      */
-    smtp_sasl_passwd_map = maps_create("smtp_sasl_passwd",
+    smtp_sasl_passwd_map = maps_create(VAR_LMTP_SMTP(SASL_PASSWD),
                                       var_smtp_sasl_passwd,
-                                      DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX);
+                                      DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX
+                                      | DICT_FLAG_UTF8_REQUEST);
     if ((smtp_sasl_impl = xsasl_client_init(var_smtp_sasl_type,
                                            var_smtp_sasl_path)) == 0)
        msg_fatal("SASL library initialization");
@@ -245,7 +246,8 @@ void    smtp_sasl_initialize(void)
      * Initialize optional supported mechanism matchlist
      */
     if (*var_smtp_sasl_mechs)
-       smtp_sasl_mechs = string_list_init(MATCH_FLAG_NONE,
+       smtp_sasl_mechs = string_list_init(VAR_SMTP_SASL_MECHS,
+                                          MATCH_FLAG_NONE,
                                           var_smtp_sasl_mechs);
 
     /*
@@ -258,7 +260,7 @@ void    smtp_sasl_initialize(void)
                                      var_smtp_sasl_auth_cache_time);
 #else
        msg_warn("not compiled with TLS support -- "
-                "ignoring the %s setting", VAR_LMTP_SMTP(SASL_AUTH_CACHE_NAME));
+           "ignoring the %s setting", VAR_LMTP_SMTP(SASL_AUTH_CACHE_NAME));
 #endif
     }
 }
index 07e3c4a6f76ae917b64cee2eb9b68e0fd8e46160..22b76ab492b0fb51a57e640e9ee1d3812046aee2 100644 (file)
@@ -132,7 +132,8 @@ void    smtp_tls_list_init(void)
     if (*var_smtp_tls_policy) {
        tls_policy = maps_create(VAR_LMTP_SMTP(TLS_POLICY),
                                 var_smtp_tls_policy,
-                                DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX);
+                                DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX
+                                | DICT_FLAG_UTF8_REQUEST);
        if (*var_smtp_tls_per_site)
            msg_warn("%s ignored when %s is not empty.",
                     VAR_LMTP_SMTP(TLS_PER_SITE), VAR_LMTP_SMTP(TLS_POLICY));
@@ -141,7 +142,8 @@ void    smtp_tls_list_init(void)
     if (*var_smtp_tls_per_site) {
        tls_per_site = maps_create(VAR_LMTP_SMTP(TLS_PER_SITE),
                                   var_smtp_tls_per_site,
-                                  DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX);
+                                  DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX
+                                  | DICT_FLAG_UTF8_REQUEST);
     }
 }
 
index 13c6e6f09ff2933e7435960f4b39852021222296..4aee827e2071c34e5e4740877923d968c2731a4f 100644 (file)
@@ -329,7 +329,7 @@ smtpd_check.o: ../../include/mail_stream.h
 smtpd_check.o: ../../include/maps.h
 smtpd_check.o: ../../include/match_list.h
 smtpd_check.o: ../../include/match_parent_style.h
-smtpd_check.o: ../../include/midna.h
+smtpd_check.o: ../../include/midna_domain.h
 smtpd_check.o: ../../include/milter.h
 smtpd_check.o: ../../include/msg.h
 smtpd_check.o: ../../include/msg_stats.h
index 35aae24b63bbac87399597f67d3c9160118c3042..566bc3b2fbc9e5db2b915896e6e4ff86a034fb25 100644 (file)
@@ -3653,7 +3653,8 @@ static int etrn_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
      * As an extension to RFC 1985 we also allow an RFC 2821 address literal
      * enclosed in [].
      * 
-     * XXX EAI: Convert to ASCII and use that form internally.
+     * XXX There does not appear to be an ETRN parameter to indicate that the
+     * domain name is UTF-8.
      */
     if (!valid_hostname(argv[1].strval, DONT_GRIPE)
        && !valid_mailhost_literal(argv[1].strval, DONT_GRIPE)) {
@@ -4948,6 +4949,14 @@ static void smtpd_proto(SMTPD_STATE *state)
            }
            watchdog_pat();
            smtpd_chat_query(state);
+           /* Safety: protect internal interfaces against malformed UTF-8. */
+           if (var_smtputf8_enable && valid_utf8_string(STR(state->buffer),
+                                                LEN(state->buffer)) == 0) {
+               state->error_mask |= MAIL_ERROR_PROTOCOL;
+               smtpd_chat_reply(state, "500 5.5.2 Error: bad UTF-8 syntax");
+               state->error_count++;
+               continue;
+           }
            /* Move into smtpd_chat_query() and update session transcript. */
            if (smtpd_cmd_filter != 0) {
                for (cp = STR(state->buffer); *cp && IS_SPACE_TAB(*cp); cp++)
@@ -5237,12 +5246,18 @@ static void pre_jail_init(char *unused_name, char **unused_argv)
      * Initialize blacklist/etc. patterns before entering the chroot jail, in
      * case they specify a filename pattern.
      */
-    smtpd_noop_cmds = string_list_init(MATCH_FLAG_RETURN, var_smtpd_noop_cmds);
-    smtpd_forbid_cmds = string_list_init(MATCH_FLAG_RETURN, var_smtpd_forbid_cmds);
-    verp_clients = namadr_list_init(MATCH_FLAG_RETURN, var_verp_clients);
-    xclient_hosts = namadr_list_init(MATCH_FLAG_RETURN, var_xclient_hosts);
-    xforward_hosts = namadr_list_init(MATCH_FLAG_RETURN, var_xforward_hosts);
-    hogger_list = namadr_list_init(MATCH_FLAG_RETURN
+    smtpd_noop_cmds = string_list_init(VAR_SMTPD_NOOP_CMDS, MATCH_FLAG_RETURN,
+                                      var_smtpd_noop_cmds);
+    smtpd_forbid_cmds = string_list_init(VAR_SMTPD_FORBID_CMDS,
+                                        MATCH_FLAG_RETURN,
+                                        var_smtpd_forbid_cmds);
+    verp_clients = namadr_list_init(VAR_VERP_CLIENTS, MATCH_FLAG_RETURN,
+                                   var_verp_clients);
+    xclient_hosts = namadr_list_init(VAR_XCLIENT_HOSTS, MATCH_FLAG_RETURN,
+                                    var_xclient_hosts);
+    xforward_hosts = namadr_list_init(VAR_XFORWARD_HOSTS, MATCH_FLAG_RETURN,
+                                     var_xforward_hosts);
+    hogger_list = namadr_list_init(VAR_SMTPD_HOGGERS, MATCH_FLAG_RETURN
                                   | match_parent_style(VAR_SMTPD_HOGGERS),
                                   var_smtpd_hoggers);
 
@@ -5267,7 +5282,8 @@ static void pre_jail_init(char *unused_name, char **unused_argv)
 
     if (*var_smtpd_sasl_exceptions_networks)
        sasl_exceptions_networks =
-           namadr_list_init(MATCH_FLAG_RETURN,
+           namadr_list_init(VAR_SMTPD_SASL_EXCEPTIONS_NETWORKS,
+                            MATCH_FLAG_RETURN,
                             var_smtpd_sasl_exceptions_networks);
 #else
        msg_warn("%s is true, but SASL support is not compiled in",
index fd114c0d1d54ca94d0802b083b85df4ddcf74fa9..5286ba86d61cbc55f120cf49af30a7515717627b 100644 (file)
 #include <inet_proto.h>
 #include <ip_match.h>
 #include <valid_utf8_hostname.h>
-#include <midna.h>
+#include <midna_domain.h>
 #include <mynetworks.h>
 
 /* DNS library. */
@@ -607,7 +607,8 @@ static ARGV *smtpd_check_parse(int flags, const char *checks)
        else if ((flags & SMTPD_CHECK_PARSE_MAPS)
                 && strchr(name, ':') && dict_handle(name) == 0) {
            dict_register(name, dict_open(name, O_RDONLY, DICT_FLAG_LOCK
-                                         | DICT_FLAG_FOLD_FIX));
+                                         | DICT_FLAG_FOLD_FIX
+                                         | DICT_FLAG_UTF8_REQUEST));
        }
        last = name;
     }
@@ -705,16 +706,17 @@ void    smtpd_check_init(void)
      * Pre-open access control lists before going to jail.
      */
     mynetworks_curr =
-       namadr_list_init(MATCH_FLAG_RETURN | match_parent_style(VAR_MYNETWORKS),
-                        var_mynetworks);
+       namadr_list_init(VAR_MYNETWORKS, MATCH_FLAG_RETURN
+                     | match_parent_style(VAR_MYNETWORKS), var_mynetworks);
     mynetworks_new =
-       namadr_list_init(MATCH_FLAG_RETURN | match_parent_style(VAR_MYNETWORKS),
-                        mynetworks_host());
+       namadr_list_init(VAR_MYNETWORKS, MATCH_FLAG_RETURN
+                  | match_parent_style(VAR_MYNETWORKS), mynetworks_host());
     relay_domains =
-       domain_list_init(match_parent_style(VAR_RELAY_DOMAINS),
+       domain_list_init(VAR_RELAY_DOMAINS,
+                        match_parent_style(VAR_RELAY_DOMAINS),
                         var_relay_domains);
     perm_mx_networks =
-       namadr_list_init(MATCH_FLAG_RETURN
+       namadr_list_init(VAR_PERM_MX_NETWORKS, MATCH_FLAG_RETURN
                         | match_parent_style(VAR_PERM_MX_NETWORKS),
                         var_perm_mx_networks);
 #ifdef USE_TLS
@@ -726,22 +728,30 @@ void    smtpd_check_init(void)
      * Pre-parse and pre-open the recipient maps.
      */
     local_rcpt_maps = maps_create(VAR_LOCAL_RCPT_MAPS, var_local_rcpt_maps,
-                                 DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX);
+                                 DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX
+                                 | DICT_FLAG_UTF8_REQUEST);
     rcpt_canon_maps = maps_create(VAR_RCPT_CANON_MAPS, var_rcpt_canon_maps,
-                                 DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX);
+                                 DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX
+                                 | DICT_FLAG_UTF8_REQUEST);
     canonical_maps = maps_create(VAR_CANONICAL_MAPS, var_canonical_maps,
-                                DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX);
+                                DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX
+                                | DICT_FLAG_UTF8_REQUEST);
     virt_alias_maps = maps_create(VAR_VIRT_ALIAS_MAPS, var_virt_alias_maps,
-                                 DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX);
+                                 DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX
+                                 | DICT_FLAG_UTF8_REQUEST);
     virt_mailbox_maps = maps_create(VAR_VIRT_MAILBOX_MAPS,
                                    var_virt_mailbox_maps,
-                                   DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX);
+                                   DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX
+                                   | DICT_FLAG_UTF8_REQUEST);
     relay_rcpt_maps = maps_create(VAR_RELAY_RCPT_MAPS, var_relay_rcpt_maps,
-                                 DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX);
+                                 DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX
+                                 | DICT_FLAG_UTF8_REQUEST);
 
 #ifdef TEST
-    virt_alias_doms = string_list_init(MATCH_FLAG_NONE, var_virt_alias_doms);
-    virt_mailbox_doms = string_list_init(MATCH_FLAG_NONE, var_virt_mailbox_doms);
+    virt_alias_doms = string_list_init(VAR_VIRT_ALIAS_DOMS, MATCH_FLAG_NONE,
+                                      var_virt_alias_doms);
+    virt_mailbox_doms = string_list_init(VAR_VIRT_MAILBOX_DOMS, MATCH_FLAG_NONE,
+                                        var_virt_mailbox_doms);
 #endif
 
     access_parent_style = match_parent_style(SMTPD_ACCESS_MAPS);
@@ -750,14 +760,16 @@ void    smtpd_check_init(void)
      * Templates for RBL rejection replies.
      */
     rbl_reply_maps = maps_create(VAR_RBL_REPLY_MAPS, var_rbl_reply_maps,
-                                DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX);
+                                DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX
+                                | DICT_FLAG_UTF8_REQUEST);
 
     /*
      * Sender to login name mapping.
      */
     smtpd_sender_login_maps = maps_create(VAR_SMTPD_SND_AUTH_MAPS,
                                          var_smtpd_snd_auth_maps,
-                                      DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX);
+                                         DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX
+                                         | DICT_FLAG_UTF8_REQUEST);
 
     /*
      * error_text is used for returning error responses.
@@ -880,7 +892,8 @@ void    smtpd_check_init(void)
     /*
      * Optional permit logging.
      */
-    smtpd_acl_perm_log = string_list_init(MATCH_FLAG_RETURN,
+    smtpd_acl_perm_log = string_list_init(VAR_SMTPD_ACL_PERM_LOG,
+                                         MATCH_FLAG_RETURN,
                                          var_smtpd_acl_perm_log);
 }
 
@@ -1117,11 +1130,29 @@ static const char *check_mail_addr_find(SMTPD_STATE *state,
     if ((result = mail_addr_find(maps, key, ext)) != 0 || maps->error == 0)
        return (result);
     if (maps->error == DICT_ERR_RETRY)
+       /* Warning is already logged. */
        reject_dict_retry(state, reply_name);
     else
        reject_server_error(state);
 }
 
+/* check_dict_get - reject with temporary failure if dict lookup fails */
+
+static const char *check_dict_get(SMTPD_STATE *state, const char *table,
+                                         const char *reply_name,
+                                         DICT *dict, const char *key)
+{
+    const char *result;
+
+    if ((result = dict_get(dict, key)) != 0 || dict->error == 0)
+       return (result);
+    if (dict->error == DICT_ERR_RETRY) {
+       msg_warn("%s: table lookup problem", table);
+       reject_dict_retry(state, reply_name);
+    } else
+       reject_server_error(state);
+}
+
 /* reject_unknown_reverse_name - fail if reverse client hostname is unknown */
 
 static int reject_unknown_reverse_name(SMTPD_STATE *state)
@@ -1417,7 +1448,7 @@ static int reject_unknown_mailhost(SMTPD_STATE *state, const char *name,
      * Fix 20140924: convert domain to ASCII.
      */
 #ifndef NO_EAI
-    if (!allascii(name) && (aname = midna_to_ascii(name)) != 0) {
+    if (!allascii(name) && (aname = midna_domain_to_ascii(name)) != 0) {
        if (msg_verbose)
            msg_info("%s asciified to %s", name, aname);
        name = aname;
@@ -1916,7 +1947,7 @@ static int permit_mx_backup(SMTPD_STATE *state, const char *recipient,
      * Fix 20140924: convert domain to ASCII.
      */
 #ifndef NO_EAI
-    if (!allascii(domain) && (adomain = midna_to_ascii(domain)) != 0) {
+    if (!allascii(domain) && (adomain = midna_domain_to_ascii(domain)) != 0) {
        if (msg_verbose)
            msg_info("%s asciified to %s", domain, adomain);
        domain = adomain;
@@ -2661,23 +2692,13 @@ static int check_access(SMTPD_STATE *state, const char *table, const char *name,
 
     if ((dict = dict_handle(table)) == 0) {
        msg_warn("%s: unexpected dictionary: %s", myname, table);
-       value = "451 4.3.5 Server configuration error";
-       CHK_ACCESS_RETURN(check_table_result(state, table, value, name,
-                                            reply_name, reply_class,
-                                            def_acl), FOUND);
+       reject_server_error(state);
     }
     if (flags == 0 || (flags & dict->flags) != 0) {
-       if ((value = dict_get(dict, name)) != 0)
-           CHK_ACCESS_RETURN(check_table_result(state, table, value, name,
-                                                reply_name, reply_class,
-                                                def_acl), FOUND);
-       if (dict->error != 0) {
-           msg_warn("%s: table lookup problem", table);
-           value = "451 4.3.5 Server configuration error";
+       if ((value = check_dict_get(state, table, reply_name, dict, name)) != 0)
            CHK_ACCESS_RETURN(check_table_result(state, table, value, name,
                                                 reply_name, reply_class,
                                                 def_acl), FOUND);
-       }
     }
     CHK_ACCESS_RETURN(SMTPD_CHECK_DUNNO, MISSED);
 }
@@ -2711,24 +2732,15 @@ static int check_domain_access(SMTPD_STATE *state, const char *table,
 
     if ((dict = dict_handle(table)) == 0) {
        msg_warn("%s: unexpected dictionary: %s", myname, table);
-       value = "451 4.3.5 Server configuration error";
-       CHK_DOMAIN_RETURN(check_table_result(state, table, value,
-                                            domain, reply_name, reply_class,
-                                            def_acl), FOUND);
+       reject_server_error(state);
     }
     for (name = domain; *name != 0; name = next) {
        if (flags == 0 || (flags & dict->flags) != 0) {
-           if ((value = dict_get(dict, name)) != 0)
-               CHK_DOMAIN_RETURN(check_table_result(state, table, value,
-                                           domain, reply_name, reply_class,
-                                                    def_acl), FOUND);
-           if (dict->error != 0) {
-               msg_warn("%s: table lookup problem", table);
-               value = "451 4.3.5 Server configuration error";
+           if ((value = check_dict_get(state, table, reply_name,
+                                       dict, name)) != 0)
                CHK_DOMAIN_RETURN(check_table_result(state, table, value,
                                            domain, reply_name, reply_class,
                                                     def_acl), FOUND);
-           }
        }
        /* Don't apply subdomain magic to numerical hostnames. */
        if (maybe_numerical
@@ -2775,24 +2787,15 @@ static int check_addr_access(SMTPD_STATE *state, const char *table,
 
     if ((dict = dict_handle(table)) == 0) {
        msg_warn("%s: unexpected dictionary: %s", myname, table);
-       value = "451 4.3.5 Server configuration error";
-       CHK_ADDR_RETURN(check_table_result(state, table, value, address,
-                                          reply_name, reply_class,
-                                          def_acl), FOUND);
+       reject_server_error(state);
     }
     do {
        if (flags == 0 || (flags & dict->flags) != 0) {
-           if ((value = dict_get(dict, addr)) != 0)
+           if ((value = check_dict_get(state, table, reply_name,
+                                       dict, addr)) != 0)
                CHK_ADDR_RETURN(check_table_result(state, table, value, address,
                                                   reply_name, reply_class,
                                                   def_acl), FOUND);
-           if (dict->error != 0) {
-               msg_warn("%s: table lookup problem", table);
-               value = "451 4.3.5 Server configuration error";
-               CHK_ADDR_RETURN(check_table_result(state, table, value, address,
-                                                  reply_name, reply_class,
-                                                  def_acl), FOUND);
-           }
        }
        flags = PARTIAL;
     } while (split_at_right(addr, delim));
@@ -2914,7 +2917,7 @@ static int check_server_access(SMTPD_STATE *state, const char *table,
      * Fix 20140924: convert domain to ASCII.
      */
 #ifndef NO_EAI
-    if (!allascii(domain) && (adomain = midna_to_ascii(domain)) != 0) {
+    if (!allascii(domain) && (adomain = midna_domain_to_ascii(domain)) != 0) {
        if (msg_verbose)
            msg_info("%s asciified to %s", domain, adomain);
        domain = adomain;
@@ -3634,7 +3637,7 @@ static const SMTPD_RBL_STATE *find_dnsxl_domain(SMTPD_STATE *state,
      * Fix 20140706: convert domain to ASCII.
      */
 #ifndef NO_EAI
-    if (!allascii(domain) && (adomain = midna_to_ascii(domain)) != 0) {
+    if (!allascii(domain) && (adomain = midna_domain_to_ascii(domain)) != 0) {
        if (msg_verbose)
            msg_info("%s asciified to %s", domain, adomain);
        domain = adomain;
@@ -3816,6 +3819,18 @@ static int reject_unauth_sender_login_mismatch(SMTPD_STATE *state, const char *s
 
 #endif
 
+/* valid_utf8_action - validate UTF-8 policy server response */
+
+static int valid_utf8_action(const char *server, const char *action)
+{
+    int     retval;
+
+    if ((retval = valid_utf8_string(action, strlen(action))) == 0)
+       msg_warn("malformed UTF-8 in policy server %s response: \"%s\"",
+                server, action);
+    return (retval);
+}
+
 /* check_policy_service - check delegated policy service */
 
 static int check_policy_service(SMTPD_STATE *state, const char *server,
@@ -3926,7 +3941,8 @@ static int check_policy_service(SMTPD_STATE *state, const char *server,
                          ATTR_TYPE_END,
                          ATTR_FLAG_MISSING,    /* Reply attributes. */
                          RECV_ATTR_STR(MAIL_ATTR_ACTION, action),
-                         ATTR_TYPE_END) != 1) {
+                         ATTR_TYPE_END) != 1
+       || (var_smtputf8_enable && valid_utf8_action(server, STR(action)) == 0)) {
        NOCLOBBER static int nesting_level = 0;
        jmp_buf savebuf;
        int     status;
@@ -5963,9 +5979,9 @@ int     main(int argc, char **argv)
 #define UPDATE_MAPS(ptr, var, val, lock) \
        { if (ptr) maps_free(ptr); ptr = maps_create(var, val, lock); }
 
-#define UPDATE_LIST(ptr, val) \
+#define UPDATE_LIST(ptr, var, val) \
        { if (ptr) string_list_free(ptr); \
-         ptr = string_list_init(MATCH_FLAG_NONE, val); }
+         ptr = string_list_init(var, MATCH_FLAG_NONE, val); }
 
        case 2:
            if (strcasecmp(args->argv[0], VAR_MYDEST) == 0) {
@@ -5979,13 +5995,14 @@ int     main(int argc, char **argv)
                UPDATE_STRING(var_virt_alias_maps, args->argv[1]);
                UPDATE_MAPS(virt_alias_maps, VAR_VIRT_ALIAS_MAPS,
                            var_virt_alias_maps, DICT_FLAG_LOCK
-                           | DICT_FLAG_FOLD_FIX);
+                           | DICT_FLAG_FOLD_FIX | DICT_FLAG_UTF8_REQUEST);
                resp = 0;
                break;
            }
            if (strcasecmp(args->argv[0], VAR_VIRT_ALIAS_DOMS) == 0) {
                UPDATE_STRING(var_virt_alias_doms, args->argv[1]);
-               UPDATE_LIST(virt_alias_doms, var_virt_alias_doms);
+               UPDATE_LIST(virt_alias_doms, VAR_VIRT_ALIAS_DOMS,
+                           var_virt_alias_doms);
                smtpd_resolve_init(100);
                resp = 0;
                break;
@@ -5994,13 +6011,14 @@ int     main(int argc, char **argv)
                UPDATE_STRING(var_virt_mailbox_maps, args->argv[1]);
                UPDATE_MAPS(virt_mailbox_maps, VAR_VIRT_MAILBOX_MAPS,
                            var_virt_mailbox_maps, DICT_FLAG_LOCK
-                           | DICT_FLAG_FOLD_FIX);
+                           | DICT_FLAG_FOLD_FIX | DICT_FLAG_UTF8_REQUEST);
                resp = 0;
                break;
            }
            if (strcasecmp(args->argv[0], VAR_VIRT_MAILBOX_DOMS) == 0) {
                UPDATE_STRING(var_virt_mailbox_doms, args->argv[1]);
-               UPDATE_LIST(virt_mailbox_doms, var_virt_mailbox_doms);
+               UPDATE_LIST(virt_mailbox_doms, VAR_VIRT_MAILBOX_DOMS,
+                           var_virt_mailbox_doms);
                smtpd_resolve_init(100);
                resp = 0;
                break;
@@ -6009,7 +6027,7 @@ int     main(int argc, char **argv)
                UPDATE_STRING(var_local_rcpt_maps, args->argv[1]);
                UPDATE_MAPS(local_rcpt_maps, VAR_LOCAL_RCPT_MAPS,
                            var_local_rcpt_maps, DICT_FLAG_LOCK
-                           | DICT_FLAG_FOLD_FIX);
+                           | DICT_FLAG_FOLD_FIX | DICT_FLAG_UTF8_REQUEST);
                resp = 0;
                break;
            }
@@ -6017,7 +6035,7 @@ int     main(int argc, char **argv)
                UPDATE_STRING(var_relay_rcpt_maps, args->argv[1]);
                UPDATE_MAPS(relay_rcpt_maps, VAR_RELAY_RCPT_MAPS,
                            var_relay_rcpt_maps, DICT_FLAG_LOCK
-                           | DICT_FLAG_FOLD_FIX);
+                           | DICT_FLAG_FOLD_FIX | DICT_FLAG_UTF8_REQUEST);
                resp = 0;
                break;
            }
@@ -6025,7 +6043,7 @@ int     main(int argc, char **argv)
                UPDATE_STRING(var_canonical_maps, args->argv[1]);
                UPDATE_MAPS(canonical_maps, VAR_CANONICAL_MAPS,
                            var_canonical_maps, DICT_FLAG_LOCK
-                           | DICT_FLAG_FOLD_FIX);
+                           | DICT_FLAG_FOLD_FIX | DICT_FLAG_UTF8_REQUEST);
                resp = 0;
                break;
            }
@@ -6033,7 +6051,7 @@ int     main(int argc, char **argv)
                UPDATE_STRING(var_rbl_reply_maps, args->argv[1]);
                UPDATE_MAPS(rbl_reply_maps, VAR_RBL_REPLY_MAPS,
                            var_rbl_reply_maps, DICT_FLAG_LOCK
-                           | DICT_FLAG_FOLD_FIX);
+                           | DICT_FLAG_FOLD_FIX | DICT_FLAG_UTF8_REQUEST);
                resp = 0;
                break;
            }
@@ -6041,7 +6059,7 @@ int     main(int argc, char **argv)
                /* NOT: UPDATE_STRING */
                namadr_list_free(mynetworks_curr);
                mynetworks_curr =
-                   namadr_list_init(MATCH_FLAG_RETURN
+                   namadr_list_init(VAR_MYNETWORKS, MATCH_FLAG_RETURN
                                     | match_parent_style(VAR_MYNETWORKS),
                                     args->argv[1]);
                smtpd_resolve_init(100);
@@ -6052,7 +6070,8 @@ int     main(int argc, char **argv)
                /* NOT: UPDATE_STRING */
                domain_list_free(relay_domains);
                relay_domains =
-                   domain_list_init(match_parent_style(VAR_RELAY_DOMAINS),
+                   domain_list_init(VAR_RELAY_DOMAINS,
+                                    match_parent_style(VAR_RELAY_DOMAINS),
                                     args->argv[1]);
                smtpd_resolve_init(100);
                resp = 0;
@@ -6062,7 +6081,7 @@ int     main(int argc, char **argv)
                UPDATE_STRING(var_perm_mx_networks, args->argv[1]);
                domain_list_free(perm_mx_networks);
                perm_mx_networks =
-                   namadr_list_init(MATCH_FLAG_RETURN
+                   namadr_list_init(VAR_PERM_MX_NETWORKS, MATCH_FLAG_RETURN
                                 | match_parent_style(VAR_PERM_MX_NETWORKS),
                                     args->argv[1]);
                resp = 0;
index 44675cb2ef7a97dc0f86d405d8832217abba7c42..0f2e2fac3024c269f0c3e8382147fa160dcf71bd 100644 (file)
@@ -11,8 +11,8 @@ OK
 >>> # Expect: REJECT (temporary lookup failure)
 >>> helo foobar
 ./smtpd_check: warning: fail:1_helo_access: table lookup problem
-./smtpd_check: <queue id>: reject: HELO from localhost[127.0.0.1]: 451 4.3.5 <foobar>: Helo command rejected: Server configuration error; proto=SMTP helo=<foobar>
-451 4.3.5 <foobar>: Helo command rejected: Server configuration error
+./smtpd_check: <queue id>: reject: HELO from localhost[127.0.0.1]: 451 4.3.0 <foobar>: Temporary lookup failure; proto=SMTP helo=<foobar>
+451 4.3.0 <foobar>: Temporary lookup failure
 >>> #
 >>> # Test check_namadr_access()
 >>> #
@@ -21,8 +21,8 @@ OK
 >>> # Expect: REJECT (temporary lookup failure)
 >>> client foo.dunno.com 131.155.210.17
 ./smtpd_check: warning: fail:1_client_access: table lookup problem
-./smtpd_check: <queue id>: reject: CONNECT from foo.dunno.com[131.155.210.17]: 451 4.3.5 <foo.dunno.com[131.155.210.17]>: Client host rejected: Server configuration error; proto=SMTP helo=<foobar>
-451 4.3.5 <foo.dunno.com[131.155.210.17]>: Client host rejected: Server configuration error
+./smtpd_check: <queue id>: reject: CONNECT from foo.dunno.com[131.155.210.17]: 451 4.3.0 <foo.dunno.com[131.155.210.17]>: Temporary lookup failure; proto=SMTP helo=<foobar>
+451 4.3.0 <foo.dunno.com[131.155.210.17]>: Temporary lookup failure
 >>> #
 >>> # Test check_mail_access()
 >>> #
@@ -31,8 +31,8 @@ OK
 >>> # Expect: REJECT (temporary lookup failure)
 >>> mail reject@dunno.domain
 ./smtpd_check: warning: fail:1_sender_access: table lookup problem
-./smtpd_check: <queue id>: reject: MAIL from foo.dunno.com[131.155.210.17]: 451 4.3.5 <reject@dunno.domain>: Sender address rejected: Server configuration error; from=<reject@dunno.domain> proto=SMTP helo=<foobar>
-451 4.3.5 <reject@dunno.domain>: Sender address rejected: Server configuration error
+./smtpd_check: <queue id>: reject: MAIL from foo.dunno.com[131.155.210.17]: 451 4.3.0 <reject@dunno.domain>: Temporary lookup failure; from=<reject@dunno.domain> proto=SMTP helo=<foobar>
+451 4.3.0 <reject@dunno.domain>: Temporary lookup failure
 >>> #
 >>> # Test check_rcpt_access()
 >>> #
@@ -41,8 +41,8 @@ OK
 >>> # Expect: REJECT (temporary lookup failure)
 >>> rcpt reject@dunno.domain
 ./smtpd_check: warning: fail:1_rcpt_access: table lookup problem
-./smtpd_check: <queue id>: reject: RCPT from foo.dunno.com[131.155.210.17]: 451 4.3.5 <reject@dunno.domain>: Recipient address rejected: Server configuration error; from=<reject@dunno.domain> to=<reject@dunno.domain> proto=SMTP helo=<foobar>
-451 4.3.5 <reject@dunno.domain>: Recipient address rejected: Server configuration error
+./smtpd_check: <queue id>: reject: RCPT from foo.dunno.com[131.155.210.17]: 451 4.3.0 <reject@dunno.domain>: Temporary lookup failure; from=<reject@dunno.domain> to=<reject@dunno.domain> proto=SMTP helo=<foobar>
+451 4.3.0 <reject@dunno.domain>: Temporary lookup failure
 >>> # Expect: OK
 >>> rcpt postmaster
 OK
@@ -57,7 +57,7 @@ OK
 >>> recipient_restrictions permit_mynetworks
 OK
 >>> rcpt reject@dunno.domain
-./smtpd_check: warning: fail:1_mynetworks: table lookup problem
+./smtpd_check: warning: mynetworks: fail:1_mynetworks: table lookup problem
 ./smtpd_check: <queue id>: reject: RCPT from foo.dunno.com[131.155.210.17]: 451 4.3.0 <reject@dunno.domain>: Temporary lookup failure; from=<reject@dunno.domain> to=<reject@dunno.domain> proto=SMTP helo=<foobar>
 451 4.3.0 <reject@dunno.domain>: Temporary lookup failure
 >>> # 
@@ -69,7 +69,7 @@ OK
 >>> # Expect REJECT (server configuration error)
 >>> #
 >>> rcpt reject@dunno.domain
-./smtpd_check: warning: non-null host address bits in "168.100.189.1/27", perhaps you should use "168.100.189.0/27" instead
+./smtpd_check: warning: mynetworks: non-null host address bits in "168.100.189.1/27", perhaps you should use "168.100.189.0/27" instead
 ./smtpd_check: <queue id>: reject: RCPT from foo.dunno.com[131.155.210.17]: 451 4.3.0 <reject@dunno.domain>: Temporary lookup failure; from=<reject@dunno.domain> to=<reject@dunno.domain> proto=SMTP helo=<foobar>
 451 4.3.0 <reject@dunno.domain>: Temporary lookup failure
 >>> #
@@ -79,8 +79,8 @@ OK
 OK
 >>> mail <>
 ./smtpd_check: warning: fail:1_sender_access: table lookup problem
-./smtpd_check: <queue id>: reject: MAIL from foo.dunno.com[131.155.210.17]: 451 4.3.5 <>: Sender address rejected: Server configuration error; from=<> proto=SMTP helo=<foobar>
-451 4.3.5 <>: Sender address rejected: Server configuration error
+./smtpd_check: <queue id>: reject: MAIL from foo.dunno.com[131.155.210.17]: 451 4.3.0 <>: Temporary lookup failure; from=<> proto=SMTP helo=<foobar>
+451 4.3.0 <>: Temporary lookup failure
 >>> #
 >>> # Test permit_tls_client_certs in generic_restrictions
 >>> #
@@ -119,7 +119,7 @@ OK
 >>> mydestination fail:1_mydestination
 OK
 >>> rcpt user@example.com
-./smtpd_check: warning: fail:1_mydestination: table lookup problem
+./smtpd_check: warning: mydestination: fail:1_mydestination: table lookup problem
 ./smtpd_check: <queue id>: reject: RCPT from foo.dunno.com[131.155.210.17]: 451 4.3.0 <user@example.com>: Temporary lookup failure; from=<> to=<user@example.com> proto=SMTP helo=<foobar>
 451 4.3.0 <user@example.com>: Temporary lookup failure
 >>> #
index cddb4915b76f3f81d3e9844dc3ece2be2e67965b..1a4896053413eaa4a54dab789158686c7866a699 100644 (file)
@@ -140,7 +140,7 @@ tls_client.o: ../../include/dict.h
 tls_client.o: ../../include/dns.h
 tls_client.o: ../../include/iostuff.h
 tls_client.o: ../../include/mail_params.h
-tls_client.o: ../../include/midna.h
+tls_client.o: ../../include/midna_domain.h
 tls_client.o: ../../include/msg.h
 tls_client.o: ../../include/myaddrinfo.h
 tls_client.o: ../../include/myflock.h
index 664faec46e275d9be8e9a42cea1074d81c993ced..9e5f3dc8bf2a8de839b972cd802022f4fb0e05e2 100644 (file)
@@ -296,23 +296,61 @@ extern void tls_param_init(void);
   * Protocol selection.
   */
 #define TLS_PROTOCOL_INVALID   (~0)    /* All protocol bits masked */
+
+#ifdef SSL_TXT_SSLV2
 #define TLS_PROTOCOL_SSLv2     (1<<0)  /* SSLv2 */
+#else
+#define SSL_TXT_SSLV2          "SSLv2"
+#define TLS_PROTOCOL_SSLv2     0       /* Unknown */
+#undef  SSL_OP_NO_SSLv2
+#define SSL_OP_NO_SSLv2                0L      /* Noop */
+#endif
+
+#ifdef SSL_TXT_SSLV3
 #define TLS_PROTOCOL_SSLv3     (1<<1)  /* SSLv3 */
+#else
+#define SSL_TXT_SSLV3          "SSLv3"
+#define TLS_PROTOCOL_SSLv3     0       /* Unknown */
+#undef  SSL_OP_NO_SSLv3
+#define SSL_OP_NO_SSLv3                0L      /* Noop */
+#endif
+
+#ifdef SSL_TXT_TLSV1
 #define TLS_PROTOCOL_TLSv1     (1<<2)  /* TLSv1 */
+#else
+#define SSL_TXT_TLSV1          "TLSv1"
+#define TLS_PROTOCOL_TLSv1     0       /* Unknown */
+#undef  SSL_OP_NO_TLSv1
+#define SSL_OP_NO_TLSv1                0L      /* Noop */
+#endif
+
 #ifdef SSL_TXT_TLSV1_1
 #define TLS_PROTOCOL_TLSv1_1   (1<<3)  /* TLSv1_1 */
 #else
+#define SSL_TXT_TLSV1_1                "TLSv1.1"
 #define TLS_PROTOCOL_TLSv1_1   0       /* Unknown */
 #undef  SSL_OP_NO_TLSv1_1
 #define SSL_OP_NO_TLSv1_1      0L      /* Noop */
 #endif
+
 #ifdef SSL_TXT_TLSV1_2
 #define TLS_PROTOCOL_TLSv1_2   (1<<4)  /* TLSv1_2 */
 #else
+#define SSL_TXT_TLSV1_2                "TLSv1.2"
 #define TLS_PROTOCOL_TLSv1_2   0       /* Unknown */
 #undef  SSL_OP_NO_TLSv1_2
 #define SSL_OP_NO_TLSv1_2      0L      /* Noop */
 #endif
+
+#ifdef SSL_TXT_TLSV1_3
+#define TLS_PROTOCOL_TLSv1_3   (1<<5)  /* TLSv1_3 */
+#else
+#define SSL_TXT_TLSV1_3                "TLSv1.3"
+#define TLS_PROTOCOL_TLSv1_3   0       /* Unknown */
+#undef  SSL_OP_NO_TLSv1_3
+#define SSL_OP_NO_TLSv1_3      0L      /* Noop */
+#endif
+
 #define TLS_KNOWN_PROTOCOLS \
        ( TLS_PROTOCOL_SSLv2 | TLS_PROTOCOL_SSLv3 | TLS_PROTOCOL_TLSv1 \
           | TLS_PROTOCOL_TLSv1_1 | TLS_PROTOCOL_TLSv1_2 )
@@ -321,7 +359,8 @@ extern void tls_param_init(void);
             | (((m) & TLS_PROTOCOL_SSLv3) ? SSL_OP_NO_SSLv3 : 0L) \
             | (((m) & TLS_PROTOCOL_TLSv1) ? SSL_OP_NO_TLSv1 : 0L) \
             | (((m) & TLS_PROTOCOL_TLSv1_1) ? SSL_OP_NO_TLSv1_1 : 0L) \
-            | (((m) & TLS_PROTOCOL_TLSv1_2) ? SSL_OP_NO_TLSv1_2 : 0L))
+            | (((m) & TLS_PROTOCOL_TLSv1_2) ? SSL_OP_NO_TLSv1_2 : 0L) \
+            | (((m) & TLS_PROTOCOL_TLSv1_3) ? SSL_OP_NO_TLSv1_3 : 0L))
 
 /*
  * SSL options that are managed via dedicated Postfix features, rather than
index f30f1e3ae42914794e4ea8c7c7713e45f5f500ba..8211e2602e837c03db635ecbef002d3b2103008e 100644 (file)
 #include <stringops.h>
 #include <msg.h>
 #include <iostuff.h>                   /* non-blocking */
-#include <midna.h>
+#include <midna_domain.h>
 
 /* Global library. */
 
@@ -535,7 +535,7 @@ static int match_servername(const char *certid,
      */
     if (!allascii(certid))
        return (0);
-    if (!allascii(nexthop) && (aname = midna_to_ascii(nexthop)) != 0) {
+    if (!allascii(nexthop) && (aname = midna_domain_to_ascii(nexthop)) != 0) {
        if (msg_verbose)
            msg_info("%s asciified to %s", nexthop, aname);
        nexthop = aname;
@@ -565,13 +565,19 @@ static int match_servername(const char *certid,
 #ifndef NO_EAI
 
            /*
-            * IDNA allows labels to be separated by any of the additional
-            * characters U+3002, U+FF0E, and U+FF61; that are Unicode
-            * variants. Their UTF-8 encodings are: E38082, EFBC8E and
-            * EFBDA1.
+            * Besides U+002E (full stop) IDNA2003 allows labels to be
+            * separated by any of the Unicode variants U+3002 (ideographic
+            * full stop), U+FF0E (fullwidth full stop), and U+FF61
+            * (halfwidth ideographic full stop). Their respective UTF-8
+            * encodings are: E38082, EFBC8E and EFBDA1.
             * 
-            * It is not clear whether the IDNA to_ASCII conversion allows empty
-            * leading labels, so we handle these explicitly here.
+            * IDNA2008 does not permit (upper) case and other variant
+            * differences in U-labels. The midna_domain_to_ascii() function,
+            * based on UTS46, midna_domain_to_ascii() normalizes the
+            * differences away.
+            * 
+            * The IDNA to_ASCII conversion does not allow empty leading labels,
+            * so we handle these explicitly here.
             */
            else {
                unsigned char *cp = (unsigned char *) domain;
@@ -586,7 +592,7 @@ static int match_servername(const char *certid,
                }
            }
            if (!allascii(domain)
-               && (aname = midna_to_ascii(domain)) != 0) {
+               && (aname = midna_domain_to_ascii(domain)) != 0) {
                if (msg_verbose)
                    msg_info("%s asciified to %s", domain, aname);
                domain = aname;
index da17be73a229aa2aebfc9759ef4685d0e158ce8e..be126c9a1ecd22249939915fdc2f5493ab436f2c 100644 (file)
@@ -82,6 +82,7 @@
 
 #define TLS_INTERNAL
 #include <tls.h>
+#include <openssl/dh.h>
 
 /* Application-specific. */
 
index 1a3590f286a153ced212778edf13dc818d9ea0aa..3497014edff7d4333be81879041ea42941e7bb67 100644 (file)
@@ -252,12 +252,9 @@ static const NAME_CODE protocol_table[] = {
     SSL_TXT_SSLV2, TLS_PROTOCOL_SSLv2,
     SSL_TXT_SSLV3, TLS_PROTOCOL_SSLv3,
     SSL_TXT_TLSV1, TLS_PROTOCOL_TLSv1,
-#ifdef SSL_TXT_TLSV1_1
     SSL_TXT_TLSV1_1, TLS_PROTOCOL_TLSv1_1,
-#endif
-#ifdef SSL_TXT_TLSV1_2
     SSL_TXT_TLSV1_2, TLS_PROTOCOL_TLSv1_2,
-#endif
+    SSL_TXT_TLSV1_3, TLS_PROTOCOL_TLSv1_3,
     0, TLS_PROTOCOL_INVALID,
 };
 
index 1dfe266b38c30f4c34a366a289148a091f60206e..aba4142d2c3a514faac6b874057ce94b6d0a57ff 100644 (file)
@@ -52,6 +52,7 @@
 
 #define TLS_INTERNAL
 #include <tls.h>
+#include <openssl/rsa.h>
 
 /* tls_tmp_rsa_cb - call-back to generate ephemeral RSA key */
 
index 1c1a29e5fb93dba2f695245dc47fd1c3351b59ca..ca86cc2580ed78cccdc5e84405e25d862a528d88 100644 (file)
@@ -481,10 +481,12 @@ TLS_SCACHE *tls_scache_open(const char *dbname, const char *cache_label,
      * opening a damaged file after some process terminated abnormally.
      */
 #ifdef SINGLE_UPDATER
-#define DICT_FLAGS (DICT_FLAG_DUP_REPLACE | DICT_FLAG_OPEN_LOCK)
+#define DICT_FLAGS (DICT_FLAG_DUP_REPLACE | DICT_FLAG_OPEN_LOCK \
+                   | DICT_FLAG_UTF8_REQUEST)
 #else
 #define DICT_FLAGS \
-       (DICT_FLAG_DUP_REPLACE | DICT_FLAG_LOCK | DICT_FLAG_SYNC_UPDATE)
+       (DICT_FLAG_DUP_REPLACE | DICT_FLAG_LOCK | DICT_FLAG_SYNC_UPDATE \
+        | DICT_FLAG_UTF8_REQUEST)
 #endif
 
     dict = dict_open(dbname, O_RDWR | O_CREAT | O_TRUNC, DICT_FLAGS);
index aa943a018989e0f506b2dba3d09ae65034baa072..b74c32736cee93f5b8f28059591e68bea0a523e2 100644 (file)
@@ -498,12 +498,7 @@ TLS_APPL_STATE *tls_server_init(const TLS_SERVER_INIT_PROPS *props)
      * Global protocol selection.
      */
     if (protomask != 0)
-       SSL_CTX_set_options(server_ctx,
-                  ((protomask & TLS_PROTOCOL_TLSv1) ? SSL_OP_NO_TLSv1 : 0L)
-            | ((protomask & TLS_PROTOCOL_TLSv1_1) ? SSL_OP_NO_TLSv1_1 : 0L)
-            | ((protomask & TLS_PROTOCOL_TLSv1_2) ? SSL_OP_NO_TLSv1_2 : 0L)
-                | ((protomask & TLS_PROTOCOL_SSLv3) ? SSL_OP_NO_SSLv3 : 0L)
-              | ((protomask & TLS_PROTOCOL_SSLv2) ? SSL_OP_NO_SSLv2 : 0L));
+       SSL_CTX_set_options(server_ctx, TLS_SSL_OP_PROTOMASK(protomask));
 
     /*
      * Some sites may want to give the client less rope. On the other hand,
index 8ffb196efcd6e7241fc27c14af5a22890ae4ff30..f7c5a6a6370487a6ff484ea5dc50165ebe6e6947 100644 (file)
   */
 
 #define STR    vstring_str
+#define LEN    VSTRING_LEN
 
  /*
   * Some of the lists that define the address domain classes.
@@ -414,11 +415,14 @@ static void resolve_addr(RES_CONTEXT *rp, char *sender, char *addr,
      */
     tok822_internalize(nextrcpt, tree, TOK822_STR_DEFL);
     rcpt_domain = strrchr(STR(nextrcpt), '@') + 1;
-    if (rcpt_domain == 0)
+    if (rcpt_domain == (char *) 1)
        msg_panic("no @ in address: \"%s\"", STR(nextrcpt));
     if (*rcpt_domain == '[') {
        if (!valid_mailhost_literal(rcpt_domain, DONT_GRIPE))
            *flags |= RESOLVE_FLAG_ERROR;
+    } else if (var_smtputf8_enable
+              && valid_utf8_string(STR(nextrcpt), LEN(nextrcpt)) == 0) {
+       *flags |= RESOLVE_FLAG_ERROR;
     } else if (!valid_utf8_hostname(var_smtputf8_enable, rcpt_domain,
                                    DONT_GRIPE)) {
        if (var_resolve_num_dom && valid_hostaddr(rcpt_domain, DONT_GRIPE)) {
@@ -791,20 +795,23 @@ void    resolve_init(void)
 
     if (*var_virt_alias_doms)
        virt_alias_doms =
-           string_list_init(MATCH_FLAG_RETURN, var_virt_alias_doms);
+           string_list_init(VAR_VIRT_ALIAS_DOMS, MATCH_FLAG_RETURN,
+                            var_virt_alias_doms);
 
     if (*var_virt_mailbox_doms)
        virt_mailbox_doms =
-           string_list_init(MATCH_FLAG_RETURN, var_virt_mailbox_doms);
+           string_list_init(VAR_VIRT_MAILBOX_DOMS, MATCH_FLAG_RETURN,
+                            var_virt_mailbox_doms);
 
     if (*var_relay_domains)
        relay_domains =
-           domain_list_init(MATCH_FLAG_RETURN
+           domain_list_init(VAR_RELAY_DOMAINS, MATCH_FLAG_RETURN
                             | match_parent_style(VAR_RELAY_DOMAINS),
                             var_relay_domains);
 
     if (*var_relocated_maps)
        relocated_maps =
            maps_create(VAR_RELOCATED_MAPS, var_relocated_maps,
-                       DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX);
+                       DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX
+                       | DICT_FLAG_UTF8_REQUEST);
 }
index ec571cdb559e58a8f92f161dfd33e505937bf8b2..f270b8ddef1d1576be97f77ed5b7e4d54a60cc1c 100644 (file)
@@ -99,7 +99,8 @@ TRANSPORT_INFO *transport_pre_init(const char *transport_maps_name,
     tp = (TRANSPORT_INFO *) mymalloc(sizeof(*tp));
     tp->transport_path = maps_create(transport_maps_name, transport_maps,
                                     DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX
-                                    | DICT_FLAG_NO_REGSUB);
+                                    | DICT_FLAG_NO_REGSUB
+                                    | DICT_FLAG_UTF8_REQUEST);
     tp->wildcard_channel = tp->wildcard_nexthop = 0;
     tp->wildcard_errno = 0;
     tp->expire = 0;
index 4a71ec0e91a368a8bddc96b35b8a838fc5da82a7..31ae822e3859222579b4d9d3c23934a7ecdbbe6e 100644 (file)
@@ -542,25 +542,25 @@ static void pre_jail_init(char *unused_name, char **unused_argv)
            maps_create(resolve_regular.snd_relay_maps_name,
                        RES_PARAM_VALUE(resolve_regular.snd_relay_maps),
                        DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX
-                       | DICT_FLAG_NO_REGSUB);
+                       | DICT_FLAG_NO_REGSUB | DICT_FLAG_UTF8_REQUEST);
     if (*RES_PARAM_VALUE(resolve_verify.snd_relay_maps))
        resolve_verify.snd_relay_info =
            maps_create(resolve_verify.snd_relay_maps_name,
                        RES_PARAM_VALUE(resolve_verify.snd_relay_maps),
                        DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX
-                       | DICT_FLAG_NO_REGSUB);
+                       | DICT_FLAG_NO_REGSUB | DICT_FLAG_UTF8_REQUEST);
     if (*RES_PARAM_VALUE(resolve_regular.snd_def_xp_maps))
        resolve_regular.snd_def_xp_info =
            maps_create(resolve_regular.snd_def_xp_maps_name,
                        RES_PARAM_VALUE(resolve_regular.snd_def_xp_maps),
                        DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX
-                       | DICT_FLAG_NO_REGSUB);
+                       | DICT_FLAG_NO_REGSUB | DICT_FLAG_UTF8_REQUEST);
     if (*RES_PARAM_VALUE(resolve_verify.snd_def_xp_maps))
        resolve_verify.snd_def_xp_info =
            maps_create(resolve_verify.snd_def_xp_maps_name,
                        RES_PARAM_VALUE(resolve_verify.snd_def_xp_maps),
                        DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX
-                       | DICT_FLAG_NO_REGSUB);
+                       | DICT_FLAG_NO_REGSUB | DICT_FLAG_UTF8_REQUEST);
 }
 
 /* post_jail_init - initialize after entering chroot jail */
index b634d30ad6a07338ea0eaba1a7458e5464c2e01b..4dee5e51f60e5705098d9d8a004d354f3398f0c5 100644 (file)
@@ -38,8 +38,8 @@ SRCS  = alldig.c allprint.c argv.c argv_split.c attr_clnt.c attr_print0.c \
        dict_fail.c msg_rate_delay.c dict_surrogate.c warn_stat.c \
        dict_sockmap.c line_number.c recv_pass_attr.c pass_accept.c \
        poll_fd.c timecmp.c slmdb.c dict_pipe.c dict_random.c \
-       valid_utf8_hostname.c midna.c argv_splitq.c balpar.c dict_union.c \
-       extpar.c dict_inline.c
+       valid_utf8_hostname.c midna_domain.c argv_splitq.c balpar.c dict_union.c \
+       extpar.c dict_inline.c casefold.c dict_utf8.c
 OBJS   = alldig.o allprint.o argv.o argv_split.o attr_clnt.o attr_print0.o \
        attr_print64.o attr_print_plain.o attr_scan0.o attr_scan64.o \
        attr_scan_plain.o auto_clnt.o base64_code.o basename.o binhash.o \
@@ -79,8 +79,8 @@ OBJS  = alldig.o allprint.o argv.o argv_split.o attr_clnt.o attr_print0.o \
        dict_fail.o msg_rate_delay.o dict_surrogate.o warn_stat.o \
        dict_sockmap.o line_number.o recv_pass_attr.o pass_accept.o \
        poll_fd.o timecmp.o $(NON_PLUGIN_MAP_OBJ) dict_pipe.o dict_random.o \
-       valid_utf8_hostname.o midna.o argv_splitq.o balpar.o dict_union.o \
-       extpar.o dict_inline.o
+       valid_utf8_hostname.o midna_domain.o argv_splitq.o balpar.o dict_union.o \
+       extpar.o dict_inline.o casefold.o dict_utf8.o
 # MAP_OBJ is for maps that may be dynamically loaded with dynamicmaps.cf.
 # When hard-linking these, makedefs sets NON_PLUGIN_MAP_OBJ=$(MAP_OBJ),
 # otherwise it sets the PLUGIN_* macros.
@@ -109,7 +109,7 @@ HDRS        = argv.h attr.h attr_clnt.h auto_clnt.h base64_code.h binhash.h \
        edit_file.h dict_cache.h dict_thash.h ip_match.h nbbio.h base32_code.h \
        dict_fail.h warn_stat.h dict_sockmap.h line_number.h timecmp.h \
        slmdb.h compat_va_copy.h dict_pipe.h dict_random.h \
-       valid_utf8_hostname.h midna.h dict_union.h dict_inline.h check_arg.h
+       valid_utf8_hostname.h midna_domain.h dict_union.h dict_inline.h check_arg.h
 TESTSRC        = fifo_open.c fifo_rdwr_bug.c fifo_rdonly_bug.c select_bug.c \
        stream_test.c dup2_pass_on_exec.c
 DEFS   = -I. -D$(SYSTYPE)
@@ -128,7 +128,7 @@ TESTPROG= dict_open dup2_pass_on_exec events exec_command fifo_open \
        unix_recv_fd unix_send_fd stream_recv_fd stream_send_fd hex_code \
        myaddrinfo myaddrinfo4 inet_proto sane_basename format_tv \
        valid_utf8_string ip_match base32_code msg_rate_delay netstring \
-       vstream timecmp dict_cache midna
+       vstream timecmp dict_cache midna_domain casefold
 PLUGIN_MAP_SO = $(LIB_PREFIX)pcre$(LIB_SUFFIX)
 
 LIB_DIR        = ../../lib
@@ -504,7 +504,12 @@ dict_cache: $(LIB)
        $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS)
        mv junk $@.o
 
-midna: $(LIB)
+midna_domain: $(LIB)
+       mv $@.o junk
+       $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS)
+       mv junk $@.o
+
+casefold: $(LIB)
        mv $@.o junk
        $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS)
        mv junk $@.o
@@ -515,7 +520,8 @@ tests: all valid_hostname_test mac_expand_test dict_test unescape_test \
        dict_cidr_test attr_scan_plain_test htable_test hex_code_test \
        myaddrinfo_test format_tv_test ip_match_test name_mask_tests \
        base32_code_test dict_thash_test surrogate_test timecmp_test \
-       dict_static_test dict_inline_test midna_test
+       dict_static_test dict_inline_test midna_domain_test casefold_test \
+       dict_utf8_test
 
 root_tests:
 
@@ -705,8 +711,8 @@ base32_code_test: base32_code
        $(SHLIB_ENV) ./base32_code
 
 dict_thash_test: ../postmap/postmap dict_thash.map
-       $(SHLIB_ENV) ../postmap/postmap -s texthash:dict_thash.map >dict_thash.tmp 2>&1
-       sort dict_thash.tmp | diff -b dict_thash.map -
+       $(SHLIB_ENV) ../postmap/postmap -s texthash:dict_thash.map | sort >dict_thash.tmp 2>&1
+       tr '[A-Z]' '[a-z]' <dict_thash.map | sort | diff -b dict_thash.tmp -
        rm -f dict_thash.tmp
 
 surrogate_test: dict_open surrogate.ref
@@ -745,15 +751,27 @@ dict_inline_test: dict_open dict_inline.ref
        $(SHLIB_ENV) ./dict_open inline:'{ foo=xx x' read </dev/null; \
        $(SHLIB_ENV) ./dict_open inline:'{ foo=xx {x=y}x}' read </dev/null; \
        (echo get foo; echo get bar; echo get baz) | $(SHLIB_ENV) \
-           ./dict_open inline:'{ foo=xx, { bar = lotsa stuff }}' read; \
+           ./dict_open inline:'{ foo=XX, { bAr = lotsa stuff }}' read fold_fix; \
+       (echo get foo; echo get bar; echo get baz) | $(SHLIB_ENV) \
+           ./dict_open inline:'{ foo=XX, { bAr = lotsa stuff }}' read 'fold_fix,utf8_request'; \
        ) >dict_inline.tmp 2>&1
        diff dict_inline.ref dict_inline.tmp
        rm -f dict_inline.tmp
 
-midna_test: midna midna_test.in midna_test.ref
-       $(SHLIB_ENV) ./midna <midna_test.in >midna_test.tmp 2>&1
-       diff midna_test.ref midna_test.tmp
-       rm -f midna_test.tmp
+midna_domain_test: midna_domain midna_domain_test.in midna_domain_test.ref
+       $(SHLIB_ENV) ./midna_domain <midna_domain_test.in >midna_domain_test.tmp 2>&1
+       diff midna_domain_test.ref midna_domain_test.tmp
+       rm -f midna_domain_test.tmp
+
+casefold_test: casefold casefold_test.in casefold_test.ref
+       $(SHLIB_ENV) ./casefold <casefold_test.in >casefold_test.tmp 2>&1
+       diff casefold_test.ref casefold_test.tmp
+       rm -f casefold_test.tmp
+
+dict_utf8_test: dict_open dict_utf8_test.in dict_utf8_test.ref
+       $(SHLIB_ENV) sh dict_utf8_test.in >dict_utf8_test.tmp 2>&1
+       diff dict_utf8_test.ref dict_utf8_test.tmp
+       rm -f dict_utf8_test.tmp
 
 depend: $(MAKES)
        (sed '1,/^# do not edit/!d' Makefile.in; \
@@ -945,6 +963,13 @@ binhash.o: binhash.h
 binhash.o: msg.h
 binhash.o: mymalloc.h
 binhash.o: sys_defs.h
+casefold.o: casefold.c
+casefold.o: check_arg.h
+casefold.o: msg.h
+casefold.o: stringops.h
+casefold.o: sys_defs.h
+casefold.o: vbuf.h
+casefold.o: vstring.h
 chroot_uid.o: chroot_uid.c
 chroot_uid.o: chroot_uid.h
 chroot_uid.o: msg.h
@@ -1132,6 +1157,7 @@ dict_ht.o: vstring.h
 dict_inline.o: argv.h
 dict_inline.o: check_arg.h
 dict_inline.o: dict.h
+dict_inline.o: dict_ht.h
 dict_inline.o: dict_inline.c
 dict_inline.o: dict_inline.h
 dict_inline.o: htable.h
@@ -1380,20 +1406,19 @@ dict_test.o: vstring_vstream.h
 dict_thash.o: argv.h
 dict_thash.o: check_arg.h
 dict_thash.o: dict.h
+dict_thash.o: dict_ht.h
 dict_thash.o: dict_thash.c
 dict_thash.o: dict_thash.h
 dict_thash.o: htable.h
 dict_thash.o: iostuff.h
 dict_thash.o: msg.h
 dict_thash.o: myflock.h
-dict_thash.o: mymalloc.h
 dict_thash.o: readlline.h
 dict_thash.o: stringops.h
 dict_thash.o: sys_defs.h
 dict_thash.o: vbuf.h
 dict_thash.o: vstream.h
 dict_thash.o: vstring.h
-dict_thash.o: warn_stat.h
 dict_union.o: argv.h
 dict_union.o: check_arg.h
 dict_union.o: dict.h
@@ -1421,6 +1446,18 @@ dict_unix.o: sys_defs.h
 dict_unix.o: vbuf.h
 dict_unix.o: vstream.h
 dict_unix.o: vstring.h
+dict_utf8.o: argv.h
+dict_utf8.o: check_arg.h
+dict_utf8.o: dict.h
+dict_utf8.o: dict_utf8.c
+dict_utf8.o: msg.h
+dict_utf8.o: myflock.h
+dict_utf8.o: mymalloc.h
+dict_utf8.o: stringops.h
+dict_utf8.o: sys_defs.h
+dict_utf8.o: vbuf.h
+dict_utf8.o: vstream.h
+dict_utf8.o: vstring.h
 dir_forest.o: check_arg.h
 dir_forest.o: dir_forest.c
 dir_forest.o: dir_forest.h
@@ -1678,8 +1715,6 @@ load_file.o: vbuf.h
 load_file.o: vstream.h
 load_file.o: warn_stat.h
 load_lib.o: load_lib.c
-load_lib.o: load_lib.h
-load_lib.o: msg.h
 load_lib.o: sys_defs.h
 lowercase.o: check_arg.h
 lowercase.o: lowercase.c
@@ -1755,17 +1790,17 @@ match_ops.o: sys_defs.h
 match_ops.o: vbuf.h
 match_ops.o: vstream.h
 match_ops.o: vstring.h
-midna.o: check_arg.h
-midna.o: ctable.h
-midna.o: midna.c
-midna.o: midna.h
-midna.o: msg.h
-midna.o: mymalloc.h
-midna.o: stringops.h
-midna.o: sys_defs.h
-midna.o: valid_hostname.h
-midna.o: vbuf.h
-midna.o: vstring.h
+midna_domain.o: check_arg.h
+midna_domain.o: ctable.h
+midna_domain.o: midna_domain.c
+midna_domain.o: midna_domain.h
+midna_domain.o: msg.h
+midna_domain.o: mymalloc.h
+midna_domain.o: stringops.h
+midna_domain.o: sys_defs.h
+midna_domain.o: valid_hostname.h
+midna_domain.o: vbuf.h
+midna_domain.o: vstring.h
 msg.o: msg.c
 msg.o: msg.h
 msg.o: msg_output.h
@@ -2223,7 +2258,7 @@ valid_hostname.o: valid_hostname.h
 valid_hostname.o: vbuf.h
 valid_hostname.o: vstring.h
 valid_utf8_hostname.o: check_arg.h
-valid_utf8_hostname.o: midna.h
+valid_utf8_hostname.o: midna_domain.h
 valid_utf8_hostname.o: msg.h
 valid_utf8_hostname.o: mymalloc.h
 valid_utf8_hostname.o: stringops.h
diff --git a/postfix/src/util/casefold.c b/postfix/src/util/casefold.c
new file mode 100644 (file)
index 0000000..c1260c7
--- /dev/null
@@ -0,0 +1,293 @@
+/*++
+/* NAME
+/*     casefold 3
+/* SUMMARY
+/*     casefold text for caseless comparison
+/* SYNOPSIS
+/*     #include <stringops.h>
+/*
+/*     char    *casefold(
+/*     int     utf8_request,
+/*     VSTRING *src,
+/*     const char *src,
+/*     CONST_CHAR_STAR *err)
+/* DESCRIPTION
+/*     casefold() converts text to a form that is suitable for
+/*     caseless comparison, rather than presentation to humans.
+/*
+/*     When compiled without EAI support, casefold() implements
+/*     ASCII case folding, leaving non-ASCII byte values unchanged.
+/*     This mode has no error returns.
+/*
+/*     When compiled with EAI support, casefold() implements UTF-8
+/*     case folding using the en_US locale, as recommended when
+/*     the conversion result is not meant to be presented to humans.
+/*     When conversion fails the result is null, and the pointer
+/*     referenced by err is updated.
+/*
+/*     With the ICU 4.8 library, there is no casefold error for
+/*     UTF-8 code points U+0000..U+10FFFF (including surrogate
+/*     range), not even when running inside an empty chroot jail.
+/*
+/*     Arguments:
+/* .IP utf8_request
+/*     Boolean parameter that enables UTF-8 case folding instead
+/*     of folding only ASCII characters. This flag is ignored when
+/*     compiled without EAI support.
+/* .IP src
+/*     Null-terminated input string.
+/* .IP dest
+/*     Output buffer, null-terminated if the function completes
+/*     without reporting an error.
+/* .IP err
+/*     Null pointer, or pointer to "const char *". for descriptive
+/*     text about errors.
+/* LICENSE
+/* .ad
+/* .fi
+/*     The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/*     Wietse Venema
+/*     IBM T.J. Watson Research
+/*     P.O. Box 704
+/*     Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <string.h>
+#include <ctype.h>
+#ifndef NO_EAI
+#include <unicode/ucasemap.h>
+#include <unicode/ustring.h>
+#include <unicode/uchar.h>
+#endif
+
+/* Utility library. */
+
+#include <msg.h>
+#include <stringops.h>
+
+#define STR(x) vstring_str(x)
+#define LEN(x) VSTRING_LEN(x)
+
+/* casefold - casefold an UTF-8 string */
+
+char   *casefold(int utf8_req, VSTRING *dest, const char *src,
+                        CONST_CHAR_STAR *err)
+{
+#ifdef NO_EAI
+
+    /*
+     * ASCII mode only.
+     */
+    vstring_strcpy(dest, src);
+    return (lowercase(STR(dest)));
+#else
+
+    /*
+     * Unicode mode.
+     */
+    static UCaseMap *csm = 0;
+    UErrorCode error;
+    ssize_t space_needed;
+    int     n;
+
+    /*
+     * All-ASCII input, or ASCII mode only.
+     */
+    if (utf8_req == 0 || allascii(src)) {
+       vstring_strcpy(dest, src);
+       return (lowercase(STR(dest)));
+    }
+
+    /*
+     * ICU 4.8 ucasemap_utf8FoldCase() does not complain about UTF-8 syntax
+     * errors. XXX Is this behavior guaranteed or accidental? We don't know,
+     * therefore must check it here.
+     */
+    if (valid_utf8_string(src, strlen(src)) == 0) {
+       if (err)
+           *err = "malformed UTF-8 or invalid codepoint";
+       return (0);
+    }
+
+    /*
+     * One-time initialization. With ICU 4.8 this works while chrooted.
+     */
+    if (csm == 0) {
+       error = U_ZERO_ERROR;
+       csm = ucasemap_open("en_US", U_FOLD_CASE_DEFAULT, &error);
+       if (U_SUCCESS(error) == 0)
+           msg_fatal("ucasemap_open error: %s", u_errorName(error));
+    }
+
+    /*
+     * Fold the input, adjusting the buffer size if needed. Safety: don't
+     * loop forever.
+     */
+    VSTRING_RESET(dest);
+    for (n = 0; n < 3; n++) {
+       error = U_ZERO_ERROR;
+       space_needed =
+           ucasemap_utf8FoldCase(csm, STR(dest), vstring_avail(dest),
+                                 src, strlen(src), &error);
+       if (error == U_BUFFER_OVERFLOW_ERROR) {
+           VSTRING_SPACE(dest, space_needed);
+       } else {
+           break;
+       }
+    }
+
+    /*
+     * Report the result. With ICU 4.8, there are no casefolding errors for
+     * the entire RFC 3629 Unicode range (code points U+0000..U+10FFFF
+     * including surrogates), nor are there casefolding errors for bad UTF-8
+     * input. XXX Is this behavior guaranteed or accidental? We don't know,
+     * therefore we have the UTF-8 syntax check (and range check) above.
+     */
+    if (U_SUCCESS(error) == 0) {
+       if (err)
+           *err = u_errorName(error);
+       return (0);
+    } else {
+       /* Position the write pointer at the null terminator. */
+       VSTRING_AT_OFFSET(dest, space_needed - 1);
+       return (STR(dest));
+    }
+#endif                                         /* NO_EAI */
+}
+
+#ifdef TEST
+
+static void encode_utf8(VSTRING *buffer, int codepoint)
+{
+    const char myname[] = "encode_utf8";
+
+    VSTRING_RESET(buffer);
+    if (codepoint < 0x80) {
+       VSTRING_ADDCH(buffer, codepoint);
+    } else if (codepoint < 0x800) {
+       VSTRING_ADDCH(buffer, 0xc0 | (codepoint >> 6));
+       VSTRING_ADDCH(buffer, 0x80 | (codepoint & 0x3f));
+    } else if (codepoint < 0x10000) {
+       VSTRING_ADDCH(buffer, 0xe0 | (codepoint >> 12));
+       VSTRING_ADDCH(buffer, 0x80 | ((codepoint >> 6) & 0x3f));
+       VSTRING_ADDCH(buffer, 0x80 | (codepoint & 0x3f));
+    } else if (codepoint <= 0x10FFFF) {
+       VSTRING_ADDCH(buffer, 0xf0 | (codepoint >> 18));
+       VSTRING_ADDCH(buffer, 0x80 | ((codepoint >> 12) & 0x3f));
+       VSTRING_ADDCH(buffer, 0x80 | ((codepoint >> 6) & 0x3f));
+       VSTRING_ADDCH(buffer, 0x80 | (codepoint & 0x3f));
+    } else {
+       msg_panic("%s: out-of-range codepoint U+%X", myname, codepoint);
+    }
+    VSTRING_TERMINATE(buffer);
+}
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <locale.h>
+
+#include <vstream.h>
+#include <vstring_vstream.h>
+#include <msg_vstream.h>
+
+int     main(int argc, char **argv)
+{
+    VSTRING *buffer = vstring_alloc(1);
+    VSTRING *dest = vstring_alloc(1);
+    char   *bp;
+    char   *conv_res;
+    const char *fold_err;
+    char   *cmd;
+    int     codepoint, first, last, utf8_req;
+
+    if (setlocale(LC_ALL, "C") == 0)
+       msg_fatal("setlocale(LC_ALL, C) failed: %m");
+
+    msg_vstream_init(argv[0], VSTREAM_ERR);
+
+    utf8_req = util_utf8_enable = 1;
+
+    VSTRING_SPACE(buffer, 256);                        /* chroot pathname */
+
+    while (vstring_fgets_nonl(buffer, VSTREAM_IN)) {
+       bp = STR(buffer);
+       vstream_printf("> %s\n", bp);
+       cmd = mystrtok(&bp, CHARS_SPACE);
+       if (cmd == 0 || *cmd == '#')
+           continue;
+       while (ISSPACE(*bp))
+           bp++;
+
+       /*
+        * Null-terminated string.
+        */
+       if (strcmp(cmd, "fold") == 0) {
+           if ((conv_res = casefold(utf8_req, dest, bp, &fold_err)) != 0)
+               vstream_printf("\"%s\" ->fold \"%s\"\n", bp, conv_res);
+           else
+               vstream_printf("cannot casefold \"%s\": %s\n", bp, fold_err);
+       }
+
+       /*
+        * Codepoint range.
+        */
+       else if (strcmp(cmd, "range") == 0
+                && sscanf(bp, "%i %i", &first, &last) == 2
+                && first <= last) {
+           for (codepoint = first; codepoint <= last; codepoint++) {
+               if (codepoint >= 0xD800 && codepoint <= 0xDFFF) {
+                   vstream_printf("skipping surrogate range\n");
+                   codepoint = 0xDFFF;
+               } else {
+                   encode_utf8(buffer, codepoint);
+                   if (msg_verbose)
+                       vstream_printf("U+%X -> %s\n", codepoint, STR(buffer));
+                   if (valid_utf8_string(STR(buffer), LEN(buffer)) == 0)
+                       msg_fatal("bad utf-8 encoding for U+%X\n", codepoint);
+                   if (casefold(utf8_req, dest, STR(buffer), &fold_err) == 0)
+                       vstream_printf("casefold error for U+%X: %s\n",
+                                      codepoint, fold_err);
+               }
+           }
+           vstream_printf("range completed: 0x%x..0x%x\n", first, last);
+       }
+
+       /*
+        * Chroot directory.
+        */
+       else if (strcmp(cmd, "chroot") == 0
+                && sscanf(bp, "%255s", STR(buffer)) == 1) {
+           if (geteuid() == 0) {
+               if (chdir(STR(buffer)) < 0)
+                   msg_fatal("chdir(%s): %m\n", STR(buffer));
+               if (chroot(STR(buffer)) < 0)
+                   msg_fatal("chroot(%s): %m\n", STR(buffer));
+               vstream_printf("chroot %s completed\n", STR(buffer));
+           }
+       }
+
+       /*
+        * Verbose.
+        */
+       else if (strcmp(cmd, "verbose") == 0
+                && sscanf(bp, "%i", &msg_verbose) == 1) {
+            /* void */ ;
+       }
+
+       /*
+        * Usage
+        */
+       else {
+           vstream_printf("Usage: %s chroot <path> | fold <text> | range <first> <last> | verbose <int>\n",
+                          argv[0]);
+       }
+       vstream_fflush(VSTREAM_OUT);
+    }
+    exit(0);
+}
+
+#endif                                 /* TEST */
diff --git a/postfix/src/util/casefold_test.in b/postfix/src/util/casefold_test.in
new file mode 100644 (file)
index 0000000..9b50e22
--- /dev/null
@@ -0,0 +1,22 @@
+# Ignored when not running as root.
+chroot /tmp
+# Casefold U+0000 .. U+10FFFF excluding surrogates.
+range 0x0 0xD7FF
+range 0xD800 0xD800
+range 0xDFFF 0xDFFF
+range 0xE000 0x10FFFF
+# Demonstrate that range is not a noop.
+verbose 1
+range 0xE000 0xE007
+verbose 0
+# Upper-case greek -> lower-case greek.
+fold Δημοσθένους.example.com
+# Upper-case ASCII -> lower-case ASCII.
+fold HeLlO.ExAmPlE.CoM
+# Folding does not change aliases for '.'.
+fold x。example.com
+fold x.example.com
+fold x。example.com
+# Bad UTF-8
+fold yyy\80\80\80
+fold \80\80\80xxx
diff --git a/postfix/src/util/casefold_test.ref b/postfix/src/util/casefold_test.ref
new file mode 100644 (file)
index 0000000..00d6f8d
--- /dev/null
@@ -0,0 +1,44 @@
+> # Ignored when not running as root.
+> chroot /tmp
+> # Casefold U+0000 .. U+10FFFF excluding surrogates.
+> range 0x0 0xD7FF
+range completed: 0x0..0xd7ff
+> range 0xD800 0xD800
+skipping surrogate range
+range completed: 0xd800..0xd800
+> range 0xDFFF 0xDFFF
+skipping surrogate range
+range completed: 0xdfff..0xdfff
+> range 0xE000 0x10FFFF
+range completed: 0xe000..0x10ffff
+> # Demonstrate that range is not a noop.
+> verbose 1
+> range 0xE000 0xE007
+U+E000 -> 
+U+E001 -> 
+U+E002 -> 
+U+E003 -> 
+U+E004 -> 
+U+E005 -> 
+U+E006 -> 
+U+E007 -> 
+range completed: 0xe000..0xe007
+> verbose 0
+> # Upper-case greek -> lower-case greek.
+> fold Δημοσθένους.example.com
+"Δημοσθένους.example.com" ->fold "δημοσθένουσ.example.com"
+> # Upper-case ASCII -> lower-case ASCII.
+> fold HeLlO.ExAmPlE.CoM
+"HeLlO.ExAmPlE.CoM" ->fold "hello.example.com"
+> # Folding does not change aliases for '.'.
+> fold x。example.com
+"x。example.com" ->fold "x。example.com"
+> fold x.example.com
+"x.example.com" ->fold "x.example.com"
+> fold x。example.com
+"x。example.com" ->fold "x。example.com"
+> # Bad UTF-8
+> fold yyy\80\80\80
+cannot casefold "yyy\80\80\80": malformed UTF-8 or invalid codepoint
+> fold \80\80\80xxx
+cannot casefold "\80\80\80xxx": malformed UTF-8 or invalid codepoint
index df583179eda67865e69d97d531e6ee04f9bd619e..75fd949721874e507e462db716f821320e2aa182 100644 (file)
@@ -637,6 +637,8 @@ static const NAME_MASK dict_mask[] = {
     "open_lock", DICT_FLAG_OPEN_LOCK,  /* permanent lock upon open */
     "bulk_update", DICT_FLAG_BULK_UPDATE,      /* bulk update if supported */
     "multi_writer", DICT_FLAG_MULTI_WRITER,    /* multi-writer safe */
+    "utf8_request", DICT_FLAG_UTF8_REQUEST,    /* request UTF-8 activation */
+    "utf8_active", DICT_FLAG_UTF8_ACTIVE,      /* UTF-8 is activated */
     0,
 };
 
index 104996ae5f8235f3d6c31e7fc013a3b00daa8e36..0c3c464a60739188f6b1a9857e7eb640c91614d9 100644 (file)
@@ -92,6 +92,7 @@ typedef struct DICT {
     VSTRING *fold_buf;                 /* key folding buffer */
     DICT_OWNER owner;                  /* provenance */
     int     error;                     /* last operation only */
+    ssize_t size;                      /* size of this thing */
     DICT_JMP_BUF *jbuf;                        /* exception handling */
 } DICT;
 
@@ -126,6 +127,10 @@ extern DICT *dict_debug(DICT *);
 #define DICT_FLAG_OPEN_LOCK    (1<<16) /* perm lock if not multi-writer safe */
 #define DICT_FLAG_BULK_UPDATE  (1<<17) /* optimize for bulk updates */
 #define DICT_FLAG_MULTI_WRITER (1<<18) /* multi-writer safe map */
+#define DICT_FLAG_UTF8_REQUEST (1<<19) /* activate UTF-8 if possible */
+#define DICT_FLAG_UTF8_ACTIVE  (1<<20) /* UTF-8 proxy layer is present */
+
+#define DICT_FLAG_UTF8_MASK    (DICT_FLAG_UTF8_REQUEST)
 
  /* IMPORTANT: Update the dict_mask[] table when the above changes */
 
@@ -157,9 +162,15 @@ extern DICT *dict_debug(DICT *);
 #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_PARANOID | DICT_FLAG_UTF8_MASK)
 #define DICT_FLAG_INST_MASK    ~(DICT_FLAG_IMPL_MASK | DICT_FLAG_RQST_MASK)
 
+ /*
+  * Feature tests.
+  */
+#define DICT_NEED_UTF8_ACTIVATION(enable, flags) \
+       ((enable) && ((flags) & DICT_FLAG_UTF8_MASK))
+
  /*
   * dict->error values. Errors must be negative; smtpd_check depends on this.
   */
@@ -233,6 +244,14 @@ extern int dict_changed(void);
 extern const char *dict_changed_name(void);
 extern const char *dict_flags_str(int);
 extern int dict_flags_mask(const char *);
+extern void dict_type_override(DICT *, const char *);
+
+ /*
+  * Check and convert UTF-8 keys and values.
+  */
+extern DICT *dict_utf8_activate(DICT *);
+extern char *dict_utf8_check_fold(DICT *, const char *, CONST_CHAR_STAR *);
+extern int dict_utf8_check(const char *, CONST_CHAR_STAR *);
 
  /*
   * Driver for interactive or scripted tests.
@@ -250,6 +269,7 @@ extern DICT *PRINTFLIKE(5, 6) dict_surrogate(const char *, const char *, int, in
   * This name is reserved for matchlist error handling.
   */
 #define DICT_TYPE_NOFILE       "non-existent"
+#define DICT_TYPE_NOUTF8       "non-UTF-8"
 
  /*
   * Duplicated from vstream(3). This should probably be abstracted out.
@@ -262,13 +282,13 @@ extern DICT *PRINTFLIKE(5, 6) dict_surrogate(const char *, const char *, int, in
   * systems have bugs in their implementation.
   */
 #ifdef NO_SIGSETJMP
-#define dict_setjmp(stream)            setjmp((stream)->jbuf[0])
-#define dict_longjmp(stream, val)      longjmp((stream)->jbuf[0], (val))
+#define dict_setjmp(dict)      setjmp((dict)->jbuf[0])
+#define dict_longjmp(dict, val)        longjmp((dict)->jbuf[0], (val))
 #else
-#define dict_setjmp(stream)            sigsetjmp((stream)->jbuf[0], 1)
-#define dict_longjmp(stream, val)      siglongjmp((stream)->jbuf[0], (val))
+#define dict_setjmp(dict)      sigsetjmp((dict)->jbuf[0], 1)
+#define dict_longjmp(dict, val)        siglongjmp((dict)->jbuf[0], (val))
 #endif
-#define dict_isjmp(stream)             ((stream)->jbuf != 0)
+#define dict_isjmp(dict)       ((dict)->jbuf != 0)
 
  /*
   * Temporary API. If exception handling proves to be useful,
index f370569a82c35697c664f575e5ca554e766e4da7..094e2193a6152dc0c97763ab2dc98ae8829063e1 100644 (file)
@@ -153,6 +153,7 @@ DICT   *dict_alloc(const char *dict_type, const char *dict_name, ssize_t size)
     dict->owner.status = DICT_OWNER_UNKNOWN;
     dict->owner.uid = INT_MAX;
     dict->error = DICT_ERR_NONE;
+    dict->size = size;
     dict->jbuf = 0;
     return dict;
 }
index dbd4bd9ada32bd1da20f45bce1076e73d3652c1b..85e49a4ca644ba65877736d74215cb9e5f3c323c 100644 (file)
@@ -195,7 +195,7 @@ static DICT *dict_cdbq_open(const char *path, int dict_flags)
 
     if ((fd = open(cdb_path, O_RDONLY)) < 0)
        DICT_CDBQ_OPEN_RETURN(dict_surrogate(DICT_TYPE_CDB, path,
-                                          O_RDONLY, dict_flags,
+                                            O_RDONLY, dict_flags,
                                         "open database %s: %m", cdb_path));
 
     dict_cdbq = (DICT_CDBQ *) dict_alloc(DICT_TYPE_CDB,
index 3d9a44325f3a656232ce7a4f14b0bad109ff43f9..46634d40cd48c04d97cc5e263e948bd8a0edae3f 100644 (file)
@@ -59,7 +59,9 @@ static const char *dict_debug_lookup(DICT *dict, const char *key)
     DICT   *real_dict = dict_debug->real_dict;
     const char *result;
 
+    real_dict->flags = dict->flags;
     result = dict_get(real_dict, key);
+    dict->flags = real_dict->flags;
     msg_info("%s:%s lookup: \"%s\" = \"%s\"", dict->type, dict->name, key,
             result ? result : real_dict->error ? "error" : "not_found");
     DICT_ERR_VAL_RETURN(dict, real_dict->error, result);
@@ -73,7 +75,9 @@ static int dict_debug_update(DICT *dict, const char *key, const char *value)
     DICT   *real_dict = dict_debug->real_dict;
     int     result;
 
+    real_dict->flags = dict->flags;
     result = dict_put(real_dict, key, value);
+    dict->flags = real_dict->flags;
     msg_info("%s:%s update: \"%s\" = \"%s\": %s", dict->type, dict->name,
             key, value, result == 0 ? "success" : real_dict->error ?
             "error" : "failed");
@@ -88,7 +92,9 @@ static int dict_debug_delete(DICT *dict, const char *key)
     DICT   *real_dict = dict_debug->real_dict;
     int     result;
 
+    real_dict->flags = dict->flags;
     result = dict_del(real_dict, key);
+    dict->flags = real_dict->flags;
     msg_info("%s:%s delete: \"%s\": %s", dict->type, dict->name, key,
             result == 0 ? "success" : real_dict->error ?
             "error" : "failed");
@@ -104,7 +110,9 @@ static int dict_debug_sequence(DICT *dict, int function,
     DICT   *real_dict = dict_debug->real_dict;
     int     result;
 
+    real_dict->flags = dict->flags;
     result = dict_seq(real_dict, function, key, value);
+    dict->flags = real_dict->flags;
     if (result == 0)
        msg_info("%s:%s sequence: \"%s\" = \"%s\"", dict->type, dict->name,
                 *key, *value);
index af646007c02cd616df9f938a874216b4e188a9bb..c6202a6ac0bf1f5b26a6585e95e9747e80cc05a8 100644 (file)
 /* System library. */
 
 #include <sys_defs.h>
+#include <string.h>
 
 /* Utility library. */
 
 #include <msg.h>
 #include <mymalloc.h>
-#include <htable.h>
 #include <stringops.h>
 #include <dict.h>
+#include <dict_ht.h>
 #include <dict_inline.h>
 
 /* Application-specific. */
 
-typedef struct {
-    DICT    dict;                      /* generic members */
-    HTABLE *table;                     /* lookup table */
-    HTABLE_INFO **info;                        /* for iterator */
-    HTABLE_INFO **cursor;              /* ditto */
-} DICT_INLINE;
-
-/* dict_inline_lookup - search inline table */
-
-static const char *dict_inline_lookup(DICT *dict, const char *name)
-{
-    DICT_INLINE *dict_inline = (DICT_INLINE *) dict;
-    const char *result = 0;
-
-    /*
-     * Optionally fold the key.
-     */
-    if (dict->flags & DICT_FLAG_FOLD_FIX) {
-       if (dict->fold_buf == 0)
-           dict->fold_buf = vstring_alloc(10);
-       vstring_strcpy(dict->fold_buf, name);
-       name = lowercase(vstring_str(dict->fold_buf));
-    }
-
-    /*
-     * Look up the value.
-     */
-    result = htable_find(dict_inline->table, name);
-
-    DICT_ERR_VAL_RETURN(dict, DICT_ERR_NONE, result);
-}
-
-/* dict_inline_sequence - traverse the dictionary */
-
-static int dict_inline_sequence(DICT *dict, int function,
-                                       const char **key, const char **value)
-{
-    const char *myname = "dict_inline_sequence";
-    DICT_INLINE *dict_inline = (DICT_INLINE *) dict;
-
-    /*
-     * Determine and execute the seek function.
-     */
-    switch (function) {
-    case DICT_SEQ_FUN_FIRST:
-       if (dict_inline->info == 0)
-           dict_inline->info = htable_list(dict_inline->table);
-       dict_inline->cursor = dict_inline->info;
-       break;
-    case DICT_SEQ_FUN_NEXT:
-       if (dict_inline->cursor[0])
-           dict_inline->cursor += 1;
-       break;
-    default:
-       msg_panic("%s: invalid function: %d", myname, function);
-    }
-
-    /*
-     * Return the entry under the cursor.
-     */
-    if (dict_inline->cursor[0]) {
-       *key = dict_inline->cursor[0]->key;
-       *value = dict_inline->cursor[0]->value;
-       DICT_ERR_VAL_RETURN(dict, DICT_ERR_NONE, DICT_STAT_SUCCESS);
-    } else {
-       *key = 0;
-       *value = 0;
-       DICT_ERR_VAL_RETURN(dict, DICT_ERR_NONE, DICT_STAT_FAIL);
-    }
-}
-
-/* dict_inline_close - disassociate from inline table */
-
-static void dict_inline_close(DICT *dict)
-{
-    DICT_INLINE *dict_inline = (DICT_INLINE *) dict;
-
-    htable_free(dict_inline->table, myfree);
-    if (dict_inline->info)
-       myfree((void *) dict_inline->info);
-    if (dict->fold_buf)
-       vstring_free(dict->fold_buf);
-    dict_free(dict);
-}
-
 /* dict_inline_open - open inline table */
 
 DICT   *dict_inline_open(const char *name, int open_flags, int dict_flags)
 {
-    DICT_INLINE *dict_inline;
+    DICT   *dict;
     char   *cp, *saved_name = 0;
     size_t  len;
-    HTABLE *table = 0;
     char   *nameval, *vname, *value;
     const char *err = 0;
     char   *xperr = 0;
+    int     count = 0;
 
     /*
      * Clarity first. Let the optimizer worry about redundant code.
@@ -149,8 +65,6 @@ DICT   *dict_inline_open(const char *name, int open_flags, int dict_flags)
                myfree(saved_name); \
            if (xperr != 0) \
                myfree(xperr); \
-           if (table != 0) \
-               htable_free(table, myfree); \
            return (__d); \
        } while (0)
 
@@ -163,6 +77,19 @@ DICT   *dict_inline_open(const char *name, int open_flags, int dict_flags)
                                  "%s:%s map requires O_RDONLY access mode",
                                          DICT_TYPE_INLINE, name));
 
+    /*
+     * UTF-8 syntax check.
+     */
+    if (DICT_NEED_UTF8_ACTIVATION(util_utf8_enable, dict_flags)
+       && allascii(name) == 0
+       && valid_utf8_string(name, strlen(name)) == 0)
+       DICT_INLINE_RETURN(dict_surrogate(DICT_TYPE_INLINE, name,
+                                         open_flags, dict_flags,
+                                         "bad UTF-8 syntax: \"%s:%s\"; "
+                                         "need \"%s:{name=value...}\"",
+                                         DICT_TYPE_INLINE, name,
+                                         DICT_TYPE_INLINE));
+
     /*
      * Parse the table into its constituent name=value pairs.
      */
@@ -175,15 +102,23 @@ DICT   *dict_inline_open(const char *name, int open_flags, int dict_flags)
                                          DICT_TYPE_INLINE, name,
                                          DICT_TYPE_INLINE));
 
-    table = htable_create(5);
+    /*
+     * Reuse the "internal" dictionary type.
+     */
+    dict = dict_open3(DICT_TYPE_HT, name, open_flags, dict_flags);
+    dict_type_override(dict, DICT_TYPE_INLINE);
     while ((nameval = mystrtokq(&cp, CHARS_COMMA_SP, CHARS_BRACE)) != 0) {
        if ((nameval[0] != CHARS_BRACE[0]
             || (err = xperr = extpar(&nameval, CHARS_BRACE, EXTPAR_FLAG_STRIP)) == 0)
            && (err = split_nameval(nameval, &vname, &value)) != 0)
            break;
-       (void) htable_enter(table, vname, mystrdup(value));
+
+       /* No duplicate checks. See comments in dict_thash.c. */
+       dict->update(dict, vname, value);
+       count += 1;
     }
-    if (err != 0 || table->used == 0)
+    if (err != 0 || count == 0) {
+       dict->close(dict);
        DICT_INLINE_RETURN(dict_surrogate(DICT_TYPE_INLINE, name,
                                          open_flags, dict_flags,
                                          "%s: \"%s:%s\"; "
@@ -191,21 +126,8 @@ DICT   *dict_inline_open(const char *name, int open_flags, int dict_flags)
                                          err != 0 ? err : "empty table",
                                          DICT_TYPE_INLINE, name,
                                          DICT_TYPE_INLINE));
+    }
+    dict->owner.status = DICT_OWNER_TRUSTED;
 
-    /*
-     * Bundle up the result.
-     */
-    dict_inline = (DICT_INLINE *)
-       dict_alloc(DICT_TYPE_INLINE, name, sizeof(*dict_inline));
-    dict_inline->dict.lookup = dict_inline_lookup;
-    dict_inline->dict.sequence = dict_inline_sequence;
-    dict_inline->dict.close = dict_inline_close;
-    dict_inline->dict.flags = dict_flags | DICT_FLAG_FIXED;
-    dict_inline->dict.owner.status = DICT_OWNER_TRUSTED;
-    if (dict_flags & DICT_FLAG_FOLD_FIX)
-       dict_inline->dict.fold_buf = vstring_alloc(10);
-    dict_inline->info = 0;
-    dict_inline->table = table;
-    table = 0;
-    DICT_INLINE_RETURN(DICT_DEBUG (&dict_inline->dict));
+    DICT_INLINE_RETURN(DICT_DEBUG (dict));
 }
index 771ca9f6a53f59cb52ebc40104a83f4ac5f752ca..e64e6d040656fb4053b9ad706110a13dbd5f357c 100644 (file)
@@ -10,7 +10,14 @@ owner=trusted (uid=2147483647)
 owner=trusted (uid=2147483647)
 owner=trusted (uid=2147483647)
 > get foo
-foo=xx
+foo=XX
+> get bar
+bar=lotsa stuff
+> get baz
+baz: not found
+owner=trusted (uid=2147483647)
+> get foo
+foo=XX
 > get bar
 bar=lotsa stuff
 > get baz
index 3cbea12f9ee8eef4c64b845161aff0333fb31e0e..50f5c709011cc2c3cd2617324fb765f9ae82ef16 100644 (file)
@@ -235,7 +235,6 @@ static int dict_lmdb_update(DICT *dict, const char *name, const char *value)
        name = lowercase(vstring_str(dict->fold_buf));
     }
     mdb_key.mv_data = (void *) name;
-
     mdb_value.mv_data = (void *) value;
     mdb_key.mv_size = strlen(name);
     mdb_value.mv_size = strlen(value);
@@ -435,6 +434,8 @@ static int dict_lmdb_sequence(DICT *dict, int function,
        if (mdb_value.mv_data != 0 && mdb_value.mv_size > 0)
            *value = SCOPY(dict_lmdb->val_buf, mdb_value.mv_data,
                           mdb_value.mv_size);
+       else
+           *value = "";                        /* XXX */
        break;
 
        /*
index 0880b95cd1790291e62a956818d8ffc0c817615b..a18a6ce34a68136775bf784644ecbd00fba729ac 100644 (file)
 /*     int     dict_longjmp(dict, val)
 /*     DICT    *dict;
 /*     int     val;
+/*
+/*     void    dict_type_override(dict, type)
+/*     DICT    *dict;
+/*     const char *type;
 /* DESCRIPTION
 /*     This module implements a low-level interface to multiple
 /*     physical dictionary types.
 /*     With databases whose lookup fields are fixed-case strings,
 /*     fold the search string to lower case before accessing the
 /*     database.  This includes hash:, cdb:, dbm:. nis:, ldap:,
-/*     *sql.
+/*     *sql. WARNING: case folding is supported only for ASCII or
+/*     valid UTF-8.
 /* .IP DICT_FLAG_FOLD_MUL
 /*     With databases where one lookup field can match both upper
 /*     and lower case, fold the search key to lower case before
-/*     accessing the database. This includes regexp: and pcre:
+/*     accessing the database. This includes regexp: and pcre:.
+/*     WARNING: case folding is supported only for ASCII or valid
+/*     UTF-8.
 /* .IP DICT_FLAG_FOLD_ANY
 /*     Short-hand for (DICT_FLAG_FOLD_FIX | DICT_FLAG_FOLD_MUL).
 /* .IP DICT_FLAG_SYNC_UPDATE
 /*     and must trap exceptions from the database client with dict_setjmp().
 /* .IP DICT_FLAG_DEBUG
 /*     Enable additional logging.
+/* .IP DICT_FLAG_UTF8_ENABLE
+/*     With util_utf8_enable != 0, require that lookup/update/delete
+/*     keys and values are valid UTF-8. Skip a lookup/update/delete
+/*     request with a non-UTF-8 key, skip an update request with
+/*     a non-UTF-8 value, and fail a lookup request with a non-UTF-8
+/*     value.
 /* .PP
 /*     Specify DICT_FLAG_NONE for no special processing.
 /*
 /*     dict_open3() takes separate arguments for dictionary type and
 /*     name, but otherwise performs the same functions as dict_open().
 /*
+/*     The dict_get(), dict_put(), dict_del(), and dict_seq()
+/*     macros evaluate their first argument multiple times.
+/*     These names should have been in uppercase.
+/*
 /*     dict_get() retrieves the value stored in the named dictionary
 /*     under the given key. A null pointer means the value was not found.
 /*     As with dict_lookup(), the result is owned by the lookup table
 /*     NB: non-local jumps such as dict_longjmp() are not safe for
 /*     jumping out of any routine that manipulates DICT data.
 /*     longjmp() like calls are best avoided in signal handlers.
+/*
+/*     dict_type_override() changes the symbolic dictionary type.
+/*     This is used by dictionaries whose internals are based on
+/*     some other dictionary type.
 /* DIAGNOSTICS
 /*     Fatal error: open error, unsupported dictionary type, attempt to
 /*     update non-writable dictionary.
@@ -457,6 +478,10 @@ DICT   *dict_open3(const char *dict_type, const char *dict_name,
            msg_fatal("%s:%s: unable to get exclusive lock: %m",
                      dict_type, dict_name);
     }
+    /* Last step: insert proxy for UTF-8 syntax checks and casefolding. */
+    if ((dict->flags & DICT_FLAG_UTF8_ACTIVE) == 0
+       && DICT_NEED_UTF8_ACTIVATION(util_utf8_enable, dict_flags))
+       dict = dict_utf8_activate(dict);
     return (dict);
 }
 
@@ -532,6 +557,14 @@ DICT_MAPNAMES_EXTEND_FN dict_mapnames_extend(DICT_MAPNAMES_EXTEND_FN new_cb)
     return (old_cb);
 }
 
+/* dict_type_override - disguise a dictionary type */
+
+void    dict_type_override(DICT *dict, const char *type)
+{
+    myfree(dict->type);
+    dict->type = mystrdup(type);
+}
+
 #ifdef TEST
 
  /*
index 0fb0133bf97da14236ffa53d7ffd24e2e2bc0123..5f394ff81c65b549b34da351cb36dfb79af7a6bc 100644 (file)
@@ -76,9 +76,11 @@ void    dict_test(int argc, char **argv)
        dict_flags |= DICT_FLAG_LOCK;
     if ((dict_flags & (DICT_FLAG_DUP_WARN | DICT_FLAG_DUP_IGNORE)) == 0)
        dict_flags |= DICT_FLAG_DUP_REPLACE;
+    dict_flags |= DICT_FLAG_UTF8_REQUEST;
     vstream_fflush(VSTREAM_OUT);
     dict_name = argv[optind];
     dict_allow_surrogate = 1;
+    util_utf8_enable = 1;
     dict = dict_open(dict_name, open_flags, dict_flags);
     dict_register(dict_name, dict);
     vstream_printf("owner=%s (uid=%ld)\n",
@@ -125,8 +127,6 @@ void    dict_test(int argc, char **argv)
            if (dict_put(dict, key, value) != 0)
                vstream_printf("%s: %s\n", key, dict->error ?
                               "error" : "not updated");
-           else
-               vstream_printf("%s=%s\n", key, value);
        } else if (strcmp(cmd, "first") == 0 && !key && !value) {
            if (dict_seq(dict, DICT_SEQ_FUN_FIRST, &key, &value) == 0)
                vstream_printf("%s=%s\n", key, value);
index 0872fb09a16b56af12fec2c0dcf9993a26c5d516..54e91f88d4e5ea8f168e87db7396aa7091275f3d 100644 (file)
@@ -12,7 +12,6 @@ foo=fooval
 > del foo
 foo: deleted
 > put baz bazval
-baz=bazval
 > get baz
 baz=bazval
 > del baz
index aaef6613f1b7098625f156c7d8b305fb731301e4..cad181cfd13c47a2b9acae9d3b8d294bea06020c 100644 (file)
 /* Utility library. */
 
 #include <msg.h>
-#include <mymalloc.h>
-#include <htable.h>
 #include <iostuff.h>
 #include <vstring.h>
 #include <stringops.h>
 #include <readlline.h>
 #include <dict.h>
+#include <dict_ht.h>
 #include <dict_thash.h>
-#include <warn_stat.h>
 
 /* Application-specific. */
 
-typedef struct {
-    DICT    dict;                      /* generic members */
-    HTABLE *table;                     /* in-memory hash */
-    HTABLE_INFO **info;                        /* for iterator */
-    HTABLE_INFO **cursor;              /* ditto */
-} DICT_THASH;
-
 #define STR    vstring_str
-
-/* dict_thash_lookup - find database entry */
-
-static const char *dict_thash_lookup(DICT *dict, const char *name)
-{
-    DICT_THASH *dict_thash = (DICT_THASH *) dict;
-    const char *result = 0;
-
-    /*
-     * Optionally fold the key.
-     */
-    if (dict->flags & DICT_FLAG_FOLD_FIX) {
-       if (dict->fold_buf == 0)
-           dict->fold_buf = vstring_alloc(10);
-       vstring_strcpy(dict->fold_buf, name);
-       name = lowercase(vstring_str(dict->fold_buf));
-    }
-
-    /*
-     * Look up the value.
-     */
-    result = htable_find(dict_thash->table, name);
-
-    DICT_ERR_VAL_RETURN(dict, DICT_ERR_NONE, result);
-}
-
-/* dict_thash_sequence - traverse the dictionary */
-
-static int dict_thash_sequence(DICT *dict, int function,
-                                      const char **key, const char **value)
-{
-    const char *myname = "dict_thash_sequence";
-    DICT_THASH *dict_thash = (DICT_THASH *) dict;
-
-    /*
-     * Determine and execute the seek function.
-     */
-    switch (function) {
-    case DICT_SEQ_FUN_FIRST:
-       if (dict_thash->info == 0)
-           dict_thash->info = htable_list(dict_thash->table);
-       dict_thash->cursor = dict_thash->info;
-       break;
-    case DICT_SEQ_FUN_NEXT:
-       if (dict_thash->cursor[0])
-           dict_thash->cursor += 1;
-       break;
-    default:
-       msg_panic("%s: invalid function: %d", myname, function);
-    }
-
-    /*
-     * Return the entry under the cursor.
-     */
-    if (dict_thash->cursor[0]) {
-       *key = dict_thash->cursor[0]->key;
-       *value = dict_thash->cursor[0]->value;
-       DICT_ERR_VAL_RETURN(dict, DICT_ERR_NONE, DICT_STAT_SUCCESS);
-    } else {
-       *key = 0;
-       *value = 0;
-       DICT_ERR_VAL_RETURN(dict, DICT_ERR_NONE, DICT_STAT_FAIL);
-    }
-}
-
-/* dict_thash_close - disassociate from data base */
-
-static void dict_thash_close(DICT *dict)
-{
-    DICT_THASH *dict_thash = (DICT_THASH *) dict;
-
-    htable_free(dict_thash->table, myfree);
-    if (dict_thash->info)
-       myfree((void *) dict_thash->info);
-    if (dict->fold_buf)
-       vstring_free(dict->fold_buf);
-    dict_free(dict);
-}
+#define LEN    VSTRING_LEN
 
 /* dict_thash_open - open flat text data base */
 
 DICT   *dict_thash_open(const char *path, int open_flags, int dict_flags)
 {
-    DICT_THASH *dict_thash;
-    VSTREAM *fp = 0;
+    DICT   *dict;
+    VSTREAM *fp = 0;                   /* DICT_THASH_OPEN_RETURN() */
     struct stat st;
     time_t  before;
     time_t  after;
-    VSTRING *line_buffer = 0;
+    VSTRING *line_buffer = 0;          /* DICT_THASH_OPEN_RETURN() */
     int     lineno;
     int     last_line;
     char   *key;
     char   *value;
-    HTABLE *table;
-    HTABLE_INFO *ht;
 
     /*
      * Let the optimizer worry about eliminating redundant code.
@@ -187,13 +99,31 @@ DICT   *dict_thash_open(const char *path, int open_flags, int dict_flags)
            DICT_THASH_OPEN_RETURN(dict_surrogate(DICT_TYPE_THASH, path,
                                                  open_flags, dict_flags,
                                             "open database %s: %m", path));
-       }
+           }
+
+       /*
+        * Reuse the "internal" dictionary type.
+        */
+       dict = dict_open3(DICT_TYPE_HT, path, open_flags, dict_flags);
+       dict_type_override(dict, DICT_TYPE_THASH);
+
        if (line_buffer == 0)
            line_buffer = vstring_alloc(100);
        last_line = 0;
-       table = htable_create(13);
        while (readllines(line_buffer, fp, &last_line, &lineno)) {
 
+           /*
+            * First some UTF-8 checks sans casefolding.
+            */
+           if ((dict->flags & DICT_FLAG_UTF8_ACTIVE)
+               && allascii(STR(line_buffer)) == 0
+               && valid_utf8_string(STR(line_buffer), LEN(line_buffer)) == 0) {
+               msg_warn("%s, line %d: non-UTF-8 input \"%s\""
+                        " -- ignoring this line",
+                        VSTREAM_PATH(fp), lineno, STR(line_buffer));
+               continue;
+           }
+
            /*
             * Split on the first whitespace character, then trim leading and
             * trailing whitespace from key and value.
@@ -220,31 +150,31 @@ DICT   *dict_thash_open(const char *path, int open_flags, int dict_flags)
                msg_warn("%s, line %d: record is in \"key: value\" format;"
                         " is this an alias file?", path, lineno);
 
-           /*
-            * Optionally fold the key.
-            */
-           if (dict_flags & DICT_FLAG_FOLD_FIX)
-               lowercase(key);
-
            /*
             * Store the value under the key. Handle duplicates
-            * appropriately.
+            * appropriately. XXX Move this into dict_ht, but 1) that map
+            * ignores duplicates by default and we would have to check that
+            * we won't break existing code that depends on such benavior; 2)
+            * by inlining the checks here we can degrade gracefully instead
+            * of terminating with a fatal error. See comment in dict_inline.c.
             */
-           if ((ht = htable_locate(table, key)) != 0) {
+           if (dict->lookup(dict, key) != 0) {
                if (dict_flags & DICT_FLAG_DUP_IGNORE) {
                     /* void */ ;
                } else if (dict_flags & DICT_FLAG_DUP_REPLACE) {
-                   myfree(ht->value);
-                   ht->value = mystrdup(value);
+                   dict->update(dict, key, value);
                } else if (dict_flags & DICT_FLAG_DUP_WARN) {
                    msg_warn("%s, line %d: duplicate entry: \"%s\"",
                             path, lineno, key);
                } else {
-                   msg_fatal("%s, line %d: duplicate entry: \"%s\"",
-                             path, lineno, key);
+                   dict->close(dict);
+                   DICT_THASH_OPEN_RETURN(dict_surrogate(DICT_TYPE_THASH, path,
+                                                    open_flags, dict_flags,
+                                    "%s, line %d: duplicate entry: \"%s\"",
+                                                       path, lineno, key));
                }
            } else {
-               htable_enter(table, key, mystrdup(value));
+               dict->update(dict, key, value);
            }
        }
 
@@ -263,27 +193,14 @@ DICT   *dict_thash_open(const char *path, int open_flags, int dict_flags)
        /*
         * Yes, it is hot. Discard the result and read the file again.
         */
-       htable_free(table, myfree);
+       dict->close(dict);
        if (msg_verbose > 1)
            msg_info("pausing to let file %s cool down", path);
        doze(300000);
     }
 
-    /*
-     * Create the in-memory table.
-     */
-    dict_thash = (DICT_THASH *)
-       dict_alloc(DICT_TYPE_THASH, path, sizeof(*dict_thash));
-    dict_thash->dict.lookup = dict_thash_lookup;
-    dict_thash->dict.sequence = dict_thash_sequence;
-    dict_thash->dict.close = dict_thash_close;
-    dict_thash->dict.flags = dict_flags | DICT_FLAG_DUP_WARN | DICT_FLAG_FIXED;
-    if (dict_flags & DICT_FLAG_FOLD_FIX)
-       dict_thash->dict.fold_buf = vstring_alloc(10);
-    dict_thash->info = 0;
-    dict_thash->table = table;
-    dict_thash->dict.owner.uid = st.st_uid;
-    dict_thash->dict.owner.status = (st.st_uid != 0);
+    dict->owner.uid = st.st_uid;
+    dict->owner.status = (st.st_uid != 0);
 
-    DICT_THASH_OPEN_RETURN(DICT_DEBUG (&dict_thash->dict));
+    DICT_THASH_OPEN_RETURN(DICT_DEBUG (dict));
 }
index 67e75784f79007cf06a8ac60f7489a2e3ef65b1a..95b54fd826dd4be47e4d552b5a6c3089f732fd79 100644 (file)
@@ -12,3 +12,4 @@ attr_scan0.c 15454
 attr_scan64.c 17256
 attr_scan_plain.c 16924
 auto_clnt.c 9819
+ABCDEF 012345
diff --git a/postfix/src/util/dict_utf8.c b/postfix/src/util/dict_utf8.c
new file mode 100644 (file)
index 0000000..17fe061
--- /dev/null
@@ -0,0 +1,327 @@
+/*++
+/* NAME
+/*     dict_utf8 3
+/* SUMMARY
+/*     dictionary UTF-8 helpers
+/* SYNOPSIS
+/*     #include <dict.h>
+/*
+/*     DICT    *dict_utf8_activate(
+/*     DICT    *dict)
+/* AUXILIARY FUNCTIONS
+/*     char    *dict_utf8_check_fold(
+/*     DICT    *dict,
+/*     const char *string,
+/*     CONST_CHAR_STAR *err,
+/*     int     fold_flag)
+/*
+/*     int     dict_utf8_check(
+/*     const char *string,
+/*     CONST_CHAR_STAR *err)
+/* DESCRIPTION
+/*     dict_utf8_activate() wraps a dictionary's lookup/update/delete
+/*     methods with code that enforces UTF-8 checks on keys and
+/*     values, and that logs a warning when incorrect UTF-8 is
+/*     encountered. The original dictionary handle becomes invalid.
+/*
+/*     The wrapper code enforces a policy that maximizes application
+/*     robustness (it avoids the need for new error-handling code
+/*     paths in application code).  Attempts to store non-UTF-8
+/*     keys or values are skipped while reporting a non-error
+/*     status, attempts to look up or delete non-UTF-8 keys are
+/*     skipped while reporting a non-error status, and lookup
+/*     results that contain a non-UTF-8 value are blocked while
+/*     reporting a configuration error.
+/*
+/*     The dict_utf8_check* functions may be invoked to perform
+/*     UTF-8 validity checks when util_utf8_enable is non-zero.
+/*
+/*     dict_utf8_check_fold() optionally folds a string, and checks
+/*     it for UTF-8 validity. The result is the possibly-folded
+/*     string, or a null pointer in case of error.
+/*
+/*     dict_utf8_check() checks a string for UTF-8 validity. The
+/*     result is zero in case of error.
+/* BUGS
+/*     dict_utf8_activate() does not nest.
+/* LICENSE
+/* .ad
+/* .fi
+/*     The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/*     Wietse Venema
+/*     IBM T.J. Watson Research
+/*     P.O. Box 704
+/*     Yorktown Heights, NY 10598, USA
+/*--*/
+
+ /*
+  * System library.
+  */
+#include <sys_defs.h>
+#include <string.h>
+
+ /*
+  * Utility library.
+  */
+#include <msg.h>
+#include <stringops.h>
+#include <dict.h>
+#include <mymalloc.h>
+#include <msg.h>
+
+ /*
+  * Backed-up accessor function pointers.
+  */
+typedef struct {
+    const char *(*lookup) (struct DICT *, const char *);
+    int     (*update) (struct DICT *, const char *, const char *);
+    int     (*delete) (struct DICT *, const char *);
+} DICT_UTF8_BACKUP;
+
+ /*
+  * The goal is to maximize robustness: bad UTF-8 should not appear in keys,
+  * because those are derived from controlled inputs, and values should be
+  * printable before they are stored. But if we failed to check something
+  * then it should not result in fatal errors and thus open up the system for
+  * a denial-of-service attack.
+  * 
+  * Proposed over-all policy: skip attempts to store invalid UTF-8 lookup keys
+  * or values. Rationale: some storage may not permit malformed UTF-8. This
+  * maximizes program robustness. If we get an invalid lookup result, report
+  * a configuration error.
+  * 
+  * LOOKUP
+  * 
+  * If the key is invalid, log a warning and skip the request. Rationale: the
+  * item cannot exist.
+  * 
+  * If the lookup result is invalid, log a warning and return a configuration
+  * error.
+  * 
+  * UPDATE
+  * 
+  * If the key is invalid, then log a warning and skip the request. Rationale:
+  * the item cannot exist.
+  * 
+  * If the value is invalid, log a warning and skip the request. Rationale:
+  * storage may not permit malformed UTF-8. This maximizes program
+  * robustness.
+  * 
+  * DELETE
+  * 
+  * If the key is invalid, then skip the request. Rationale: the item cannot
+  * exist.
+  */
+
+/* dict_utf8_check_fold - casefold or validate string */
+
+char   *dict_utf8_check_fold(DICT *dict, const char *string,
+                                    CONST_CHAR_STAR *err)
+{
+    int     fold_flag = (dict->flags & DICT_FLAG_FOLD_ANY);
+
+    /*
+     * Casefold and implicitly validate UTF-8.
+     */
+    if (fold_flag != 0 && (fold_flag & (dict->flags & DICT_FLAG_FIXED) ?
+                          DICT_FLAG_FOLD_FIX : DICT_FLAG_FOLD_MUL)) {
+       if (dict->fold_buf == 0)
+           dict->fold_buf = vstring_alloc(10);
+       return (casefold(dict->flags & DICT_FLAG_UTF8_ACTIVE,
+                        dict->fold_buf, string, err));
+    }
+
+    /*
+     * Validate UTF-8 without casefolding.
+     */
+    if (!allascii(string) && valid_utf8_string(string, strlen(string)) == 0) {
+       if (err)
+           *err = "malformed UTF-8 or invalid codepoint";
+       return (0);
+    }
+    return ((char *) string);
+}
+
+/* dict_utf8_check validate UTF-8 string */
+
+int     dict_utf8_check(const char *string, CONST_CHAR_STAR *err)
+{
+    if (!allascii(string) && valid_utf8_string(string, strlen(string)) == 0) {
+       if (err)
+           *err = "malformed UTF-8 or invalid codepoint";
+       return (0);
+    }
+    return (1);
+}
+
+/* dict_utf8_lookup - UTF-8 lookup method wrapper */
+
+static const char *dict_utf8_lookup(DICT *dict, const char *key)
+{
+    DICT_UTF8_BACKUP *backup;
+    const char *utf8_err;
+    const char *fold_res;
+    const char *value;
+    int     saved_flags;
+
+    /*
+     * Validate and optionally fold the key, and if invalid skip the request.
+     */
+    if ((fold_res = dict_utf8_check_fold(dict, key, &utf8_err)) == 0) {
+       msg_warn("%s:%s: non-UTF-8 key \"%s\": %s",
+                dict->type, dict->name, key, utf8_err);
+       dict->error = DICT_ERR_NONE;
+       return (0);
+    }
+
+    /*
+     * Proxy the request with casefolding turned off.
+     */
+    saved_flags = (dict->flags & DICT_FLAG_FOLD_ANY);
+    dict->flags &= ~DICT_FLAG_FOLD_ANY;
+    backup = (void *) dict + dict->size;
+    value = backup->lookup(dict, fold_res);
+    dict->flags |= saved_flags;
+
+    /*
+     * Validate the result, and if invalid fail the request.
+     */
+    if (value != 0 && dict_utf8_check(value, &utf8_err) == 0) {
+       msg_warn("%s:%s: key \"%s\": non-UTF-8 value \"%s\": %s",
+                dict->type, dict->name, key, value, utf8_err);
+       dict->error = DICT_ERR_CONFIG;
+       return (0);
+    } else {
+       return (value);
+    }
+}
+
+/* dict_utf8_update - UTF-8 update method wrapper */
+
+static int dict_utf8_update(DICT *dict, const char *key, const char *value)
+{
+    DICT_UTF8_BACKUP *backup;
+    const char *utf8_err;
+    const char *fold_res;
+    int     saved_flags;
+    int     status;
+
+    /*
+     * Validate or fold the key, and if invalid skip the request.
+     */
+    if ((fold_res = dict_utf8_check_fold(dict, key, &utf8_err)) == 0) {
+       msg_warn("%s:%s: non-UTF-8 key \"%s\": %s",
+                dict->type, dict->name, key, utf8_err);
+       dict->error = DICT_ERR_NONE;
+       return (DICT_STAT_SUCCESS);
+    }
+
+    /*
+     * Validate the value, and if invalid skip the request.
+     */
+    else if (dict_utf8_check(value, &utf8_err) == 0) {
+       msg_warn("%s:%s: key \"%s\": non-UTF-8 value \"%s\": %s",
+                dict->type, dict->name, key, value, utf8_err);
+       dict->error = DICT_ERR_NONE;
+       return (DICT_STAT_SUCCESS);
+    }
+
+    /*
+     * Proxy the request with casefolding turned off.
+     */
+    else {
+       saved_flags = (dict->flags & DICT_FLAG_FOLD_ANY);
+       dict->flags &= ~DICT_FLAG_FOLD_ANY;
+       backup = (void *) dict + dict->size;
+       status = backup->update(dict, fold_res, value);
+       dict->flags |= saved_flags;
+       return (status);
+    }
+}
+
+/* dict_utf8_delete - UTF-8 delete method wrapper */
+
+static int dict_utf8_delete(DICT *dict, const char *key)
+{
+    DICT_UTF8_BACKUP *backup;
+    const char *utf8_err;
+    const char *fold_res;
+    int     saved_flags;
+    int     status;
+
+    /*
+     * Validate and optionally fold the key, and if invalid skip the request.
+     */
+    if ((fold_res = dict_utf8_check_fold(dict, key, &utf8_err)) == 0) {
+       msg_warn("%s:%s: non-UTF-8 key \"%s\": %s",
+                dict->type, dict->name, key, utf8_err);
+       dict->error = DICT_ERR_NONE;
+       return (DICT_STAT_SUCCESS);
+    }
+
+    /*
+     * Proxy the request with casefolding turned off.
+     */
+    else {
+       saved_flags = (dict->flags & DICT_FLAG_FOLD_ANY);
+       dict->flags &= ~DICT_FLAG_FOLD_ANY;
+       backup = (void *) dict + dict->size;
+       status = backup->delete(dict, fold_res);
+       dict->flags |= saved_flags;
+       return (status);
+    }
+}
+
+/* dict_utf8_activate - wrap a legacy dict object for UTF-8 processing */
+
+DICT   *dict_utf8_activate(DICT *dict)
+{
+    const char myname[] = "dict_utf8_activate";
+    DICT_UTF8_BACKUP *backup;
+
+    /*
+     * Sanity check.
+     */
+    if (util_utf8_enable == 0)
+       msg_panic("%s: Unicode support is not available", myname);
+    if ((dict->flags & DICT_FLAG_UTF8_REQUEST) == 0)
+       msg_panic("%s: %s:%s does not request Unicode support",
+                 myname, dict->type, dict->name);
+    if (dict->flags & DICT_FLAG_UTF8_ACTIVE)
+       msg_panic("%s: %s:%s Unicode support is already activated",
+                 myname, dict->type, dict->name);
+
+    /*
+     * Unlike dict_debug(3) we do not put a proxy dict object in front of the
+     * encapsulated object, because then we would have to bidirectionally
+     * propagate changes in the data members (errors, flags, jbuf, and so on)
+     * between proxy object and encapsulated object.
+     * 
+     * Instead we append ourselves to the encapsulated dict object itself, and
+     * redirect some function pointers. This approach does not yet generalize
+     * to arbitrary levels of encapsulation. That is, it does not co-exist
+     * with dict_debug(3) which is broken for the reasons stated above.
+     */
+    dict = myrealloc(dict, dict->size + sizeof(*backup));
+    backup = (void *) dict + dict->size;
+
+    /*
+     * Interpose on the lookup/update/delete methods. It is a conscious
+     * decision not to tinker with the iterator or destructor.
+     */
+    backup->lookup = dict->lookup;
+    backup->update = dict->update;
+    backup->delete = dict->delete;
+
+    dict->lookup = dict_utf8_lookup;
+    dict->update = dict_utf8_update;
+    dict->delete = dict_utf8_delete;
+
+    /*
+     * Leave our mark. See sanity check above.
+     */
+    dict->flags |= DICT_FLAG_UTF8_ACTIVE;
+
+    return (dict);
+}
diff --git a/postfix/src/util/dict_utf8_test.in b/postfix/src/util/dict_utf8_test.in
new file mode 100644 (file)
index 0000000..748d25b
--- /dev/null
@@ -0,0 +1,12 @@
+#!/bin/sh
+
+awk 'BEGIN {
+       print "flags"
+       print "verbose"
+       printf "get foo\n"
+       printf "put %c%c%c xxx\n", 128, 128, 128
+       printf "get %c%c%c\n", 128, 128, 128
+       printf "put xxx %c%c%c\n", 128, 128, 128
+       printf "get xxx\n"
+       exit
+}' | ./dict_open internal:whatever write utf8_request
diff --git a/postfix/src/util/dict_utf8_test.ref b/postfix/src/util/dict_utf8_test.ref
new file mode 100644 (file)
index 0000000..c29a336
--- /dev/null
@@ -0,0 +1,15 @@
+owner=trusted (uid=2147483647)
+> flags
+dict flags fixed|lock|replace|utf8_request|utf8_active
+> verbose
+> get foo
+foo: not found
+> put \80\80\80 xxx
+./dict_open: warning: internal:whatever: non-UTF-8 key "???": malformed UTF-8 or invalid codepoint
+> get \80\80\80
+./dict_open: warning: internal:whatever: non-UTF-8 key "???": malformed UTF-8 or invalid codepoint
+\80\80\80: not found
+> put xxx \80\80\80
+./dict_open: warning: internal:whatever: key "xxx": non-UTF-8 value "???": malformed UTF-8 or invalid codepoint
+> get xxx
+xxx: not found
index defa3b4553840921200cc924216cd8d05655dad4..c4e86166debd6f7adeb55527e96510b20ac3286f 100644 (file)
@@ -91,7 +91,7 @@
 
 #include <msg.h>
 #include <split_at.h>
-#include <stringops.h>                 /* XXX temp_utf8_kludge */
+#include <stringops.h>                 /* XXX util_utf8_enable */
 #include <valid_utf8_hostname.h>
 
 /* Global library. */
@@ -158,7 +158,7 @@ const char *host_port(char *buf, char **host, char *def_host,
      * network addresses instead of requiring proper [ipaddress] forms.
      */
     if (*host != def_host 
-       && !valid_utf8_hostname(temp_utf8_kludge, *host, DONT_GRIPE)
+       && !valid_utf8_hostname(util_utf8_enable, *host, DONT_GRIPE)
        && !valid_hostaddr(*host, DONT_GRIPE))
        return ("valid hostname or network address required");
     if (*port != def_service && ISDIGIT(**port) && !alldig(*port))
index 7bf88bd583fcb24b2dad499c04c041608f446941..36ee0902ad73369acc06c6c30253a2322f737952 100644 (file)
@@ -6,7 +6,8 @@
 /* SYNOPSIS
 /*     #include <match_list.h>
 /*
-/*     MATCH_LIST *match_list_init(flags, pattern_list, count, func,...)
+/*     MATCH_LIST *match_list_init(pname, flags, pattern_list, count, func,...)
+/*     const char *pname;
 /*     int     flags;
 /*     const char *pattern_list;
 /*     int     count;
 /*     void match_list_free(list)
 /*     MATCH_LIST *list;
 /* DESCRIPTION
-/*     This module implements a framework for tests for list membership.
-/*     The actual tests are done by user-supplied functions.
+/*     This module implements a framework for tests for list
+/*     membership.  The actual tests are done by user-supplied
+/*     functions.
 /*
 /*     Patterns are separated by whitespace and/or commas. A pattern
 /*     is either a string, a file name (in which case the contents
 /*     of the file are substituted for the file name) or a type:name
-/*     lookup table specification. In order to reverse the result of
-/*     a pattern match, precede a pattern with an exclamation point (!).
+/*     lookup table specification. In order to reverse the result
+/*     of a pattern match, precede a pattern with an exclamation
+/*     point (!).
 /*
-/*     match_list_init() performs initializations. The flags argument
-/*     specifies the bit-wise OR of zero or more of the following:
+/*     match_list_init() performs initializations.  When the global
+/*     util_utf8_enable variable is non-zero, and when the code
+/*     is compiled with EAI support, string comparison will use
+/*     caseless UTF-8 mode.  Otherwise, only ASCII characters will
+/*     be casefolded.
+/*
+/*     match_list_match() matches strings against the specified
+/*     pattern list, passing the first string to the first function
+/*     given to match_list_init(), the second string to the second
+/*     function, and so on.
+/*
+/*     match_list_free() releases storage allocated by match_list_init().
+/*
+/*     Arguments:
+/* .IP pname
+/*     Parameter name or other identiying information that is
+/*     prepended to error messages.
+/* .IP flags
+/*     Specifies the bit-wise OR of zero or more of the following:
 /* .RS
 /* .IP MATCH_FLAG_PARENT
-/*     The hostname pattern foo.com matches any name within the domain
-/*     foo.com. If this flag is cleared, foo.com matches itself
-/*     only, and .foo.com matches any name below the domain foo.com.
+/*     The hostname pattern foo.com matches any name within the
+/*     domain foo.com. If this flag is cleared, foo.com matches
+/*     itself only, and .foo.com matches any name below the domain
+/*     foo.com.
 /* .IP MATCH_FLAG_RETURN
 /*     Request that match_list_match() logs a warning and returns
 /*     zero (with list->error set to a non-zero dictionary error
 /*     code) instead of raising a fatal run-time error.
 /* .RE
 /*     Specify MATCH_FLAG_NONE to request none of the above.
-/*     The pattern_list argument specifies a list of patterns.  The third
-/*     argument specifies how many match functions follow.
-/*
-/*     match_list_match() matches strings against the specified pattern
-/*     list, passing the first string to the first function given to
-/*     match_list_init(), the second string to the second function, and
-/*     so on.
-/*
-/*     match_list_free() releases storage allocated by match_list_init().
+/* .IP pattern_list
+/*     A list of patterns.
+/* .IP count
+/*     Specifies how many match functions follow.
+/* .IP list
+/*     Pattern list produced by match_list_init().
+/* .IP string
+/*     Search string.
 /* DIAGNOSTICS
 /*     Fatal error: unable to open or read a match_list file; invalid
-/*     match_list pattern.
+/*     match_list pattern; casefold error (UTF-8 mode only).
 /* SEE ALSO
 /*     host_match(3) match hosts by name or by address
 /* LICENSE
 
 /* match_list_parse - parse buffer, destroy buffer */
 
-static ARGV *match_list_parse(ARGV *list, char *string, int init_match)
+static ARGV *match_list_parse(MATCH_LIST *match_list, ARGV *pat_list,
+                                     char *string, int init_match)
 {
     const char *myname = "match_list_parse";
     VSTRING *buf = vstring_alloc(10);
@@ -105,39 +126,52 @@ static ARGV *match_list_parse(ARGV *list, char *string, int init_match)
     char   *item;
     char   *map_type_name_flags;
     int     match;
+    const char *utf8_err;
 
+    /*
+     * We do not use DICT_FLAG_FOLD_FIX, because we casefold the search
+     * string at the beginning of a search, and we use strcmp() for string
+     * comparison. This works because string patterns are casefolded during
+     * match_list initialization, and databases are supposed to fold case
+     * upon creation.
+     */
 #define OPEN_FLAGS     O_RDONLY
-#define DICT_FLAGS     (DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX)
+#define DICT_FLAGS     (DICT_FLAG_LOCK | DICT_FLAG_UTF8_REQUEST)
 #define STR(x)         vstring_str(x)
 
     /*
      * /filename contents are expanded in-line. To support !/filename we
      * prepend the negation operator to each item from the file.
+     * 
+     * If there is an error, implement graceful degradation by inserting a
+     * pseudo table whose lookups fail with a warning message.
      */
     while ((start = mystrtokq(&bp, delim, CHARS_BRACE)) != 0) {
        if (*start == '#') {
            msg_warn("%s: comment at end of line is not supported: %s %s",
-                    myname, start, bp);
+                    match_list->pname, start, bp);
            break;
        }
        for (match = init_match, item = start; *item == '!'; item++)
            match = !match;
        if (*item == 0)
-           msg_fatal("%s: no pattern after '!'", myname);
+           /* No graceful degradation for this... */
+           msg_fatal("%s: no pattern after '!'", match_list->pname);
        if (*item == '/') {                     /* /file/name */
            if ((fp = vstream_fopen(item, O_RDONLY, 0)) == 0) {
+               /* Replace unusable pattern with pseudo table. */
                vstring_sprintf(buf, "%s:%s", DICT_TYPE_NOFILE, item);
-               /* XXX Should increment existing map refcount. */
                if (dict_handle(STR(buf)) == 0)
                    dict_register(STR(buf),
                                  dict_surrogate(DICT_TYPE_NOFILE, item,
                                                 OPEN_FLAGS, DICT_FLAGS,
                                                 "open file %s: %m", item));
-               argv_add(list, STR(buf), (char *) 0);
+               argv_add(pat_list, STR(buf), (char *) 0);
            } else {
                while (vstring_fgets(buf, fp))
                    if (vstring_str(buf)[0] != '#')
-                       list = match_list_parse(list, vstring_str(buf), match);
+                       pat_list = match_list_parse(match_list, pat_list,
+                                                   vstring_str(buf), match);
                if (vstream_fclose(fp))
                    msg_fatal("%s: read file %s: %m", myname, item);
            }
@@ -145,23 +179,35 @@ static ARGV *match_list_parse(ARGV *list, char *string, int init_match)
            vstring_sprintf(buf, "%s%s(%o,%s)", match ? "" : "!",
                            item, OPEN_FLAGS, dict_flags_str(DICT_FLAGS));
            map_type_name_flags = STR(buf) + (match == 0);
-           /* XXX Should increment existing map refcount. */
            if (dict_handle(map_type_name_flags) == 0)
                dict_register(map_type_name_flags,
                              dict_open(item, OPEN_FLAGS, DICT_FLAGS));
-           argv_add(list, STR(buf), (char *) 0);
+           argv_add(pat_list, STR(buf), (char *) 0);
        } else {                                /* other pattern */
-           argv_add(list, match ? item :
-                    STR(vstring_sprintf(buf, "!%s", item)), (char *) 0);
+           if (casefold(util_utf8_enable, match_list->fold_buf, match ?
+                        item : STR(vstring_sprintf(buf, "!%s", item)),
+                        &utf8_err) == 0) {
+               /* Replace unusable pattern with pseudo table. */
+               vstring_sprintf(match_list->fold_buf, "%s:%s",
+                               DICT_TYPE_NOUTF8, item);
+               if (dict_handle(STR(match_list->fold_buf)) == 0)
+                   dict_register(STR(match_list->fold_buf),
+                                 dict_surrogate(DICT_TYPE_NOUTF8, item,
+                                                OPEN_FLAGS, DICT_FLAGS,
+                                                "casefold error: %s",
+                                                utf8_err));
+           }
+           argv_add(pat_list, STR(match_list->fold_buf), (char *) 0);
        }
     }
     vstring_free(buf);
-    return (list);
+    return (pat_list);
 }
 
 /* match_list_init - initialize pattern list */
 
-MATCH_LIST *match_list_init(int flags, const char *patterns, int match_count,...)
+MATCH_LIST *match_list_init(const char *pname, int flags,
+                                 const char *patterns, int match_count,...)
 {
     MATCH_LIST *list;
     char   *saved_patterns;
@@ -172,6 +218,7 @@ MATCH_LIST *match_list_init(int flags, const char *patterns, int match_count,...
        msg_panic("match_list_init: bad flags 0x%x", flags);
 
     list = (MATCH_LIST *) mymalloc(sizeof(*list));
+    list->pname = mystrdup(pname);
     list->flags = flags;
     list->match_count = match_count;
     list->match_func =
@@ -183,11 +230,13 @@ MATCH_LIST *match_list_init(int flags, const char *patterns, int match_count,...
        list->match_func[i] = va_arg(ap, MATCH_LIST_FN);
     va_end(ap);
     list->error = 0;
+    list->fold_buf = vstring_alloc(20);
 
 #define DO_MATCH       1
 
     saved_patterns = mystrdup(patterns);
-    list->patterns = match_list_parse(argv_alloc(1), saved_patterns, DO_MATCH);
+    list->patterns = match_list_parse(list, argv_alloc(1), saved_patterns,
+                                     DO_MATCH);
     argv_terminate(list->patterns);
     myfree(saved_patterns);
     return (list);
@@ -203,6 +252,7 @@ int     match_list_match(MATCH_LIST *list,...)
     int     match;
     int     i;
     va_list ap;
+    const char *utf8_err;
 
     /*
      * Iterate over all patterns in the list, stop at the first match.
@@ -216,11 +266,18 @@ int     match_list_match(MATCH_LIST *list,...)
     for (cpp = list->patterns->argv; (pat = *cpp) != 0; cpp++) {
        for (match = 1; *pat == '!'; pat++)
            match = !match;
-       for (i = 0; i < list->match_count; i++)
-           if (list->match_func[i] (list, list->match_args[i], pat))
+       for (i = 0; i < list->match_count; i++) {
+           if (casefold(util_utf8_enable, list->fold_buf,
+                        list->match_args[i], &utf8_err) == 0) {
+               msg_warn("%s: casefold error for \"%s\": %s",
+                        myname, list->match_args[i], utf8_err);
+               continue;
+           }
+           if (list->match_func[i] (list, STR(list->fold_buf), pat))
                return (match);
            else if (list->error != 0)
                return (0);
+       }
     }
     if (msg_verbose)
        for (i = 0; i < list->match_count; i++)
@@ -233,8 +290,10 @@ int     match_list_match(MATCH_LIST *list,...)
 void    match_list_free(MATCH_LIST *list)
 {
     /* XXX Should decrement map refcounts. */
+    myfree(list->pname);
     argv_free(list->patterns);
     myfree((void *) list->match_func);
     myfree((void *) list->match_args);
+    vstring_free(list->fold_buf);
     myfree((void *) list);
 }
index d51b6e831f50916c0af362efeea8731cdd782e93..d8b779430dc6013b1890d57ec8d3ba644c557fa7 100644 (file)
@@ -15,6 +15,7 @@
   * Utility library.
   */
 #include <argv.h>
+#include <vstring.h>
 
  /*
   * External interface.
@@ -24,11 +25,13 @@ typedef struct MATCH_LIST MATCH_LIST;
 typedef int (*MATCH_LIST_FN) (MATCH_LIST *, const char *, const char *);
 
 struct MATCH_LIST {
+    char   *pname;                     /* used in error messages */
     int     flags;                     /* processing options */
     ARGV   *patterns;                  /* one pattern each */
     int     match_count;               /* match function/argument count */
     MATCH_LIST_FN *match_func;         /* match functions */
     const char **match_args;           /* match arguments */
+    VSTRING *fold_buf;                 /* case-folded pattern string */
     int     error;                     /* last operation */
 };
 
@@ -37,7 +40,7 @@ struct MATCH_LIST {
 #define MATCH_FLAG_RETURN      (1<<1)
 #define MATCH_FLAG_ALL         (MATCH_FLAG_PARENT | MATCH_FLAG_RETURN)
 
-extern MATCH_LIST *match_list_init(int, const char *, int,...);
+extern MATCH_LIST *match_list_init(const char *, int, const char *, int,...);
 extern int match_list_match(MATCH_LIST *,...);
 extern void match_list_free(MATCH_LIST *);
 
@@ -61,4 +64,3 @@ extern int match_hostaddr(MATCH_LIST *, const char *, const char *);
 /*--*/
 
 #endif
-
index f978d2629b80597828b028e5eabfa576017ac97b..6542f49106a1a4c1fbc5168d339b6425d82a2254 100644 (file)
@@ -99,9 +99,9 @@ static int match_error(MATCH_LIST *list, const char *fmt,...)
     vstring_vsprintf(buf, fmt, ap);
     va_end(ap);
     if (list->flags & MATCH_FLAG_RETURN) {
-       msg_warn("%s", vstring_str(buf));
+       msg_warn("%s: %s", list->pname, vstring_str(buf));
     } else {
-       msg_fatal("%s", vstring_str(buf));
+       msg_fatal("%s: %s", list->pname, vstring_str(buf));
     }
     vstring_free(buf);
     return (0);
@@ -115,7 +115,7 @@ int     match_string(MATCH_LIST *list, const char *string, const char *pattern)
     DICT   *dict;
 
     if (msg_verbose)
-       msg_info("%s: %s ~? %s", myname, string, pattern);
+       msg_info("%s: %s: %s ~? %s", myname, list->pname, string, pattern);
 
     /*
      * Try dictionary lookup: exact match.
@@ -132,9 +132,10 @@ int     match_string(MATCH_LIST *list, const char *string, const char *pattern)
     }
 
     /*
-     * Try an exact string match.
+     * Try an exact string match. Note that the string and pattern are
+     * already casefolded.
      */
-    if (strcasecmp(string, pattern) == 0) {
+    if (strcmp(string, pattern) == 0) {
        return (1);
     }
 
@@ -156,7 +157,7 @@ int     match_hostname(MATCH_LIST *list, const char *name, const char *pattern)
     DICT   *dict;
 
     if (msg_verbose)
-       msg_info("%s: %s ~? %s", myname, name, pattern);
+       msg_info("%s: %s: %s ~? %s", myname, list->pname, name, pattern);
 
     /*
      * Try dictionary lookup: exact match and parent domains.
@@ -171,9 +172,9 @@ int     match_hostname(MATCH_LIST *list, const char *name, const char *pattern)
            if (entry == name || (dict->flags & DICT_FLAG_FIXED)) {
                match = (dict_get(dict, entry) != 0);
                if (msg_verbose > 1)
-                   msg_info("%s: lookup %s:%s %s: %s",
-                            myname, dict->type, dict->name, entry,
-                            match ? "found" : "notfound");
+                   msg_info("%s: %s: lookup %s:%s %s: %s",
+                            myname, list->pname, dict->type, dict->name,
+                            entry, match ? "found" : "notfound");
                if (match != 0)
                    break;
                if ((list->error = dict->error) != 0)
@@ -189,23 +190,25 @@ int     match_hostname(MATCH_LIST *list, const char *name, const char *pattern)
     }
 
     /*
-     * Try an exact match with the host name.
+     * Try an exact match with the host name. Note that the name and the
+     * pattern are already casefolded.
      */
-    if (strcasecmp(name, pattern) == 0) {
+    if (strcmp(name, pattern) == 0) {
        return (1);
     }
 
     /*
-     * See if the pattern is a parent domain of the hostname.
+     * See if the pattern is a parent domain of the hostname. Note that the
+     * name and the pattern are already casefolded.
      */
     else {
        if (list->flags & MATCH_FLAG_PARENT) {
            pd = name + strlen(name) - strlen(pattern);
-           if (pd > name && pd[-1] == '.' && strcasecmp(pd, pattern) == 0)
+           if (pd > name && pd[-1] == '.' && strcmp(pd, pattern) == 0)
                return (1);
        } else if (pattern[0] == '.') {
            pd = name + strlen(name) - strlen(pattern);
-           if (pd > name && strcasecmp(pd, pattern) == 0)
+           if (pd > name && strcmp(pd, pattern) == 0)
                return (1);
        }
     }
@@ -224,7 +227,7 @@ int     match_hostaddr(MATCH_LIST *list, const char *addr, const char *pattern)
     int     rc;
 
     if (msg_verbose)
-       msg_info("%s: %s ~? %s", myname, addr, pattern);
+       msg_info("%s: %s: %s ~? %s", myname, list->pname, addr, pattern);
 
 #define V4_ADDR_STRING_CHARS   "01234567890."
 #define V6_ADDR_STRING_CHARS   V4_ADDR_STRING_CHARS "abcdefABCDEF:"
@@ -247,15 +250,16 @@ int     match_hostaddr(MATCH_LIST *list, const char *addr, const char *pattern)
     }
 
     /*
-     * Try an exact match with the host address.
+     * Try an exact match with the host address. Note that the address and
+     * pattern are already casefolded.
      */
     if (pattern[0] != '[') {
-       if (strcasecmp(addr, pattern) == 0)
+       if (strcmp(addr, pattern) == 0)
            return (1);
     } else {
        size_t  addr_len = strlen(addr);
 
-       if (strncasecmp(addr, pattern + 1, addr_len) == 0
+       if (strncmp(addr, pattern + 1, addr_len) == 0
            && strcmp(pattern + 1 + addr_len, "]") == 0)
            return (1);
     }
@@ -271,7 +275,7 @@ int     match_hostaddr(MATCH_LIST *list, const char *addr, const char *pattern)
      * - Don't bother unless the pattern is either an IPv6 address or net/mask.
      * 
      * We can safely skip IPv4 address patterns because their form is
-     * unambiguous and they did not match in the strcasecmp() calls above.
+     * unambiguous and they did not match in the strcmp() calls above.
      * 
      * XXX We MUST skip (parent) domain names, which may appear in NAMADR_LIST
      * input, to avoid triggering false cidr_match_parse() errors.
diff --git a/postfix/src/util/midna.c b/postfix/src/util/midna.c
deleted file mode 100644 (file)
index 0f4118e..0000000
+++ /dev/null
@@ -1,335 +0,0 @@
-/*++
-/* NAME
-/*     midna 3
-/* SUMMARY
-/*     Postfix domain name conversion
-/* SYNOPSIS
-/*     #include <midna.h>
-/*
-/*     int midna_cache_size;
-/*
-/*     const char *midna_to_ascii(
-/*     const char *name)
-/*
-/*     const char *midna_to_utf8(
-/*     const char *name)
-/*
-/*     const char *midna_suffix_to_ascii(
-/*     const char *name)
-/*
-/*     const char *midna_suffix_to_utf8(
-/*     const char *name)
-/* DESCRIPTION
-/*     The functions in this module transform domain names from
-/*     or to IDNA form. The result is cached to avoid repeated
-/*     conversion of the same name.
-/*
-/*     midna_to_ascii() converts an UTF-8 or ASCII domain name to
-/*     ASCII.  The result is a null pointer in case of error.  This
-/*     function verifies that the result is a valid ASCII domainname.
-/*
-/*     midna_to_utf8() converts an UTF-8 or ASCII domain name to
-/*     UTF-8.  The result is a null pointer in case of error.  This
-/*     function verifies that the result converts to a valid ASCII
-/*     domainname.
-/*
-/*     midna_suffix_to_ascii() and midna_suffix_to_utf8() take a
-/*     name that starts with '.' and otherwise perform the same
-/*     operations as midna_to_ascii() and midna_to_utf8().
-/*
-/*     midna_cache_size specifies the size of the conversion result
-/*     cache.  This value is used only once, upon the first lookup
-/*     request.
-/* SEE ALSO
-/*     msg(3) diagnostics interface
-/* DIAGNOSTICS
-/*     Fatal errors: memory allocation problem.
-/*     Warnings: conversion error or result validation error.
-/* LICENSE
-/* .ad
-/* .fi
-/*     The Secure Mailer license must be distributed with this software.
-/* AUTHOR(S)
-/*     Arnt Gulbrandsen
-/*
-/*     Wietse Venema
-/*     IBM T.J. Watson Research
-/*     P.O. Box 704
-/*     Yorktown Heights, NY 10598, USA
-/*--*/
-
- /*
-  * System library.
-  */
-#include <sys_defs.h>
-#include <string.h>
-#include <ctype.h>
-
-#ifndef NO_EAI
-#include <unicode/uidna.h>
-
- /*
-  * Utility library.
-  */
-#include <mymalloc.h>
-#include <msg.h>
-#include <ctable.h>
-#include <stringops.h>
-#include <valid_hostname.h>
-#include <midna.h>
-
- /*
-  * Application-specific.
-  */
-#define DEF_MIDNA_CACHE_SIZE   256
-
-int     midna_cache_size = DEF_MIDNA_CACHE_SIZE;
-static VSTRING *midna_buf;             /* x.suffix */
-
-#define STR(x) vstring_str(x)
-
-/* midna_to_ascii_create - convert domain to ASCII */
-
-static void *midna_to_ascii_create(const char *name, void *unused_context)
-{
-    static const char myname[] = "midna_to_ascii_create";
-    char    buf[1024];                 /* XXX */
-    UErrorCode error = U_ZERO_ERROR;
-    UIDNAInfo info = UIDNA_INFO_INITIALIZER;
-    UIDNA  *idna;
-    int     anl;
-
-    /*
-     * Paranoia: do not expose uidna_*() to unfiltered network data.
-     */
-    if (allascii(name) == 0 && valid_utf8_string(name, strlen(name)) == 0) {
-       msg_warn("%s: Problem translating domain \"%s\" to ASCII form: %s",
-                myname, name, "malformed UTF-8");
-       return (0);
-    }
-
-    /*
-     * Perform the requested conversion.
-     */
-    idna = uidna_openUTS46(UIDNA_DEFAULT, &error);
-    anl = uidna_nameToASCII_UTF8(idna,
-                                name, strlen(name),
-                                buf, sizeof(buf),
-                                &info,
-                                &error);
-    uidna_close(idna);
-
-    /*
-     * Paranoia: verify that the result is a valid ASCII domain name. A quick
-     * check shows that the UTS46 implementation will reject labels that
-     * start or end in '-' or that are over-long, but let's play safe here.
-     */
-    if (U_SUCCESS(error) && info.errors == 0 && anl > 0) {
-       buf[anl] = 0;                           /* XXX */
-       if (!valid_hostname(buf, DONT_GRIPE)) {
-           msg_warn("%s: Problem translating domain \"%s\" to ASCII form: %s",
-                    myname, name, "malformed ASCII label(s)");
-           return (0);
-       }
-       return (mystrndup(buf, anl));
-    } else {
-       msg_warn("%s: Problem translating domain \"%s\" to ASCII form: %s",
-                myname, name, u_errorName(error));
-       return (0);
-    }
-}
-
-/* midna_to_utf8_create - convert domain to UTF8 */
-
-static void *midna_to_utf8_create(const char *name, void *unused_context)
-{
-    static const char myname[] = "midna_to_utf8_create";
-    char    buf[1024];                 /* XXX */
-    UErrorCode error = U_ZERO_ERROR;
-    UIDNAInfo info = UIDNA_INFO_INITIALIZER;
-    UIDNA  *idna;
-    int     anl;
-
-    /*
-     * Paranoia: do not expose uidna_*() to unfiltered network data.
-     */
-    if (allascii(name) == 0 && valid_utf8_string(name, strlen(name)) == 0) {
-       msg_warn("%s: Problem translating domain \"%s\" to UTF-8 form: %s",
-                myname, name, "malformed UTF-8");
-       return (0);
-    }
-
-    /*
-     * Perform the requested conversion.
-     */
-    idna = uidna_openUTS46(UIDNA_DEFAULT, &error);
-    anl = uidna_nameToUnicodeUTF8(idna,
-                                 name, strlen(name),
-                                 buf, sizeof(buf),
-                                 &info,
-                                 &error);
-    uidna_close(idna);
-
-    /*
-     * Paranoia: UTS46 toUTF8 will accept and produce a name that does not
-     * convert to a valid ASCII domain name. So we enforce sanity here.
-     */
-    if (U_SUCCESS(error) && info.errors == 0 && anl > 0) {
-       buf[anl] = 0;                           /* XXX */
-       if (midna_to_ascii(buf) == 0)
-           return (0);
-       return (mystrndup(buf, anl));
-    } else {
-       msg_warn("%s: Problem translating domain \"%s\" to UTF8 form: %s",
-                myname, name, u_errorName(error));
-       return (0);
-    }
-}
-
-/* midna_cache_free - cache element destructor */
-
-static void midna_cache_free(void *value, void *unused_context)
-{
-    if (value)
-       myfree(value);
-}
-
-/* midna_to_ascii - convert name to ASCII */
-
-const char *midna_to_ascii(const char *name)
-{
-    static CTABLE *midna_to_ascii_cache = 0;
-
-    if (midna_to_ascii_cache == 0)
-       midna_to_ascii_cache = ctable_create(midna_cache_size,
-                                            midna_to_ascii_create,
-                                            midna_cache_free,
-                                            (void *) 0);
-    return (ctable_locate(midna_to_ascii_cache, name));
-}
-
-/* midna_to_utf8 - convert name to UTF8 */
-
-const char *midna_to_utf8(const char *name)
-{
-    static CTABLE *midna_to_utf8_cache = 0;
-
-    if (midna_to_utf8_cache == 0)
-       midna_to_utf8_cache = ctable_create(midna_cache_size,
-                                           midna_to_utf8_create,
-                                           midna_cache_free,
-                                           (void *) 0);
-    return (ctable_locate(midna_to_utf8_cache, name));
-}
-
-/* midna_suffix_to_ascii - convert .name to ASCII */
-
-const char *midna_suffix_to_ascii(const char *suffix)
-{
-    const char *cache_res;
-
-    /*
-     * If prepending x to .name causes the result to become too long, then
-     * the suffix is bad.
-     */
-    if (midna_buf == 0)
-       midna_buf = vstring_alloc(100);
-    vstring_sprintf(midna_buf, "x%s", suffix);
-    if ((cache_res = midna_to_ascii(STR(midna_buf))) == 0)
-       return (0);
-    else
-       return (cache_res + 1);
-}
-
-/* midna_suffix_to_utf8 - convert .name to UTF8 */
-
-const char *midna_suffix_to_utf8(const char *name)
-{
-    const char *cache_res;
-
-    /*
-     * If prepending x to .name causes the result to become too long, then
-     * the suffix is bad.
-     */
-    if (midna_buf == 0)
-       midna_buf = vstring_alloc(100);
-    vstring_sprintf(midna_buf, "x%s", name);
-    if ((cache_res = midna_to_utf8(STR(midna_buf))) == 0)
-       return (0);
-    else
-       return (cache_res + 1);
-}
-
-#ifdef TEST
-
- /*
-  * Test program - reads names from stdin, reports invalid names to stderr.
-  */
-#include <stdlib.h>
-#include <locale.h>
-
-#include <stringops.h>                 /* XXX temp_utf8_kludge */
-#include <vstring.h>
-#include <vstream.h>
-#include <vstring_vstream.h>
-#include <msg_vstream.h>
-
-int     main(int argc, char **argv)
-{
-    VSTRING *buffer = vstring_alloc(1);
-    const char *bp;
-    const char *ascii;
-    const char *utf8;
-
-    if (setlocale(LC_ALL, "C") == 0)
-       msg_fatal("setlocale(LC_ALL, C) failed: %m");
-
-    msg_vstream_init(argv[0], VSTREAM_ERR);
-    msg_verbose = 1;
-    temp_utf8_kludge = 1;
-
-    while (vstring_fgets_nonl(buffer, VSTREAM_IN)) {
-       bp = STR(buffer);
-       msg_info("> %s", bp);
-       while (ISSPACE(*bp))
-           bp++;
-       if (*bp == '#' || *bp == 0)
-           continue;
-       if (!allascii(bp)) {
-           utf8 = midna_to_utf8(bp);
-           if (utf8 != 0)
-               msg_info("\"%s\" ->utf8 \"%s\"", bp, utf8);
-           ascii = midna_to_ascii(bp);
-           if (ascii != 0) {
-               msg_info("\"%s\" ->ascii \"%s\"", bp, ascii);
-               utf8 = midna_to_utf8(ascii);
-               if (utf8 != 0) {
-                   msg_info("\"%s\" ->ascii \"%s\" ->utf8 \"%s\"",
-                            bp, ascii, utf8);
-                   if (strcmp(utf8, bp) != 0)
-                       msg_warn("\"%s\" != \"%s\"", bp, utf8);
-               }
-           }
-       } else {
-           ascii = midna_to_ascii(bp);
-           if (ascii != 0)
-               msg_info("\"%s\" ->ascii \"%s\"", bp, ascii);
-           utf8 = midna_to_utf8(bp);
-           if (utf8 != 0) {
-               msg_info("\"%s\" ->utf8 \"%s\"", bp, utf8);
-               ascii = midna_to_ascii(utf8);
-               if (ascii != 0) {
-                   msg_info("\"%s\" ->utf8 \"%s\" ->ascii \"%s\"",
-                            bp, utf8, ascii);
-                   if (strcmp(ascii, bp) != 0)
-                       msg_warn("\"%s\" != \"%s\"", bp, ascii);
-               }
-           }
-       }
-    }
-    exit(0);
-}
-
-#endif                                 /* TEST */
-
-#endif                                 /* NO_EAI */
diff --git a/postfix/src/util/midna_domain.c b/postfix/src/util/midna_domain.c
new file mode 100644 (file)
index 0000000..1563cac
--- /dev/null
@@ -0,0 +1,345 @@
+/*++
+/* NAME
+/*     midna_domain 3
+/* SUMMARY
+/*     ASCII/UTF-8 domain name conversion
+/* SYNOPSIS
+/*     #include <midna_domain.h>
+/*
+/*     int midna_domain_cache_size;
+/*
+/*     const char *midna_domain_to_ascii(
+/*     const char *name)
+/*
+/*     const char *midna_domain_to_utf8(
+/*     const char *name)
+/*
+/*     const char *midna_domain_suffix_to_ascii(
+/*     const char *name)
+/*
+/*     const char *midna_domain_suffix_to_utf8(
+/*     const char *name)
+/* DESCRIPTION
+/*     The functions in this module transform domain names from/to
+/*     ASCII and UTF-8 form. The result is cached to avoid repeated
+/*     conversion.
+/*
+/*     This module builds on the ICU library implementation of the
+/*     UTS #46 specification, using default ICU library options
+/*     because those are likely best tested: with transitional
+/*     processing, with case mapping, with normalization, with
+/*     limited IDNA2003 compatibility, without STD3 ASCII rules.
+/*
+/*     midna_domain_to_ascii() converts an UTF-8 or ASCII domain
+/*     name to ASCII.  The result is a null pointer in case of
+/*     error.  This function verifies that the result passes
+/*     valid_hostname().
+/*
+/*     midna_domain_to_utf8() converts an UTF-8 or ASCII domain
+/*     name to UTF-8.  The result is a null pointer in case of
+/*     error.  This function verifies that the result, after
+/*     conversion to ASCII, passes valid_hostname().
+/*
+/*     midna_domain_suffix_to_ascii() and midna_domain_suffix_to_utf8()
+/*     take a name that starts with '.' and otherwise perform the
+/*     same operations as midna_domain_to_ascii() and
+/*     midna_domain_to_utf8().
+/*
+/*     midna_domain_cache_size specifies the size of the conversion
+/*     result cache.  This value is used only once, upon the first
+/*     lookup
+/*     request.
+/* SEE ALSO
+/*     http://unicode.org/reports/tr46/ Unicode IDNA Compatibility processing
+/*     msg(3) diagnostics interface
+/* DIAGNOSTICS
+/*     Fatal errors: memory allocation problem.
+/*     Warnings: conversion error or result validation error.
+/* LICENSE
+/* .ad
+/* .fi
+/*     The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/*     Arnt Gulbrandsen
+/*
+/*     Wietse Venema
+/*     IBM T.J. Watson Research
+/*     P.O. Box 704
+/*     Yorktown Heights, NY 10598, USA
+/*--*/
+
+ /*
+  * System library.
+  */
+#include <sys_defs.h>
+#include <string.h>
+#include <ctype.h>
+
+#ifndef NO_EAI
+#include <unicode/uidna.h>
+
+ /*
+  * Utility library.
+  */
+#include <mymalloc.h>
+#include <msg.h>
+#include <ctable.h>
+#include <stringops.h>
+#include <valid_hostname.h>
+#include <midna_domain.h>
+
+ /*
+  * Application-specific.
+  */
+#define DEF_MIDNA_CACHE_SIZE   256
+
+int     midna_domain_cache_size = DEF_MIDNA_CACHE_SIZE;
+static VSTRING *midna_domain_buf;      /* x.suffix */
+
+#define STR(x) vstring_str(x)
+
+/* midna_domain_to_ascii_create - convert domain to ASCII */
+
+static void *midna_domain_to_ascii_create(const char *name, void *unused_context)
+{
+    static const char myname[] = "midna_domain_to_ascii_create";
+    char    buf[1024];                 /* XXX */
+    UErrorCode error = U_ZERO_ERROR;
+    UIDNAInfo info = UIDNA_INFO_INITIALIZER;
+    UIDNA  *idna;
+    int     anl;
+
+    /*
+     * Paranoia: do not expose uidna_*() to unfiltered network data.
+     */
+    if (allascii(name) == 0 && valid_utf8_string(name, strlen(name)) == 0) {
+       msg_warn("%s: Problem translating domain \"%.100s\" to ASCII form: %s",
+                myname, name, "malformed UTF-8");
+       return (0);
+    }
+
+    /*
+     * Perform the requested conversion.
+     */
+    idna = uidna_openUTS46(UIDNA_DEFAULT, &error);/* XXX check error */
+    anl = uidna_nameToASCII_UTF8(idna,
+                                name, strlen(name),
+                                buf, sizeof(buf) - 1,
+                                &info,
+                                &error);
+    uidna_close(idna);
+
+    /*
+     * Paranoia: verify that the result passes valid_hostname(). A quick
+     * check shows that UTS46 ToASCII by default rejects inputs with labels
+     * that start or end in '-', with names or labels that are over-long, or
+     * "fake" A-labels, as required by UTS 46 section 4.1, but we rely on
+     * valid_hostname() on the output side just to be sure.
+     */
+    if (U_SUCCESS(error) && info.errors == 0 && anl > 0) {
+       buf[anl] = 0;                           /* XXX */
+       if (!valid_hostname(buf, DONT_GRIPE)) {
+           msg_warn("%s: Problem translating domain \"%.100s\" to ASCII form: %s",
+                    myname, name, "malformed ASCII label(s)");
+           return (0);
+       }
+       return (mystrndup(buf, anl));
+    } else {
+       msg_warn("%s: Problem translating domain \"%.100s\" to ASCII form: %s",
+                myname, name, u_errorName(info.errors));
+       return (0);
+    }
+}
+
+/* midna_domain_to_utf8_create - convert domain to UTF8 */
+
+static void *midna_domain_to_utf8_create(const char *name, void *unused_context)
+{
+    static const char myname[] = "midna_domain_to_utf8_create";
+    char    buf[1024];                 /* XXX */
+    UErrorCode error = U_ZERO_ERROR;
+    UIDNAInfo info = UIDNA_INFO_INITIALIZER;
+    UIDNA  *idna;
+    int     anl;
+
+    /*
+     * Paranoia: do not expose uidna_*() to unfiltered network data.
+     */
+    if (allascii(name) == 0 && valid_utf8_string(name, strlen(name)) == 0) {
+       msg_warn("%s: Problem translating domain \"%.100s\" to UTF-8 form: %s",
+                myname, name, "malformed UTF-8");
+       return (0);
+    }
+
+    /*
+     * Perform the requested conversion.
+     */
+    idna = uidna_openUTS46(UIDNA_DEFAULT, &error);/* XXX check error */
+    anl = uidna_nameToUnicodeUTF8(idna,
+                                 name, strlen(name),
+                                 buf, sizeof(buf) - 1,
+                                 &info,
+                                 &error);
+    uidna_close(idna);
+
+    /*
+     * Paranoia: UTS46 toUTF8 by default accepts and produces an over-long
+     * name or a name that contains an over-long NR-LDH label (and perhaps
+     * other invalid forms that are not covered in UTS 46, section 4.1). We
+     * rely on midna_domain_to_ascii() to validate the output.
+     */
+    if (U_SUCCESS(error) && info.errors == 0 && anl > 0) {
+       buf[anl] = 0;                           /* XXX */
+       if (midna_domain_to_ascii(buf) == 0)
+           return (0);
+       return (mystrndup(buf, anl));
+    } else {
+       msg_warn("%s: Problem translating domain \"%.100s\" to UTF8 form: %s",
+                myname, name, u_errorName(info.errors));
+       return (0);
+    }
+}
+
+/* midna_domain_cache_free - cache element destructor */
+
+static void midna_domain_cache_free(void *value, void *unused_context)
+{
+    if (value)
+       myfree(value);
+}
+
+/* midna_domain_to_ascii - convert name to ASCII */
+
+const char *midna_domain_to_ascii(const char *name)
+{
+    static CTABLE *midna_domain_to_ascii_cache = 0;
+
+    if (midna_domain_to_ascii_cache == 0)
+       midna_domain_to_ascii_cache = ctable_create(midna_domain_cache_size,
+                                              midna_domain_to_ascii_create,
+                                                   midna_domain_cache_free,
+                                                   (void *) 0);
+    return (ctable_locate(midna_domain_to_ascii_cache, name));
+}
+
+/* midna_domain_to_utf8 - convert name to UTF8 */
+
+const char *midna_domain_to_utf8(const char *name)
+{
+    static CTABLE *midna_domain_to_utf8_cache = 0;
+
+    if (midna_domain_to_utf8_cache == 0)
+       midna_domain_to_utf8_cache = ctable_create(midna_domain_cache_size,
+                                               midna_domain_to_utf8_create,
+                                                  midna_domain_cache_free,
+                                                  (void *) 0);
+    return (ctable_locate(midna_domain_to_utf8_cache, name));
+}
+
+/* midna_domain_suffix_to_ascii - convert .name to ASCII */
+
+const char *midna_domain_suffix_to_ascii(const char *suffix)
+{
+    const char *cache_res;
+
+    /*
+     * If prepending x to .name causes the result to become too long, then
+     * the suffix is bad.
+     */
+    if (midna_domain_buf == 0)
+       midna_domain_buf = vstring_alloc(100);
+    vstring_sprintf(midna_domain_buf, "x%s", suffix);
+    if ((cache_res = midna_domain_to_ascii(STR(midna_domain_buf))) == 0)
+       return (0);
+    else
+       return (cache_res + 1);
+}
+
+/* midna_domain_suffix_to_utf8 - convert .name to UTF8 */
+
+const char *midna_domain_suffix_to_utf8(const char *name)
+{
+    const char *cache_res;
+
+    /*
+     * If prepending x to .name causes the result to become too long, then
+     * the suffix is bad.
+     */
+    if (midna_domain_buf == 0)
+       midna_domain_buf = vstring_alloc(100);
+    vstring_sprintf(midna_domain_buf, "x%s", name);
+    if ((cache_res = midna_domain_to_utf8(STR(midna_domain_buf))) == 0)
+       return (0);
+    else
+       return (cache_res + 1);
+}
+
+#ifdef TEST
+
+ /*
+  * Test program - reads names from stdin, reports invalid names to stderr.
+  */
+#include <stdlib.h>
+#include <locale.h>
+
+#include <stringops.h>                 /* XXX util_utf8_enable */
+#include <vstring.h>
+#include <vstream.h>
+#include <vstring_vstream.h>
+#include <msg_vstream.h>
+
+int     main(int argc, char **argv)
+{
+    VSTRING *buffer = vstring_alloc(1);
+    const char *bp;
+    const char *ascii;
+    const char *utf8;
+
+    if (setlocale(LC_ALL, "C") == 0)
+       msg_fatal("setlocale(LC_ALL, C) failed: %m");
+
+    msg_vstream_init(argv[0], VSTREAM_ERR);
+    /* msg_verbose = 1; */
+    util_utf8_enable = 1;
+
+    while (vstring_fgets_nonl(buffer, VSTREAM_IN)) {
+       bp = STR(buffer);
+       msg_info("> %s", bp);
+       while (ISSPACE(*bp))
+           bp++;
+       if (*bp == '#' || *bp == 0)
+           continue;
+       msg_info("unconditional conversions:");
+       utf8 = midna_domain_to_utf8(bp);
+       msg_info("\"%s\" ->utf8 \"%s\"", bp, utf8 ? utf8 : "(error)");
+       ascii = midna_domain_to_ascii(bp);
+       msg_info("\"%s\" ->ascii \"%s\"", bp, ascii ? ascii : "(error)");
+       msg_info("conditional conversions:");
+       if (!allascii(bp)) {
+           if (ascii != 0) {
+               utf8 = midna_domain_to_utf8(ascii);
+               msg_info("\"%s\" ->ascii \"%s\" ->utf8 \"%s\"",
+                        bp, ascii, utf8 ? utf8 : "(error)");
+               if (utf8 != 0) {
+                   if (strcmp(utf8, bp) != 0)
+                       msg_warn("\"%s\" != \"%s\"", bp, utf8);
+               }
+           }
+       } else {
+           if (utf8 != 0) {
+               ascii = midna_domain_to_ascii(utf8);
+               msg_info("\"%s\" ->utf8 \"%s\" ->ascii \"%s\"",
+                        bp, utf8, ascii ? ascii : "(error)");
+               if (ascii != 0) {
+                   if (strcmp(ascii, bp) != 0)
+                       msg_warn("\"%s\" != \"%s\"", bp, ascii);
+               }
+           }
+       }
+    }
+    exit(0);
+}
+
+#endif                                 /* TEST */
+
+#endif                                 /* NO_EAI */
similarity index 54%
rename from postfix/src/util/midna.h
rename to postfix/src/util/midna_domain.h
index 5990978327c2fdb753a62fa5cbb9239be8ab5806..29cfc8c845dfa24b51b6413c0fef46b4fef15072 100644 (file)
@@ -3,21 +3,21 @@
 
 /*++
 /* NAME
-/*     mail_idna 3h
+/*     midna_domain 3h
 /* SUMMARY
-/*     domain name conversion
+/*     ASCII/UTF-8 domain name conversion
 /* SYNOPSIS
-/*     #include <mail_idna.h>
+/*     #include <midna_domain.h>
 /* DESCRIPTION
 /* .nf
 
  /*
   * External interface.
   */
-extern const char *midna_to_ascii(const char *);
-extern const char *midna_to_utf8(const char *);
-extern const char *midna_suffix_to_ascii(const char *);
-extern const char *midna_suffix_to_utf8(const char *);
+extern const char *midna_domain_to_ascii(const char *);
+extern const char *midna_domain_to_utf8(const char *);
+extern const char *midna_domain_suffix_to_ascii(const char *);
+extern const char *midna_domain_suffix_to_utf8(const char *);
 
 /* LICENSE
 /* .ad
diff --git a/postfix/src/util/midna_domain_test.in b/postfix/src/util/midna_domain_test.in
new file mode 100644 (file)
index 0000000..55491e4
--- /dev/null
@@ -0,0 +1,21 @@
+# Upper-case greek -> lower-case greek.
+Δημοσθένους.example.com
+# Upper-case ASCII -> lower-case ASCII.
+Hello.example.com
+# Invalid LDH label('-' at begin or end).
+bad-.example.com
+-bad.example.com
+# Invalid LDH (label > 63 bytes).
+abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789.example.com
+# Valid LDH label (label <= 63 bytes).
+abcdef0123456789abcdef0123456789abcdef0123456789abcdef012345678.example.com
+# Invalid name (length > 255 bytes).
+abcdef0123456789abcdef0123456789abcdef0123456789abcdef01234567.abcdef0123456789abcdef0123456789abcdef0123456789abcdef01234567.abcdef0123456789abcdef0123456789abcdef0123456789abcdef01234567.abcdef0123456789abcdef0123456789abcdef0123456789abcdef01234567.example.com
+# Aliases for '.' -> '.'.
+x。example.com
+x.example.com
+x。example.com
+# Good a-label.
+xn--mumble.example.com
+# Bad a-label.
+xn--123456.example.com
diff --git a/postfix/src/util/midna_domain_test.ref b/postfix/src/util/midna_domain_test.ref
new file mode 100644 (file)
index 0000000..17e4fcc
--- /dev/null
@@ -0,0 +1,89 @@
+./midna_domain: > # Upper-case greek -> lower-case greek.
+./midna_domain: > Δημοσθένους.example.com
+./midna_domain: unconditional conversions:
+./midna_domain: "Δημοσθένους.example.com" ->utf8 "δημοσθένουσ.example.com"
+./midna_domain: "Δημοσθένους.example.com" ->ascii "xn--ixanjetild6aev.example.com"
+./midna_domain: conditional conversions:
+./midna_domain: "Δημοσθένους.example.com" ->ascii "xn--ixanjetild6aev.example.com" ->utf8 "δημοσθένουσ.example.com"
+./midna_domain: warning: "Δημοσθένους.example.com" != "δημοσθένουσ.example.com"
+./midna_domain: > # Upper-case ASCII -> lower-case ASCII.
+./midna_domain: > Hello.example.com
+./midna_domain: unconditional conversions:
+./midna_domain: "Hello.example.com" ->utf8 "hello.example.com"
+./midna_domain: "Hello.example.com" ->ascii "hello.example.com"
+./midna_domain: conditional conversions:
+./midna_domain: "Hello.example.com" ->utf8 "hello.example.com" ->ascii "hello.example.com"
+./midna_domain: warning: "Hello.example.com" != "hello.example.com"
+./midna_domain: > # Invalid LDH label('-' at begin or end).
+./midna_domain: > bad-.example.com
+./midna_domain: unconditional conversions:
+./midna_domain: warning: midna_domain_to_utf8_create: Problem translating domain "bad-.example.com" to UTF8 form: U_UNSUPPORTED_ERROR
+./midna_domain: "bad-.example.com" ->utf8 "(error)"
+./midna_domain: warning: midna_domain_to_ascii_create: Problem translating domain "bad-.example.com" to ASCII form: U_UNSUPPORTED_ERROR
+./midna_domain: "bad-.example.com" ->ascii "(error)"
+./midna_domain: conditional conversions:
+./midna_domain: > -bad.example.com
+./midna_domain: unconditional conversions:
+./midna_domain: warning: midna_domain_to_utf8_create: Problem translating domain "-bad.example.com" to UTF8 form: U_INDEX_OUTOFBOUNDS_ERROR
+./midna_domain: "-bad.example.com" ->utf8 "(error)"
+./midna_domain: warning: midna_domain_to_ascii_create: Problem translating domain "-bad.example.com" to ASCII form: U_INDEX_OUTOFBOUNDS_ERROR
+./midna_domain: "-bad.example.com" ->ascii "(error)"
+./midna_domain: conditional conversions:
+./midna_domain: > # Invalid LDH (label > 63 bytes).
+./midna_domain: > abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789.example.com
+./midna_domain: unconditional conversions:
+./midna_domain: warning: midna_domain_to_ascii_create: Problem translating domain "abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789.example.com" to ASCII form: U_MISSING_RESOURCE_ERROR
+./midna_domain: "abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789.example.com" ->utf8 "(error)"
+./midna_domain: "abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789.example.com" ->ascii "(error)"
+./midna_domain: conditional conversions:
+./midna_domain: > # Valid LDH label (label <= 63 bytes).
+./midna_domain: > abcdef0123456789abcdef0123456789abcdef0123456789abcdef012345678.example.com
+./midna_domain: unconditional conversions:
+./midna_domain: "abcdef0123456789abcdef0123456789abcdef0123456789abcdef012345678.example.com" ->utf8 "abcdef0123456789abcdef0123456789abcdef0123456789abcdef012345678.example.com"
+./midna_domain: "abcdef0123456789abcdef0123456789abcdef0123456789abcdef012345678.example.com" ->ascii "abcdef0123456789abcdef0123456789abcdef0123456789abcdef012345678.example.com"
+./midna_domain: conditional conversions:
+./midna_domain: "abcdef0123456789abcdef0123456789abcdef0123456789abcdef012345678.example.com" ->utf8 "abcdef0123456789abcdef0123456789abcdef0123456789abcdef012345678.example.com" ->ascii "abcdef0123456789abcdef0123456789abcdef0123456789abcdef012345678.example.com"
+./midna_domain: > # Invalid name (length > 255 bytes).
+./midna_domain: > abcdef0123456789abcdef0123456789abcdef0123456789abcdef01234567.abcdef0123456789abcdef0123456789abcdef0123456789abcdef01234567.abcdef0123456789abcdef0123456789abcdef0123456789abcdef01234567.abcdef0123456789abcdef0123456789abcdef0123456789abcdef01234567.example.com
+./midna_domain: unconditional conversions:
+./midna_domain: warning: midna_domain_to_ascii_create: Problem translating domain "abcdef0123456789abcdef0123456789abcdef0123456789abcdef01234567.abcdef0123456789abcdef0123456789abcde" to ASCII form: U_FILE_ACCESS_ERROR
+./midna_domain: "abcdef0123456789abcdef0123456789abcdef0123456789abcdef01234567.abcdef0123456789abcdef0123456789abcdef0123456789abcdef01234567.abcdef0123456789abcdef0123456789abcdef0123456789abcdef01234567.abcdef0123456789abcdef0123456789abcdef0123456789abcdef01234567.example.com" ->utf8 "(error)"
+./midna_domain: "abcdef0123456789abcdef0123456789abcdef0123456789abcdef01234567.abcdef0123456789abcdef0123456789abcdef0123456789abcdef01234567.abcdef0123456789abcdef0123456789abcdef0123456789abcdef01234567.abcdef0123456789abcdef0123456789abcdef0123456789abcdef01234567.example.com" ->ascii "(error)"
+./midna_domain: conditional conversions:
+./midna_domain: > # Aliases for '.' -> '.'.
+./midna_domain: > x。example.com
+./midna_domain: unconditional conversions:
+./midna_domain: "x。example.com" ->utf8 "x.example.com"
+./midna_domain: "x。example.com" ->ascii "x.example.com"
+./midna_domain: conditional conversions:
+./midna_domain: "x。example.com" ->ascii "x.example.com" ->utf8 "x.example.com"
+./midna_domain: warning: "x。example.com" != "x.example.com"
+./midna_domain: > x.example.com
+./midna_domain: unconditional conversions:
+./midna_domain: "x.example.com" ->utf8 "x.example.com"
+./midna_domain: "x.example.com" ->ascii "x.example.com"
+./midna_domain: conditional conversions:
+./midna_domain: "x.example.com" ->ascii "x.example.com" ->utf8 "x.example.com"
+./midna_domain: warning: "x.example.com" != "x.example.com"
+./midna_domain: > x。example.com
+./midna_domain: unconditional conversions:
+./midna_domain: "x。example.com" ->utf8 "x.example.com"
+./midna_domain: "x。example.com" ->ascii "x.example.com"
+./midna_domain: conditional conversions:
+./midna_domain: "x。example.com" ->ascii "x.example.com" ->utf8 "x.example.com"
+./midna_domain: warning: "x。example.com" != "x.example.com"
+./midna_domain: > # Good a-label.
+./midna_domain: > xn--mumble.example.com
+./midna_domain: unconditional conversions:
+./midna_domain: "xn--mumble.example.com" ->utf8 "㲹㲺㲵㲴.example.com"
+./midna_domain: "xn--mumble.example.com" ->ascii "xn--mumble.example.com"
+./midna_domain: conditional conversions:
+./midna_domain: "xn--mumble.example.com" ->utf8 "㲹㲺㲵㲴.example.com" ->ascii "xn--mumble.example.com"
+./midna_domain: > # Bad a-label.
+./midna_domain: > xn--123456.example.com
+./midna_domain: unconditional conversions:
+./midna_domain: warning: midna_domain_to_utf8_create: Problem translating domain "xn--123456.example.com" to UTF8 form: [BOGUS UErrorCode]
+./midna_domain: "xn--123456.example.com" ->utf8 "(error)"
+./midna_domain: warning: midna_domain_to_ascii_create: Problem translating domain "xn--123456.example.com" to ASCII form: [BOGUS UErrorCode]
+./midna_domain: "xn--123456.example.com" ->ascii "(error)"
+./midna_domain: conditional conversions:
diff --git a/postfix/src/util/midna_test.in b/postfix/src/util/midna_test.in
deleted file mode 100644 (file)
index b309be9..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-# Upper-case greek -> lower-case greek.
-Δημοσθένους.example.com
-# Upper-case ASCII -> lower-case ASCII.
-Hello.example.com
-# Invalid domain name ('-' at begin or end).
--bad-.example.com
-# Invalid domain name (label > 62 bytes).
-abcdef01234567890abcdef01234567890abcdef01234567890abcdef0123456789.example.com
-# Valid domain name.
-abcdef01234567890abcdef01234567890abcdef01234567890abcdef012345.example.com
-# Aliases for '.' -> '.'.
-x。example.com
-x.example.com
-x。example.com
diff --git a/postfix/src/util/midna_test.ref b/postfix/src/util/midna_test.ref
deleted file mode 100644 (file)
index 1df1eb6..0000000
+++ /dev/null
@@ -1,71 +0,0 @@
-./midna: > # Upper-case greek -> lower-case greek.
-./midna: > Δημοσθένους.example.com
-./midna: ctable_locate: install entry key δημοσθένουσ.example.com
-./midna: ctable_locate: install entry key Δημοσθένους.example.com
-./midna: "Δημοσθένους.example.com" ->utf8 "δημοσθένουσ.example.com"
-./midna: ctable_locate: install entry key Δημοσθένους.example.com
-./midna: "Δημοσθένους.example.com" ->ascii "xn--ixanjetild6aev.example.com"
-./midna: ctable_locate: move existing entry key δημοσθένουσ.example.com
-./midna: ctable_locate: install entry key xn--ixanjetild6aev.example.com
-./midna: "Δημοσθένους.example.com" ->ascii "xn--ixanjetild6aev.example.com" ->utf8 "δημοσθένουσ.example.com"
-./midna: warning: "Δημοσθένους.example.com" != "δημοσθένουσ.example.com"
-./midna: > # Upper-case ASCII -> lower-case ASCII.
-./midna: > Hello.example.com
-./midna: ctable_locate: install entry key Hello.example.com
-./midna: "Hello.example.com" ->ascii "hello.example.com"
-./midna: ctable_locate: install entry key hello.example.com
-./midna: ctable_locate: install entry key Hello.example.com
-./midna: "Hello.example.com" ->utf8 "hello.example.com"
-./midna: ctable_locate: leave existing entry key hello.example.com
-./midna: "Hello.example.com" ->utf8 "hello.example.com" ->ascii "hello.example.com"
-./midna: warning: "Hello.example.com" != "hello.example.com"
-./midna: > # Invalid domain name ('-' at begin or end).
-./midna: > -bad-.example.com
-./midna: warning: midna_to_ascii_create: Problem translating domain "-bad-.example.com" to ASCII form: U_ZERO_ERROR
-./midna: ctable_locate: install entry key -bad-.example.com
-./midna: warning: midna_to_utf8_create: Problem translating domain "-bad-.example.com" to UTF8 form: U_ZERO_ERROR
-./midna: ctable_locate: install entry key -bad-.example.com
-./midna: > # Invalid domain name (label > 62 bytes).
-./midna: > abcdef01234567890abcdef01234567890abcdef01234567890abcdef0123456789.example.com
-./midna: warning: midna_to_ascii_create: Problem translating domain "abcdef01234567890abcdef01234567890abcdef01234567890abcdef0123456789.example.com" to ASCII form: U_ZERO_ERROR
-./midna: ctable_locate: install entry key abcdef01234567890abcdef01234567890abcdef01234567890abcdef0123456789.example.com
-./midna: ctable_locate: leave existing entry key abcdef01234567890abcdef01234567890abcdef01234567890abcdef0123456789.example.com
-./midna: ctable_locate: install entry key abcdef01234567890abcdef01234567890abcdef01234567890abcdef0123456789.example.com
-./midna: > # Valid domain name.
-./midna: > abcdef01234567890abcdef01234567890abcdef01234567890abcdef012345.example.com
-./midna: ctable_locate: install entry key abcdef01234567890abcdef01234567890abcdef01234567890abcdef012345.example.com
-./midna: "abcdef01234567890abcdef01234567890abcdef01234567890abcdef012345.example.com" ->ascii "abcdef01234567890abcdef01234567890abcdef01234567890abcdef012345.example.com"
-./midna: ctable_locate: leave existing entry key abcdef01234567890abcdef01234567890abcdef01234567890abcdef012345.example.com
-./midna: ctable_locate: install entry key abcdef01234567890abcdef01234567890abcdef01234567890abcdef012345.example.com
-./midna: "abcdef01234567890abcdef01234567890abcdef01234567890abcdef012345.example.com" ->utf8 "abcdef01234567890abcdef01234567890abcdef01234567890abcdef012345.example.com"
-./midna: ctable_locate: leave existing entry key abcdef01234567890abcdef01234567890abcdef01234567890abcdef012345.example.com
-./midna: "abcdef01234567890abcdef01234567890abcdef01234567890abcdef012345.example.com" ->utf8 "abcdef01234567890abcdef01234567890abcdef01234567890abcdef012345.example.com" ->ascii "abcdef01234567890abcdef01234567890abcdef01234567890abcdef012345.example.com"
-./midna: > # Aliases for '.' -> '.'.
-./midna: > x。example.com
-./midna: ctable_locate: install entry key x.example.com
-./midna: ctable_locate: install entry key x。example.com
-./midna: "x。example.com" ->utf8 "x.example.com"
-./midna: ctable_locate: install entry key x。example.com
-./midna: "x。example.com" ->ascii "x.example.com"
-./midna: ctable_locate: move existing entry key x.example.com
-./midna: ctable_locate: install entry key x.example.com
-./midna: "x。example.com" ->ascii "x.example.com" ->utf8 "x.example.com"
-./midna: warning: "x。example.com" != "x.example.com"
-./midna: > x.example.com
-./midna: ctable_locate: leave existing entry key x.example.com
-./midna: ctable_locate: install entry key x.example.com
-./midna: "x.example.com" ->utf8 "x.example.com"
-./midna: ctable_locate: install entry key x.example.com
-./midna: "x.example.com" ->ascii "x.example.com"
-./midna: ctable_locate: move existing entry key x.example.com
-./midna: "x.example.com" ->ascii "x.example.com" ->utf8 "x.example.com"
-./midna: warning: "x.example.com" != "x.example.com"
-./midna: > x。example.com
-./midna: ctable_locate: move existing entry key x.example.com
-./midna: ctable_locate: install entry key x。example.com
-./midna: "x。example.com" ->utf8 "x.example.com"
-./midna: ctable_locate: install entry key x。example.com
-./midna: "x。example.com" ->ascii "x.example.com"
-./midna: ctable_locate: move existing entry key x.example.com
-./midna: "x。example.com" ->ascii "x.example.com" ->utf8 "x.example.com"
-./midna: warning: "x。example.com" != "x.example.com"
index 1ba0731c7e1510740f4683f3dd182368116a14f8..a37ff6306410478105b3416ed31b6407cd69978d 100644 (file)
@@ -6,7 +6,7 @@
 /* SYNOPSIS
 /*     #include <stringops.h>
 /*
-/*     int     temp_utf8_kludge;
+/*     int     util_utf8_enable;
 /*
 /*     char    *printable(buffer, replacement)
 /*     char    *buffer;
@@ -15,8 +15,8 @@
 /*     printable() replaces non-printable characters
 /*     in its input with the given replacement.
 /*
-/*     temp_utf8_kludge controls whether UTF8 is considered printable.
-/*     By default, non-ASCII text is replaced.
+/*     util_utf8_enable controls whether UTF8 is considered printable.
+/*     With util_utf8_enable equal to zero, non-ASCII text is replaced.
 /*
 /*     Arguments:
 /* .IP buffer
@@ -44,7 +44,7 @@
 
 #include "stringops.h"
 
-int temp_utf8_kludge = 0;
+int util_utf8_enable = 0;
 
 char   *printable(char *string, int replacement)
 {
@@ -59,7 +59,7 @@ char   *printable(char *string, int replacement)
     while ((ch = *cp) != 0) {
        if (ISASCII(ch) && ISPRINT(ch)) {
            /* ok */
-       } else if (temp_utf8_kludge && ch >= 194 && ch <= 254
+       } else if (util_utf8_enable && ch >= 194 && ch <= 254
                   && cp[1] >= 128 && cp[1] < 192) {
            /* UTF8; skip the rest of the bytes in the character. */
            while (cp[1] >= 128 && cp[1] < 192)
index 3d4d3b86933e70786c7f8443c3fce8e551d1efcd..52938ca307951136bda35bd9ed64160919443a62 100644 (file)
  /*
   * External interface.
   */
-extern int temp_utf8_kludge;
+extern int util_utf8_enable;
 extern char *printable(char *, int);
 extern char *neuter(char *, const char *, int);
 extern char *lowercase(char *);
+extern char *casefold(int, VSTRING *, const char *, CONST_CHAR_STAR *);
 extern char *uppercase(char *);
 extern char *skipblanks(const char *);
 extern char *trimblanks(char *, ssize_t);
index f42e21f535754aba4aa3fa44b7b9567a68c9d9f4..3d6922aa9161f333376e016f6ea0f21a8602155a 100644 (file)
@@ -8,8 +8,8 @@
 /*
 /*     int     valid_utf8_hostname(
 /*     int     enable_utf8,
-/*     const char *domain, 
-/*     int gripe)
+/*     const char *domain,
+/*     int     gripe)
 /* DESCRIPTION
 /*     valid_utf8_hostname() is a wrapper around valid_hostname().
 /*     If EAI support is compiled in, and enable_utf8 is true, the
@@ -43,7 +43,7 @@
 #include <mymalloc.h>
 #include <stringops.h>
 #include <valid_hostname.h>
-#include <midna.h>
+#include <midna_domain.h>
 #include <valid_utf8_hostname.h>
 
 /* valid_utf8_hostname - validate internationalized domain name */
@@ -51,8 +51,6 @@
 int     valid_utf8_hostname(int enable_utf8, const char *name, int gripe)
 {
     static const char myname[] = "valid_utf8_hostname";
-    const char *aname;
-    int     ret;
 
     /*
      * Trivial cases first.
@@ -64,23 +62,26 @@ int     valid_utf8_hostname(int enable_utf8, const char *name, int gripe)
     }
 
     /*
-     * Convert domain name to ASCII form.
+     * Convert non-ASCII domain name to ASCII and validate the result per
+     * STD3. midna_domain_to_ascii() applies valid_hostname() to the result.
+     * Propagate the gripe parameter for better diagnostics (note that
+     * midna_domain_to_ascii() logs a problem only when the result is not
+     * cached).
      */
 #ifndef NO_EAI
     if (enable_utf8 && !allascii(name)) {
-       if ((aname = midna_to_ascii(name)) == 0) {
+       if (midna_domain_to_ascii(name) == 0) {
            if (gripe)
                msg_warn("%s: malformed UTF-8 domain name", myname);
            return (0);
+       } else {
+           return (1);
        }
-    } else
+    }
 #endif
-       aname = name;
 
     /*
-     * Validate the name per STD3 (if the IDNA routines didn't already).
+     * Validate ASCII name per STD3.
      */
-    ret = valid_hostname(aname, gripe);
-
-    return (ret);
+    return (valid_hostname(name, gripe));
 }
index fb36540b0c7d8b7c321d4e46663ffa0cdd4d8ac1..009396ef9f6c0ed30cbd6047602f22ad7ab04c9b 100644 (file)
@@ -680,7 +680,7 @@ static void pre_jail_init(char *unused_name, char **unused_argv)
      * Start the cache cleanup thread after permanently dropping privileges.
      */
 #define VERIFY_DICT_OPEN_FLAGS (DICT_FLAG_DUP_REPLACE | DICT_FLAG_SYNC_UPDATE \
-           | DICT_FLAG_OPEN_LOCK)
+           | DICT_FLAG_OPEN_LOCK | DICT_FLAG_UTF8_REQUEST)
 
     saved_mask = umask(022);
     verify_map =
index 69252c308955a1b8ae822114e40508a2b5a59582..487b3a2d7637d3f2fd8e60630c6657afd940c9d8 100644 (file)
@@ -457,15 +457,18 @@ static void post_init(char *unused_name, char **unused_argv)
      */
     virtual_mailbox_maps =
        maps_create(VAR_VIRT_MAILBOX_MAPS, var_virt_mailbox_maps,
-                   DICT_FLAG_LOCK | DICT_FLAG_PARANOID);
+                   DICT_FLAG_LOCK | DICT_FLAG_PARANOID
+                   | DICT_FLAG_UTF8_REQUEST);
 
     virtual_uid_maps =
        maps_create(VAR_VIRT_UID_MAPS, var_virt_uid_maps,
-                   DICT_FLAG_LOCK | DICT_FLAG_PARANOID);
+                   DICT_FLAG_LOCK | DICT_FLAG_PARANOID
+                   | DICT_FLAG_UTF8_REQUEST);
 
     virtual_gid_maps =
        maps_create(VAR_VIRT_GID_MAPS, var_virt_gid_maps,
-                   DICT_FLAG_LOCK | DICT_FLAG_PARANOID);
+                   DICT_FLAG_LOCK | DICT_FLAG_PARANOID
+                   | DICT_FLAG_UTF8_REQUEST);
 
     virtual_mbox_lock_mask = mbox_lock_mask(var_virt_mailbox_lock);
 }