This feature is available in Postfix 2.9 and later.
+%PARAM smtp_address_verify_target 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. +
+ +This feature is available in Postfix 2.12 and later.
+ +%PARAM lmtp_address_verify_target rcpt + +The LMTP-specific version of the smtp_address_verify_target +configuration parameter. See there for details.
+ +This feature is available in Postfix 2.12 and later.
+ %PARAM daemon_table_open_error_is_fatal noHow a Postfix daemon process handles errors while opening lookup diff -ur --exclude=html --exclude=README_FILES /var/tmp/postfix-2.11.3/src/global/mail_params.h ./src/global/mail_params.h --- /var/tmp/postfix-2.11.3/src/global/mail_params.h 2013-12-28 20:27:16.000000000 -0500 +++ ./src/global/mail_params.h 2014-12-28 11:55:09.000000000 -0500 @@ -2733,6 +2733,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.11.3/src/smtp/lmtp_params.c ./src/smtp/lmtp_params.c --- /var/tmp/postfix-2.11.3/src/smtp/lmtp_params.c 2013-08-25 15:49:34.000000000 -0400 +++ ./src/smtp/lmtp_params.c 2014-12-28 11:54:07.000000000 -0500 @@ -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, diff -ur --exclude=html --exclude=README_FILES /var/tmp/postfix-2.11.3/src/smtp/smtp.c ./src/smtp/smtp.c --- /var/tmp/postfix-2.11.3/src/smtp/smtp.c 2013-11-26 17:58:51.000000000 -0500 +++ ./src/smtp/smtp.c 2014-12-28 11:57:14.000000000 -0500 @@ -195,8 +195,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: @@ -664,6 +665,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 @@ -795,6 +801,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; @@ -1051,6 +1058,11 @@ */ smtp_dns_res_opt = name_mask(SMTP_X(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.11.3/src/smtp/smtp.h ./src/smtp/smtp.h --- /var/tmp/postfix-2.11.3/src/smtp/smtp.h 2014-05-07 13:17:29.000000000 -0400 +++ ./src/smtp/smtp.h 2014-12-28 11:54:07.000000000 -0500 @@ -365,6 +365,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 *); @@ -484,6 +485,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.11.3/src/smtp/smtp_params.c ./src/smtp/smtp_params.c --- /var/tmp/postfix-2.11.3/src/smtp/smtp_params.c 2013-08-25 15:49:34.000000000 -0400 +++ ./src/smtp/smtp_params.c 2014-12-28 11:54:07.000000000 -0500 @@ -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, diff -ur --exclude=html --exclude=README_FILES /var/tmp/postfix-2.11.3/src/smtp/smtp_proto.c ./src/smtp/smtp_proto.c --- /var/tmp/postfix-2.11.3/src/smtp/smtp_proto.c 2014-01-03 19:56:24.000000000 -0500 +++ ./src/smtp/smtp_proto.c 2014-12-28 11:54:07.000000000 -0500 @@ -254,6 +254,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) @@ -1455,7 +1473,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; @@ -1722,7 +1741,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); } @@ -1736,7 +1756,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; @@ -1747,6 +1768,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, STR(iter->host), resp, @@ -1756,7 +1778,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; /*