2
0
mirror of https://github.com/vdukhovni/postfix synced 2025-08-30 05:38:06 +00:00

postfix-2.12-20141228

This commit is contained in:
Wietse Venema 2014-12-28 00:00:00 -05:00 committed by Viktor Dukhovni
parent e5e46ec03f
commit 19b6598b23
30 changed files with 548 additions and 80 deletions

View File

@ -21191,3 +21191,21 @@ Apologies for any names omitted.
child process. Background: some shells implement "IFS=foo
command" as a permanent IFS change; this was allowed by
standards at some point in time. File: postfix-install.
20141227
Feature: smtp_address_verify_target (default: rcpt) that
determines what protocol stage decides if a recipient is
valid. Specify "data" for servers that reject recipients
after the DATA command. Files: mantools/postlink,
proto/postconf.proto, proto/ADDRESS_VERIFICATION_README.html,
global/mail_params.h, smtp/lmtp_params.c, smtp/smtp.c,
smtp/smtp.h, smtp/smtp_params.c, smtp/smtp_proto.c.
20141228
Cleanup: the IDNA conversion routines now accept both
ASCII and UTF8 inputs. The functions als verify that
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.

View File

@ -81,6 +81,11 @@ postconf(5) for details.
LLiimmiittaattiioonnss ooff aaddddrreessss vveerriiffiiccaattiioonn
* Postfix assumes that a remote SMTP server will reject unknown addresses in
reply to the RCPT TO command. However, some sites report this in reply to
the DATA command. For such sites you may configure a workaround with the
smtp_address_verify_target parameter (Postfix 2.12 and later).
* When verifying a remote address, Postfix probes the preferred MTAs for that
address, without actually delivering mail. If a preferred MTA accepts the
address, then Postfix assumes that the address is deliverable. In reality,
@ -108,9 +113,9 @@ LLiimmiittaattiioonnss ooff aaddddrreessss vveerriiffi
here just in case people believe that it is a limitation.
* Unfortunately, some sites do not reject unknown addresses in reply to the
RCPT TO command, but report a delivery failure in response to end of DATA
after a message is transferred. Postfix address verification does not work
with such sites.
RCPT TO or DATA command, but instead report a delivery failure in response
to end of DATA after a message is transferred. Postfix address verification
does not work with such sites.
* By default, Postfix probe messages have a sender address "double-
bounce@$myorigin" (with Postfix versions before 2.5, the default is

View File

@ -198,6 +198,12 @@ details. </p>
<ul>
<li> <p> Postfix assumes that a remote SMTP server will reject
unknown addresses in reply to the RCPT TO command. However, some
sites report this in reply to the DATA command. For such sites
you may configure a workaround with the <a href="postconf.5.html#smtp_address_verify_target">smtp_address_verify_target</a>
parameter (Postfix 2.12 and later). </p>
<li> <p> When verifying a remote address, Postfix probes the preferred
MTAs for that address, without actually delivering mail. If
a preferred MTA accepts the address, then Postfix assumes that the
@ -227,8 +233,8 @@ MTA for that address rejects mail from your machine for any reason.
This is not a limitation, but it is mentioned here just in case
people believe that it is a limitation. </p>
<li> <p> Unfortunately, some sites do not reject
unknown addresses in reply to the RCPT TO command, but report a
<li> <p> Unfortunately, some sites do not reject unknown addresses
in reply to the RCPT TO or DATA command, but instead report a
delivery failure in response to end of DATA after a message is
transferred. Postfix address verification does not work with such
sites. </p>

View File

@ -833,6 +833,12 @@ SMTP(8) SMTP(8)
Optional list of relay hosts for SMTP destinations that can't be
found or that are unreachable.
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
stage that determines whether an email address is deliverable.
<b>SEE ALSO</b>
<a href="generic.5.html">generic(5)</a>, output address rewriting
<a href="header_checks.5.html">header_checks(5)</a>, message header content inspection

View File

