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, global/mail_params.h, smtp/lmtp_params.c,
	smtp/smtp.c, smtp/smtp.h, smtp/smtp_params.c, smtp/smtp_proto.c.

diff -ur --exclude=html --exclude=README_FILES /var/tmp/postfix-2.10.5/man/man5/postconf.5 ./man/man5/postconf.5
--- /var/tmp/postfix-2.10.5/man/man5/postconf.5	2013-06-13 10:06:06.000000000 -0400
+++ ./man/man5/postconf.5	2014-12-28 12:34:49.000000000 -0500
@@ -2241,6 +2241,11 @@
 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_address_verify_target
+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
@@ -5608,6 +5613,13 @@
 .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.
+.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
diff -ur --exclude=html --exclude=README_FILES /var/tmp/postfix-2.10.5/man/man8/smtp.8 ./man/man8/smtp.8
--- /var/tmp/postfix-2.10.5/man/man8/smtp.8	2012-07-17 15:40:55.000000000 -0400
+++ ./man/man8/smtp.8	2014-12-28 12:11:28.000000000 -0500
@@ -219,8 +219,9 @@
 .PP
 Available in Postfix version 2.2.9 and later:
 .IP "\fBsmtp_cname_overrides_servername (version dependent)\fR"
-Allow DNS CNAME records to override the servername that the
-Postfix SMTP client uses for logging, SASL password lookup, TLS
+When the remote SMTP servername is a DNS CNAME, replace the
+servername with the result from CNAME expansion for the purpose of
+logging, SASL password lookup, TLS
 policy decisions, or TLS certificate verification.
 .PP
 Available in Postfix version 2.3 and later:
@@ -683,6 +684,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.
 .SH "SEE ALSO"
 .na
 .nf
diff -ur --exclude=html --exclude=README_FILES /var/tmp/postfix-2.10.5/mantools/postlink ./mantools/postlink
--- /var/tmp/postfix-2.10.5/mantools/postlink	2013-02-03 13:22:21.000000000 -0500
+++ ./mantools/postlink	2014-12-28 12:11:28.000000000 -0500
@@ -89,6 +89,8 @@
     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_commands\b;<a href="postconf.5.html#allow_mail_to_commands">$&</a>;g;
diff -ur --exclude=html --exclude=README_FILES /var/tmp/postfix-2.10.5/proto/postconf.proto ./proto/postconf.proto
--- /var/tmp/postfix-2.10.5/proto/postconf.proto	2013-06-13 09:48:55.000000000 -0400
+++ ./proto/postconf.proto	2014-12-28 12:34:23.000000000 -0500
@@ -14875,6 +14875,23 @@
 
 <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.
+</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_address_verify_target
+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
diff -ur --exclude=html --exclude=README_FILES /var/tmp/postfix-2.10.5/src/global/mail_params.h ./src/global/mail_params.h
--- /var/tmp/postfix-2.10.5/src/global/mail_params.h	2013-06-22 19:57:19.000000000 -0400
+++ ./src/global/mail_params.h	2014-12-28 12:11:28.000000000 -0500
@@ -2688,6 +2688,14 @@
 #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.
   */
diff -ur --exclude=html --exclude=README_FILES /var/tmp/postfix-2.10.5/src/smtp/lmtp_params.c ./src/smtp/lmtp_params.c
--- /var/tmp/postfix-2.10.5/src/smtp/lmtp_params.c	2011-12-05 16:03:07.000000000 -0500
+++ ./src/smtp/lmtp_params.c	2014-12-28 12:11:28.000000000 -0500
@@ -32,6 +32,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_CACHE_DEST, DEF_LMTP_CACHE_DEST, &var_smtp_cache_dest, 0, 0,
diff -ur --exclude=html --exclude=README_FILES /var/tmp/postfix-2.10.5/src/smtp/smtp.c ./src/smtp/smtp.c
--- /var/tmp/postfix-2.10.5/src/smtp/smtp.c	2012-07-17 15:40:55.000000000 -0400
+++ ./src/smtp/smtp.c	2014-12-28 12:11:28.000000000 -0500
@@ -197,8 +197,9 @@
 /* .PP
 /*	Available in Postfix version 2.2.9 and later:
 /* .IP "\fBsmtp_cname_overrides_servername (version dependent)\fR"
-/*	Allow DNS CNAME records to override the servername that the
-/*	Postfix SMTP client uses for logging, SASL password lookup, TLS
+/*	When the remote SMTP servername is a DNS CNAME, replace the
+/*	servername with the result from CNAME expansion for the purpose of
+/*	logging, SASL password lookup, TLS
 /*	policy decisions, or TLS certificate verification.
 /* .PP
 /*	Available in Postfix version 2.3 and later:
@@ -645,6 +646,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
@@ -776,6 +782,7 @@
 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_smtp_pix_delay;
@@ -1005,6 +1012,11 @@
      */
     smtp_dns_res_opt = name_mask(VAR_SMTP_DNS_RES_OPT, dns_res_opt_masks,
 				 var_smtp_dns_res_opt);
+
+    /*
+     * Address verification.
+     */
+    smtp_vrfy_init();
 }
 
 /* pre_init - pre-jail initialization */
diff -ur --exclude=html --exclude=README_FILES /var/tmp/postfix-2.10.5/src/smtp/smtp.h ./src/smtp/smtp.h
--- /var/tmp/postfix-2.10.5/src/smtp/smtp.h	2010-08-27 20:03:15.000000000 -0400
+++ ./src/smtp/smtp.h	2014-12-28 12:11:28.000000000 -0500
@@ -269,6 +269,7 @@
  /*
   * 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 *);
@@ -386,6 +387,11 @@
 
 #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
diff -ur --exclude=html --exclude=README_FILES /var/tmp/postfix-2.10.5/src/smtp/smtp_params.c ./src/smtp/smtp_params.c
--- /var/tmp/postfix-2.10.5/src/smtp/smtp_params.c	2011-12-05 16:03:07.000000000 -0500
+++ ./src/smtp/smtp_params.c	2014-12-28 12:11:28.000000000 -0500
@@ -33,6 +33,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_CACHE_DEST, DEF_SMTP_CACHE_DEST, &var_smtp_cache_dest, 0, 0,
diff -ur --exclude=html --exclude=README_FILES /var/tmp/postfix-2.10.5/src/smtp/smtp_proto.c ./src/smtp/smtp_proto.c
--- /var/tmp/postfix-2.10.5/src/smtp/smtp_proto.c	2012-01-14 19:41:15.000000000 -0500
+++ ./src/smtp/smtp_proto.c	2014-12-28 12:11:28.000000000 -0500
@@ -250,6 +250,24 @@
     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)
@@ -1437,7 +1455,8 @@
 					   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;
 
@@ -1704,7 +1723,8 @@
 			    }
 			    ++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);
 			    }
@@ -1718,7 +1738,8 @@
 		    }
 		    /* 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;
@@ -1729,6 +1750,7 @@
 		     * 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, session->host, resp,
@@ -1738,7 +1760,30 @@
 					   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;
 
 		    /*
