-TDICT_THASH
-TDICT_UNION
-TDICT_UNIX
+-TDICT_UTF8_BACKUP
-TDNS_FIXED
-TDNS_REPLY
-TDNS_RR
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.
# 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:
# 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
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
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.
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.
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
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.
+-//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
-------------------------------------------------------------------------------
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:\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 uses SMTPS to send all remote mail to a provider's mail
server called "mail.example.com".
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
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
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.
# 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:
# 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>
<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
<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>
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>
<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>
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>
-<!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>
<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 ≥ 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 ≥ 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 ≥ 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 < 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 < 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>
<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 < 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
<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
<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>
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>
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:
<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 < 2.3 control for the Postfix SMTP client TLS
+ Obsolete Postfix < 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>
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>
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>
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>
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>
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.
<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>
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>
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:
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>
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>
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.
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>
<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>
<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>
</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>
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>
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>
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
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:
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>
<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>
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>
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.
<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 < 2.3 control for the Postfix SMTP client TLS
+ Obsolete Postfix < 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>
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>
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>
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>
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>
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.
<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>
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>
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:
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>
.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
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"
.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
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"
.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
.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:
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
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
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
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;
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;
# 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:
# 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>
<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
<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>
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>
<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>
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>
-<!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>
<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 ≥ 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 ≥ 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 ≥ 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 < 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 < 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>
<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 < 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
<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
<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>
</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>
+
+
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
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),
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)
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);
#include <stringops.h>
#include <mymalloc.h>
#ifndef NO_EAI
-#include <midna.h>
+#include <midna_domain.h>
#endif
/* Global library. */
"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);
/* 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
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,
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,
};
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,
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,
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));
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)
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);
}
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 \
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)
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
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
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
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
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
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
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
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
#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)
{
}
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);
*/
#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
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 */
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)
/*
*/
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);
}
/*
* 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);
* 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));
}
/*
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",
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",
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",
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",
#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
/*
* 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);
#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)
{
}
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");
*/
#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
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);
}
* 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);
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, "+");
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,
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;
/*
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.
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();
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
#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"
#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.
*/
* 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
*/
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.
--- /dev/null
+/*++
+/* 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 */
--- /dev/null
+#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
#include <dict_fail.h>
#include <sigdelay.h>
#include <mymalloc.h>
+#include <stringops.h>
/* Global library. */
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.
*/
#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)
{
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,
*/
#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
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
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
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
/* 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 */
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
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 */
} 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);
} 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);
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
#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)
{
}
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");
*/
#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
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);
return (tp);
} else {
t1->head = t2;
+ t2->owner = t1;
while (t2->next)
(t2 = t2->next)->owner = t1;
return (t1->tail = 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);
/* 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
/* 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
/* 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;
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);
/*
* 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
}
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();
}
*/
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,
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. */
*/
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,
/* 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
&& (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.
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 "@", ":"
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);
}
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;
/*
* 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]);
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;
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);
}
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);
}
* 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);
/* 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"
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.
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);
}
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.
/*
* 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]);
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;
&& (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
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);
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);
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);
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);
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,
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
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
#include <sane_connect.h>
#include <myaddrinfo.h>
#include <sock_addr.h>
-#include <midna.h>
+#include <midna_domain.h>
#define STR(x) vstring_str(x)
* 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
* 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
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 = "";
}
}
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 = "";
}
} 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);
}
}
} 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);
}
}
{
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);
}
* 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);
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);
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
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
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,
/* 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
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;
* 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.
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.
#include <stringops.h>
#include <myaddrinfo.h>
#include <inet_proto.h>
-#include <midna.h>
+#include <midna_domain.h>
/* Global library. */
* 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
* 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
* 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 */
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;
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);
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,
#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.
*/
* 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.
* 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)
* 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");
* 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);
/*
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
}
}
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));
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);
}
}
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
* 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)) {
}
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++)
* 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);
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",
#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. */
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;
}
* 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
* 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);
* 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.
/*
* 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);
}
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)
* 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;
* 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;
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);
}
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
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));
* 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;
* 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;
#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,
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;
#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) {
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;
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;
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;
}
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;
}
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;
}
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;
}
/* 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);
/* 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;
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;
>>> # 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()
>>> #
>>> # 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()
>>> #
>>> # 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()
>>> #
>>> # 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
>>> 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
>>> #
>>> # 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
>>> #
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
>>> #
>>> 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
>>> #
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
* 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 )
| (((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
#include <stringops.h>
#include <msg.h>
#include <iostuff.h> /* non-blocking */
-#include <midna.h>
+#include <midna_domain.h>
/* Global library. */
*/
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;
#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;
}
}
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;
#define TLS_INTERNAL
#include <tls.h>
+#include <openssl/dh.h>
/* Application-specific. */
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,
};
#define TLS_INTERNAL
#include <tls.h>
+#include <openssl/rsa.h>
/* tls_tmp_rsa_cb - call-back to generate ephemeral RSA key */
* 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);
* 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,
*/
#define STR vstring_str
+#define LEN VSTRING_LEN
/*
* Some of the lists that define the address domain classes.
*/
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)) {
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);
}
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;
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 */
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 \
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.
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)
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
$(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
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:
$(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
$(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; \
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
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
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
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
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
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
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
--- /dev/null
+/*++
+/* 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 */
--- /dev/null
+# 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
--- /dev/null
+> # 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
"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,
};
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;
#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 */
#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.
*/
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.
* 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.
* 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,
dict->owner.status = DICT_OWNER_UNKNOWN;
dict->owner.uid = INT_MAX;
dict->error = DICT_ERR_NONE;
+ dict->size = size;
dict->jbuf = 0;
return dict;
}
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,
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);
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");
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");
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);
/* 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.
myfree(saved_name); \
if (xperr != 0) \
myfree(xperr); \
- if (table != 0) \
- htable_free(table, myfree); \
return (__d); \
} while (0)
"%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.
*/
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\"; "
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));
}
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
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);
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;
/*
/* 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.
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);
}
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
/*
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",
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);
> del foo
foo: deleted
> put baz bazval
-baz=bazval
> get baz
baz=bazval
> del baz
/* 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.
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.
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);
}
}
/*
* 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));
}
attr_scan64.c 17256
attr_scan_plain.c 16924
auto_clnt.c 9819
+ABCDEF 012345
--- /dev/null
+/*++
+/* 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);
+}
--- /dev/null
+#!/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
--- /dev/null
+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
#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. */
* 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))
/* 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);
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);
}
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;
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 =
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);
int match;
int i;
va_list ap;
+ const char *utf8_err;
/*
* Iterate over all patterns in the list, stop at the first match.
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++)
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);
}
* Utility library.
*/
#include <argv.h>
+#include <vstring.h>
/*
* External interface.
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 */
};
#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 *);
/*--*/
#endif
-
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);
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.
}
/*
- * 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);
}
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.
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)
}
/*
- * 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);
}
}
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:"
}
/*
- * 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);
}
* - 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.
+++ /dev/null
-/*++
-/* 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 */
--- /dev/null
+/*++
+/* 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 */
/*++
/* 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
--- /dev/null
+# 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
--- /dev/null
+./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:
+++ /dev/null
-# 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
+++ /dev/null
-./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"
/* SYNOPSIS
/* #include <stringops.h>
/*
-/* int temp_utf8_kludge;
+/* int util_utf8_enable;
/*
/* char *printable(buffer, replacement)
/* char *buffer;
/* 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
#include "stringops.h"
-int temp_utf8_kludge = 0;
+int util_utf8_enable = 0;
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)
/*
* 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);
/*
/* 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
#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 */
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.
}
/*
- * 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));
}
* 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 =
*/
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);
}