@ -4021,6 +4021,17 @@ configuration parameter. See there for details. </p>
<p> This feature is available in Postfix 2.8 and later. </p>
</DD>
<DT><b><a name="lmtp_address_verify_target">lmtp_address_verify_target</a>
(default: rcpt)</b></DT><DD>
<p> The LMTP-specific version of the <a href="postconf.5.html#smtp_dns_support_level">smtp_dns_support_level</a>
configuration parameter. See there for details. </p>
<p> This feature is available in Postfix 2.12 and later. </p>
</DD>
<DT><b><a name="lmtp_assume_final">lmtp_assume_final</a>
@ -9859,6 +9870,49 @@ that affects IPv6 or IPv4, as long as it does not affect both. </p>
<p> This feature is available in Postfix 2.8 and later. </p>
</DD>
<DT><b><a name="smtp_address_verify_target">smtp_address_verify_target</a>
(default: rcpt)</b></DT><DD>
<p> In the context of email address verification, the SMTP protocol
stage that determines whether an email address is deliverable.
Specify one of "rcpt" or "data". The latter is needed with remote
SMTP servers that reject recipients after the DATA command. Use
<a href="postconf.5.html#transport_maps">transport_maps</a> to apply this feature selectively: </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
</pre>
</blockquote>
<blockquote>
<pre>
/etc/postfix/transport:
smtp-domain_that_verifies_after_data smtp-data-target:
lmtp-domain_that_verifies_after_data lmtp-data-target:
</pre>
</blockquote>
<blockquote>
<pre>
/etc/postfix/<a href="master.5.html">master.cf</a>:
smtp-data-target unix - - n - - smtp
-o <a href="postconf.5.html#smtp_address_verify_target">smtp_address_verify_target</a>=data
lmtp-data-target unix - - n - - lmtp
-o <a href="postconf.5.html#lmtp_address_verify_target">lmtp_address_verify_target</a>=data
<blockquote>
<pre>
<p> Unselective use of the "data" target does no harm, but will
result in unnecessary "lost connection after DATA" events at remote
SMTP/LMTP servers. </p>
<p> This feature is available in Postfix 2.12 and later. </p>
</DD>
<DT><b><a name="smtp_always_send_ehlo">smtp_always_send_ehlo</a>

View File

@ -833,6 +833,12 @@ SMTP(8) SMTP(8)
Optional list of relay hosts for SMTP destinations that can't be
found or that are unreachable.
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
stage that determines whether an email address is deliverable.
<b>SEE ALSO</b>
<a href="generic.5.html">generic(5)</a>, output address rewriting
<a href="header_checks.5.html">header_checks(5)</a>, message header content inspection

View File

@ -2472,6 +2472,11 @@ The LMTP-specific version of the smtp_address_preference
configuration parameter. See there for details.
.PP
This feature is available in Postfix 2.8 and later.
.SH lmtp_address_verify_target (default: rcpt)
The LMTP-specific version of the smtp_dns_support_level
configuration parameter. See there for details.
.PP
This feature is available in Postfix 2.12 and later.
.SH lmtp_assume_final (default: no)
When a remote LMTP server announces no DSN support, assume that
the
@ -6085,6 +6090,56 @@ that affects IPv6 or IPv4, as long as it does not affect both.
.br
.PP
This feature is available in Postfix 2.8 and later.
.SH smtp_address_verify_target (default: rcpt)
In the context of email address verification, the SMTP protocol
stage that determines whether an email address is deliverable.
Specify one of "rcpt" or "data". The latter is needed with remote
SMTP servers that reject recipients after the DATA command. Use
transport_maps to apply this feature selectively:
.sp
.in +4
.nf
.na
.ft C
/etc/postfix/main.cf:
transport_maps = hash:/etc/postfix/transport
.fi
.ad
.ft R
.in -4
.sp
.in +4
.nf
.na
.ft C
/etc/postfix/transport:
smtp-domain_that_verifies_after_data smtp-data-target:
lmtp-domain_that_verifies_after_data lmtp-data-target:
.fi
.ad
.ft R
.in -4
.sp
.in +4
.nf
.na
.ft C
/etc/postfix/master.cf:
smtp-data-target unix - - n - - smtp
-o smtp_address_verify_target=data
lmtp-data-target unix - - n - - lmtp
-o lmtp_address_verify_target=data
.sp
.in +4
.nf
.na
.ft C
.PP
Unselective use of the "data" target does no harm, but will
result in unnecessary "lost connection after DATA" events at remote
SMTP/LMTP servers.
.PP
This feature is available in Postfix 2.12 and later.
.SH smtp_always_send_ehlo (default: yes)
Always send EHLO at the start of an SMTP session.
.PP

View File

@ -725,6 +725,11 @@ Available with Postfix 2.3 and later:
.IP "\fBsmtp_fallback_relay ($fallback_relay)\fR"
Optional list of relay hosts for SMTP destinations that can't be
found or that are unreachable.
.PP
Available with Postfix 2.12 and later:
.IP "\fBsmtp_address_verify_target (rcpt)\fR"
In the context of email address verification, the SMTP protocol
stage that determines whether an email address is deliverable.
.SH "SEE ALSO"
.na
.nf

View File

@ -89,6 +89,8 @@ while (<>) {
s;\baddress_verify_service_name\b;<a href="postconf.5.html#address_verify_service_name">$&</a>;g;
s;\baddress_verify_transport_maps\b;<a href="postconf.5.html#address_verify_transport_maps">$&</a>;g;
s;\baddress_verify_virtual_transport\b;<a href="postconf.5.html#address_verify_virtual_transport">$&</a>;g;
s;\bsmtp_address_verify_target\b;<a href="postconf.5.html#smtp_address_verify_target">$&</a>;g;
s;\blmtp_address_verify_target\b;<a href="postconf.5.html#lmtp_address_verify_target">$&</a>;g;
s;\balias_database\b;<a href="postconf.5.html#alias_database">$&</a>;g;
s;\balias_maps\b;<a href="postconf.5.html#alias_maps">$&</a>;g;
s;\ballow_mail_to_com[-</bB>]*\n*[ <bB>]*mands\b;<a href="postconf.5.html#allow_mail_to_commands">$&</a>;g;

View File

@ -198,6 +198,12 @@ details. </p>
<ul>
<li> <p> Postfix assumes that a remote SMTP server will reject
unknown addresses in reply to the RCPT TO command. However, some
sites report this in reply to the DATA command. For such sites
you may configure a workaround with the smtp_address_verify_target
parameter (Postfix 2.12 and later). </p>
<li> <p> When verifying a remote address, Postfix probes the preferred
MTAs for that address, without actually delivering mail. If
a preferred MTA accepts the address, then Postfix assumes that the
@ -227,8 +233,8 @@ MTA for that address rejects mail from your machine for any reason.
This is not a limitation, but it is mentioned here just in case
people believe that it is a limitation. </p>
<li> <p> Unfortunately, some sites do not reject
unknown addresses in reply to the RCPT TO command, but report a
<li> <p> Unfortunately, some sites do not reject unknown addresses
in reply to the RCPT TO or DATA command, but instead report a
delivery failure in response to end of DATA after a message is
transferred. Postfix address verification does not work with such
sites. </p>

View File

@ -15419,6 +15419,52 @@ units are: s (seconds), m (minutes), h (hours), d (days), w (weeks).
<p> This feature is available in Postfix 2.9 and later. </p>
%PARAM smtp_address_verify_target rcpt
<p> In the context of email address verification, the SMTP protocol
stage that determines whether an email address is deliverable.
Specify one of "rcpt" or "data". The latter is needed with remote
SMTP servers that reject recipients after the DATA command. Use
transport_maps to apply this feature selectively: </p>
<blockquote>
<pre>
/etc/postfix/main.cf:
transport_maps = hash:/etc/postfix/transport
</pre>
</blockquote>
<blockquote>
<pre>
/etc/postfix/transport:
smtp-domain_that_verifies_after_data smtp-data-target:
lmtp-domain_that_verifies_after_data lmtp-data-target:
</pre>
</blockquote>
<blockquote>
<pre>
/etc/postfix/master.cf:
smtp-data-target unix - - n - - smtp
-o smtp_address_verify_target=data
lmtp-data-target unix - - n - - lmtp
-o lmtp_address_verify_target=data
<blockquote>
<pre>
<p> Unselective use of the "data" target does no harm, but will
result in unnecessary "lost connection after DATA" events at remote
SMTP/LMTP servers. </p>
<p> This feature is available in Postfix 2.12 and later. </p>
%PARAM lmtp_address_verify_target rcpt
<p> The LMTP-specific version of the smtp_dns_support_level
configuration parameter. See there for details. </p>
<p> This feature is available in Postfix 2.12 and later. </p>
%PARAM daemon_table_open_error_is_fatal no
<p> How a Postfix daemon process handles errors while opening lookup

View File

@ -462,7 +462,7 @@ static const char *bounce_template_lookup(const char *key, int unused_mode,
"non-ASCII input value: \"%s\"",
tp->origin, key, asc_val);
return (asc_val);
} else if ((utf8_val = midna_ascii_to_utf8(asc_val)) == 0) {
} else if ((utf8_val = midna_to_utf8(asc_val)) == 0) {
msg_warn("%s: conversion \"%s\" failed: "
"input value: \"%s\"",
tp->origin, key, asc_val);

View File

@ -2778,6 +2778,14 @@ extern char *var_vrfy_relay_maps;
#define DEF_VRFY_XPORT_MAPS "$" VAR_TRANSPORT_MAPS
extern char *var_vrfy_xport_maps;
#define SMTP_VRFY_TGT_RCPT "rcpt"
#define SMTP_VRFY_TGT_DATA "data"
#define VAR_LMTP_VRFY_TGT "lmtp_address_verify_target"
#define DEF_LMTP_VRFY_TGT SMTP_VRFY_TGT_RCPT
#define VAR_SMTP_VRFY_TGT "smtp_address_verify_target"
#define DEF_SMTP_VRFY_TGT SMTP_VRFY_TGT_RCPT
extern char *var_smtp_vrfy_tgt;
/*
* Message delivery trace service.
*/

View File

@ -17,7 +17,7 @@
/*
/* Arguments:
/* .IP name
/* Parameter name. This is used to privode context for
/* Parameter name. This is used to provide context for
/* error messages.
/* .IP value
/* Parameter value.

View File

@ -20,7 +20,7 @@
* Patches change both the patchlevel and the release date. Snapshots have no
* patchlevel; they change the release date only.
*/
#define MAIL_RELEASE_DATE "20141226"
#define MAIL_RELEASE_DATE "20141228"
#define MAIL_VERSION_NUMBER "2.12"
#ifdef SNAPSHOT

View File

@ -1103,7 +1103,7 @@ static DNS_RR *domain_addr(STATE *state, char *domain)
* IDNA support.
*/
#ifndef NO_EAI
if (!allascii(domain) && (aname = midna_utf8_to_ascii(domain)) != 0) {
if (!allascii(domain) && (aname = midna_to_ascii(domain)) != 0) {
msg_info("%s asciified to %s", domain, aname);
} else
#endif
@ -1168,7 +1168,7 @@ static DNS_RR *host_addr(STATE *state, const char *host)
* IDNA support.
*/
#ifndef NO_EAI
if (!allascii(host) && (ahost = midna_utf8_to_ascii(host)) != 0) {
if (!allascii(host) && (ahost = midna_to_ascii(host)) != 0) {
msg_info("%s asciified to %s", host, ahost);
} else
#endif

View File

@ -33,6 +33,7 @@
VAR_LMTP_SASL_TYPE, DEF_LMTP_SASL_TYPE, &var_smtp_sasl_type, 1, 0,
VAR_LMTP_BIND_ADDR, DEF_LMTP_BIND_ADDR, &var_smtp_bind_addr, 0, 0,
VAR_LMTP_BIND_ADDR6, DEF_LMTP_BIND_ADDR6, &var_smtp_bind_addr6, 0, 0,
VAR_LMTP_VRFY_TGT, DEF_LMTP_VRFY_TGT, &var_smtp_vrfy_tgt, 1, 0,
VAR_LMTP_HELO_NAME, DEF_LMTP_HELO_NAME, &var_smtp_helo_name, 1, 0,
VAR_LMTP_HOST_LOOKUP, DEF_LMTP_HOST_LOOKUP, &var_smtp_host_lookup, 1, 0,
VAR_LMTP_DNS_SUPPORT, DEF_LMTP_DNS_SUPPORT, &var_smtp_dns_support, 0, 0,

View File

@ -685,6 +685,11 @@
/* .IP "\fBsmtp_fallback_relay ($fallback_relay)\fR"
/* Optional list of relay hosts for SMTP destinations that can't be
/* found or that are unreachable.
/* .PP
/* Available with Postfix 2.12 and later:
/* .IP "\fBsmtp_address_verify_target (rcpt)\fR"
/* In the context of email address verification, the SMTP protocol
/* stage that determines whether an email address is deliverable.
/* SEE ALSO
/* generic(5), output address rewriting
/* header_checks(5), message header content inspection
@ -816,6 +821,7 @@ char *var_smtp_sasl_mechs;
char *var_smtp_sasl_type;
char *var_smtp_bind_addr;
char *var_smtp_bind_addr6;
char *var_smtp_vrfy_tgt;
bool var_smtp_rand_addr;
int var_smtp_pix_thresh;
int var_queue_run_delay;
@ -1077,6 +1083,11 @@ static void post_init(char *unused_name, char **unused_argv)
*/
smtp_dns_res_opt = name_mask(VAR_LMTP_SMTP(DNS_RES_OPT), dns_res_opt_masks,
var_smtp_dns_res_opt);
/*
* Address verification.
*/
smtp_vrfy_init();
}
/* pre_init - pre-jail initialization */

View File

@ -365,6 +365,7 @@ extern int smtp_connect(SMTP_STATE *);
/*
* smtp_proto.c
*/
extern void smtp_vrfy_init(void);
extern int smtp_helo(SMTP_STATE *);
extern int smtp_xfer(SMTP_STATE *);
extern int smtp_rset(SMTP_STATE *);
@ -515,6 +516,11 @@ extern void smtp_chat_notify(SMTP_SESSION *);
#define DSN_BY_LOCAL_MTA ((char *) 0) /* DSN issued by local MTA */
#define SMTP_RESP_SET_DSN(resp, _dsn) do { \
vstring_strcpy((resp)->dsn_buf, (_dsn)); \
(resp)->dsn = STR((resp)->dsn_buf); \
} while (0)
/*
* These operations implement a redundant mark-and-sweep algorithm that
* explicitly accounts for the fate of every recipient. The interface is

View File

@ -378,7 +378,7 @@ DNS_RR *smtp_domain_addr(const char *name, DNS_RR **mxrr, int misc_flags,
* IDNA support.
*/
#ifndef NO_EAI
if (!allascii(name) && (aname = midna_utf8_to_ascii(name)) != 0) {
if (!allascii(name) && (aname = midna_to_ascii(name)) != 0) {
if (msg_verbose)
msg_info("%s asciified to %s", name, aname);
} else
@ -524,7 +524,7 @@ DNS_RR *smtp_host_addr(const char *host, int misc_flags, DSN_BUF *why)
* IDNA support.
*/
#ifndef NO_EAI
if (!allascii(host) && (ahost = midna_utf8_to_ascii(host)) != 0) {
if (!allascii(host) && (ahost = midna_to_ascii(host)) != 0) {
if (msg_verbose)
msg_info("%s asciified to %s", host, ahost);
} else

View File

@ -34,6 +34,7 @@
VAR_SMTP_SASL_TYPE, DEF_SMTP_SASL_TYPE, &var_smtp_sasl_type, 1, 0,
VAR_SMTP_BIND_ADDR, DEF_SMTP_BIND_ADDR, &var_smtp_bind_addr, 0, 0,
VAR_SMTP_BIND_ADDR6, DEF_SMTP_BIND_ADDR6, &var_smtp_bind_addr6, 0, 0,
VAR_SMTP_VRFY_TGT, DEF_SMTP_VRFY_TGT, &var_smtp_vrfy_tgt, 1, 0,
VAR_SMTP_HELO_NAME, DEF_SMTP_HELO_NAME, &var_smtp_helo_name, 1, 0,
VAR_SMTP_HOST_LOOKUP, DEF_SMTP_HOST_LOOKUP, &var_smtp_host_lookup, 1, 0,
VAR_SMTP_DNS_SUPPORT, DEF_SMTP_DNS_SUPPORT, &var_smtp_dns_support, 0, 0,

View File

@ -144,6 +144,8 @@
#include <tok822.h>
#include <mail_addr_map.h>
#include <ext_prop.h>
#include <namadr_list.h>
#include <match_parent_style.h>
#include <lex_822.h>
#include <dsn_mask.h>
#include <xtext.h>
@ -261,6 +263,24 @@ HBC_CALL_BACKS smtp_hbc_callbacks[1] = {
smtp_text_out,
};
static int smtp_vrfy_tgt;
/* smtp_vrfy_init - initialize */
void smtp_vrfy_init(void)
{
static const NAME_CODE vrfy_init_table[] = {
SMTP_VRFY_TGT_RCPT, SMTP_STATE_RCPT,
SMTP_VRFY_TGT_DATA, SMTP_STATE_DATA,
0,
};
if ((smtp_vrfy_tgt = name_code(vrfy_init_table, NAME_CODE_FLAG_NONE,
var_smtp_vrfy_tgt)) == 0)
msg_fatal("bad protocol stage: \"%s = %s\"",
VAR_SMTP_VRFY_TGT, var_smtp_vrfy_tgt);
}
/* smtp_helo - perform initial handshake with SMTP server */
int smtp_helo(SMTP_STATE *state)
@ -1541,7 +1561,8 @@ static int smtp_loop(SMTP_STATE *state, NOCLOBBER int send_state,
dsn_notify_str(rcpt->dsn_notify));
}
if ((next_rcpt = send_rcpt + 1) == SMTP_RCPT_LEFT(state))
next_state = DEL_REQ_TRACE_ONLY(request->flags) ?
next_state = (DEL_REQ_TRACE_ONLY(request->flags)
&& smtp_vrfy_tgt == SMTP_STATE_RCPT) ?
SMTP_STATE_ABORT : SMTP_STATE_DATA;
break;
@ -1808,7 +1829,8 @@ static int smtp_loop(SMTP_STATE *state, NOCLOBBER int send_state,
}
++nrcpt;
/* If trace-only, mark the recipient done. */
if (DEL_REQ_TRACE_ONLY(request->flags)) {
if (DEL_REQ_TRACE_ONLY(request->flags)
&& smtp_vrfy_tgt == SMTP_STATE_RCPT) {
translit(resp->str, "\n", " ");
smtp_rcpt_done(state, resp, rcpt);
}
@ -1822,7 +1844,8 @@ static int smtp_loop(SMTP_STATE *state, NOCLOBBER int send_state,
}
/* If trace-only, send RSET instead of DATA. */
if (++recv_rcpt == SMTP_RCPT_LEFT(state))
recv_state = DEL_REQ_TRACE_ONLY(request->flags) ?
recv_state = (DEL_REQ_TRACE_ONLY(request->flags)
&& smtp_vrfy_tgt == SMTP_STATE_RCPT) ?
SMTP_STATE_ABORT : SMTP_STATE_DATA;
/* XXX Also: record if non-delivering session. */
break;
@ -1833,6 +1856,7 @@ static int smtp_loop(SMTP_STATE *state, NOCLOBBER int send_state,
* receiver can apply a course correction.
*/
case SMTP_STATE_DATA:
recv_state = SMTP_STATE_DOT;
if (resp->code / 100 != 3) {
if (nrcpt > 0)
smtp_mesg_fail(state, STR(iter->host), resp,
@ -1842,7 +1866,30 @@ static int smtp_loop(SMTP_STATE *state, NOCLOBBER int send_state,
xfer_request[SMTP_STATE_DATA]);
nrcpt = -1;
}
recv_state = SMTP_STATE_DOT;
/*
* In the case of a successful address probe with target
* equal to DATA, the remote server is now in the DATA
* state, and therefore we must not make any further
* attempt to send or receive on this connection. This
* means that we cannot not reuse the general-purpose
* course-correction logic below which sends RSET (and
* perhaps QUIT). Instead we "jump" straight to the exit
* and force an unceremonious disconnect.
*/
else if (DEL_REQ_TRACE_ONLY(request->flags)
&& smtp_vrfy_tgt == SMTP_STATE_DATA) {
for (nrcpt = 0; nrcpt < recv_rcpt; nrcpt++) {
rcpt = request->rcpt_list.info + nrcpt;
if (!SMTP_RCPT_ISMARKED(rcpt)) {
translit(resp->str, "\n", " ");
SMTP_RESP_SET_DSN(resp, "2.0.0");
smtp_rcpt_done(state, resp, rcpt);
}
}
DONT_CACHE_THIS_SESSION;
send_state = recv_state = SMTP_STATE_LAST;
}
break;
/*

View File

@ -1417,7 +1417,7 @@ static int reject_unknown_mailhost(SMTPD_STATE *state, const char *name,
* Fix 20140924: convert domain to ASCII.
*/
#ifndef NO_EAI
if (!allascii(name) && (aname = midna_utf8_to_ascii(name)) != 0) {
if (!allascii(name) && (aname = midna_to_ascii(name)) != 0) {
if (msg_verbose)
msg_info("%s asciified to %s", name, aname);
name = aname;
@ -1916,7 +1916,7 @@ static int permit_mx_backup(SMTPD_STATE *state, const char *recipient,
* Fix 20140924: convert domain to ASCII.
*/
#ifndef NO_EAI
if (!allascii(domain) && (adomain = midna_utf8_to_ascii(domain)) != 0) {
if (!allascii(domain) && (adomain = midna_to_ascii(domain)) != 0) {
if (msg_verbose)
msg_info("%s asciified to %s", domain, adomain);
domain = adomain;
@ -2914,7 +2914,7 @@ static int check_server_access(SMTPD_STATE *state, const char *table,
* Fix 20140924: convert domain to ASCII.
*/
#ifndef NO_EAI
if (!allascii(domain) && (adomain = midna_utf8_to_ascii(domain)) != 0) {
if (!allascii(domain) && (adomain = midna_to_ascii(domain)) != 0) {
if (msg_verbose)
msg_info("%s asciified to %s", domain, adomain);
domain = adomain;
@ -3634,7 +3634,7 @@ static const SMTPD_RBL_STATE *find_dnsxl_domain(SMTPD_STATE *state,
* Fix 20140706: convert domain to ASCII.
*/
#ifndef NO_EAI
if (!allascii(domain) && (adomain = midna_utf8_to_ascii(domain)) != 0) {
if (!allascii(domain) && (adomain = midna_to_ascii(domain)) != 0) {
if (msg_verbose)
msg_info("%s asciified to %s", domain, adomain);
domain = adomain;

View File

@ -535,7 +535,7 @@ static int match_servername(const char *certid,
*/
if (!allascii(certid))
return (0);
if (!allascii(nexthop) && (aname = midna_utf8_to_ascii(nexthop)) != 0) {
if (!allascii(nexthop) && (aname = midna_to_ascii(nexthop)) != 0) {
if (msg_verbose)
msg_info("%s asciified to %s", nexthop, aname);
nexthop = aname;
@ -586,7 +586,7 @@ static int match_servername(const char *certid,
}
}
if (!allascii(domain)
&& (aname = midna_utf8_to_ascii(domain)) != 0) {
&& (aname = midna_to_ascii(domain)) != 0) {
if (msg_verbose)
msg_info("%s asciified to %s", domain, aname);
domain = aname;

View File

@ -515,7 +515,7 @@ tests: all valid_hostname_test mac_expand_test dict_test unescape_test \
dict_cidr_test attr_scan_plain_test htable_test hex_code_test \
myaddrinfo_test format_tv_test ip_match_test name_mask_tests \
base32_code_test dict_thash_test surrogate_test timecmp_test \
dict_static_test dict_inline_test
dict_static_test dict_inline_test midna_test
root_tests:
@ -750,6 +750,11 @@ dict_inline_test: dict_open dict_inline.ref
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
depend: $(MAKES)
(sed '1,/^# do not edit/!d' Makefile.in; \
set -e; for i in [a-z][a-z0-9]*.c; do \

View File

@ -8,21 +8,34 @@
/*
/* int midna_cache_size;
/*
/* const char *midna_utf8_to_ascii(
/* const char *midna_to_ascii(
/* const char *name)
/*
/* const char *midna_ascii_to_utf8(
/* 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_utf8_to_ascii() converts an UTF-8 domain name to
/* ASCII. The result is a null pointer in case of error.
/* 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_ascii_to_utf8() converts an ASCII domain name to
/* UTF-8. The result is a null pointer in case of error.
/* 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
@ -31,7 +44,7 @@
/* msg(3) diagnostics interface
/* DIAGNOSTICS
/* Fatal errors: memory allocation problem.
/* Warnings: conversion error.
/* Warnings: conversion error or result validation error.
/* LICENSE
/* .ad
/* .fi
@ -50,6 +63,7 @@
*/
#include <sys_defs.h>
#include <string.h>
#include <ctype.h>
#ifndef NO_EAI
#include <unicode/uidna.h>
@ -67,15 +81,18 @@
/*
* Application-specific.
*/
#define DEF_MIDNA_CACHE_SIZE 100
#define DEF_MIDNA_CACHE_SIZE 256
int midna_cache_size = DEF_MIDNA_CACHE_SIZE;
static VSTRING *midna_buf; /* x.suffix */
/* midna_utf8_to_ascii_create - convert UTF8 domain to ASCII */
#define STR(x) vstring_str(x)
static void *midna_utf8_to_ascii_create(const char *name, void *unused_context)
/* 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_utf8_to_ascii_create";
static const char myname[] = "midna_to_ascii_create";
char buf[1024]; /* XXX */
UErrorCode error = U_ZERO_ERROR;
UIDNAInfo info = UIDNA_INFO_INITIALIZER;
@ -85,11 +102,15 @@ static void *midna_utf8_to_ascii_create(const char *name, void *unused_context)
/*
* Paranoia: do not expose uidna_*() to unfiltered network data.
*/
if (valid_utf8_string(name, strlen(name)) == 0) {
msg_warn("%s: Problem translating domain \"%s\" to IDNA form: %s",
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),
@ -97,20 +118,32 @@ static void *midna_utf8_to_ascii_create(const char *name, void *unused_context)
&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 IDNA form: %s",
msg_warn("%s: Problem translating domain \"%s\" to ASCII form: %s",
myname, name, u_errorName(error));
return (0);
}
}
/* midna_ascii_to_utf8_create - convert ASCII domain to UTF8 */
/* midna_to_utf8_create - convert domain to UTF8 */
static void *midna_ascii_to_utf8_create(const char *name, void *unused_context)
static void *midna_to_utf8_create(const char *name, void *unused_context)
{
static const char myname[] = "midna_ascii_to_utf8_create";
static const char myname[] = "midna_to_utf8_create";
char buf[1024]; /* XXX */
UErrorCode error = U_ZERO_ERROR;
UIDNAInfo info = UIDNA_INFO_INITIALIZER;
@ -120,11 +153,15 @@ static void *midna_ascii_to_utf8_create(const char *name, void *unused_context)
/*
* Paranoia: do not expose uidna_*() to unfiltered network data.
*/
if (valid_hostname(name, DONT_GRIPE) == 0) {
msg_warn("%s: Problem translating domain \"%s\" to UTF8 form: %s",
myname, name, "malformed ASCII");
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),
@ -132,10 +169,18 @@ static void *midna_ascii_to_utf8_create(const char *name, void *unused_context)
&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 IDNA form: %s",
msg_warn("%s: Problem translating domain \"%s\" to UTF8 form: %s",
myname, name, u_errorName(error));
return (0);
}
@ -149,39 +194,76 @@ static void midna_cache_free(void *value, void *unused_context)
myfree(value);
}
/* midna_utf8_to_ascii - convert UTF8 hostname to ASCII */
/* midna_to_ascii - convert name to ASCII */
const char *midna_utf8_to_ascii(const char *name)
const char *midna_to_ascii(const char *name)
{
static CTABLE *midna_utf8_to_ascii_cache = 0;
static CTABLE *midna_to_ascii_cache = 0;
if (midna_utf8_to_ascii_cache == 0)
midna_utf8_to_ascii_cache = ctable_create(midna_cache_size,
midna_utf8_to_ascii_create,
midna_cache_free,
(void *) 0);
return (ctable_locate(midna_utf8_to_ascii_cache, name));
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_ascii_to_utf8 - convert UTF8 hostname to ASCII */
/* midna_to_utf8 - convert name to UTF8 */
const char *midna_ascii_to_utf8(const char *name)
const char *midna_to_utf8(const char *name)
{
static CTABLE *midna_ascii_to_utf8_cache = 0;
static CTABLE *midna_to_utf8_cache = 0;
if (midna_ascii_to_utf8_cache == 0)
midna_ascii_to_utf8_cache = ctable_create(midna_cache_size,
midna_ascii_to_utf8_create,
midna_cache_free,
(void *) 0);
return (ctable_locate(midna_ascii_to_utf8_cache, name));
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 hostnames from stdin, reports invalid hostnames to
* stderr.
* Test program - reads names from stdin, reports invalid names to stderr.
*/
#include <stdlib.h>
#include <locale.h>
@ -207,26 +289,37 @@ int main(int argc, char **argv)
temp_utf8_kludge = 1;
while (vstring_fgets_nonl(buffer, VSTREAM_IN)) {
msg_info("testing: \"%s\"", bp = vstring_str(buffer));
bp = STR(buffer);
msg_info("> %s", bp);
while (ISSPACE(*bp))
bp++;
if (*bp == '#' || *bp == 0)
continue;
if (!allascii(bp)) {
ascii = midna_utf8_to_ascii(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\" -> \"%s\"", bp, ascii);
utf8 = midna_ascii_to_utf8(ascii);
msg_info("\"%s\" ->ascii \"%s\"", bp, ascii);
utf8 = midna_to_utf8(ascii);
if (utf8 != 0) {
msg_info("\"%s\" -> \"%s\" -> \"%s\"",
msg_info("\"%s\" ->ascii \"%s\" ->utf8 \"%s\"",
bp, ascii, utf8);
if (strcmp(utf8, bp) != 0)
msg_warn("\"%s\" != \"%s\"", bp, utf8);
}
}
} else {
utf8 = midna_ascii_to_utf8(bp);
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\" -> \"%s\"", bp, utf8);
ascii = midna_utf8_to_ascii(utf8);
msg_info("\"%s\" ->utf8 \"%s\"", bp, utf8);
ascii = midna_to_ascii(utf8);
if (ascii != 0) {
msg_info("\"%s\" -> \"%s\" -> \"%s\"",
msg_info("\"%s\" ->utf8 \"%s\" ->ascii \"%s\"",
bp, utf8, ascii);
if (strcmp(ascii, bp) != 0)
msg_warn("\"%s\" != \"%s\"", bp, ascii);
@ -237,6 +330,6 @@ int main(int argc, char **argv)
exit(0);
}
#endif
#endif /* TEST */
#endif
#endif /* NO_EAI */

View File

@ -14,8 +14,10 @@
/*
* External interface.
*/
extern const char *midna_utf8_to_ascii(const char *);
extern const char *midna_ascii_to_utf8(const char *);
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 *);
/* LICENSE
/* .ad

View File

@ -0,0 +1,14 @@
# 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
xexample.com
x。example.com

View File

@ -0,0 +1,71 @@
./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: > xexample.com
./midna: ctable_locate: leave existing entry key x.example.com
./midna: ctable_locate: install entry key xexample.com
./midna: "xexample.com" ->utf8 "x.example.com"
./midna: ctable_locate: install entry key xexample.com
./midna: "xexample.com" ->ascii "x.example.com"
./midna: ctable_locate: move existing entry key x.example.com
./midna: "xexample.com" ->ascii "x.example.com" ->utf8 "x.example.com"
./midna: warning: "xexample.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"

View File

@ -68,7 +68,7 @@ int valid_utf8_hostname(int enable_utf8, const char *name, int gripe)
*/
#ifndef NO_EAI
if (enable_utf8 && !allascii(name)) {
if ((aname = midna_utf8_to_ascii(name)) == 0) {
if ((aname = midna_to_ascii(name)) == 0) {
if (gripe)
msg_warn("%s: malformed UTF-8 domain name", myname);
return (0);