diff --git a/postfix/HISTORY b/postfix/HISTORY
index 98f384fee..ebc0f1657 100644
--- a/postfix/HISTORY
+++ b/postfix/HISTORY
@@ -14048,3 +14048,8 @@ Apologies for any names omitted.
Cleanup: don't try sending HELO after a 421 EHLO reply.
File: smtp/smtp_proto.c.
+
+20071222
+
+ Further polishing of the Milter code and logging. File:
+ milter/milter8.c.
diff --git a/postfix/html/smtp-sink.1.html b/postfix/html/smtp-sink.1.html
index fe778618b..41bddc37d 100644
--- a/postfix/html/smtp-sink.1.html
+++ b/postfix/html/smtp-sink.1.html
@@ -135,8 +135,8 @@ SMTP-SINK(1) SMTP-SINK(1)
shell. Command names are case-insensitive.
-Q command,command,...
- Disconnect after sending a 431 reply after receiv-
- ing one of the specified commands.
+ Send a 421 reply and disconnect after receiving one
+ of the specified commands.
Examples of commands are CONNECT, HELO, EHLO, LHLO,
MAIL, RCPT, VRFY, DATA, ., RSET, NOOP, and QUIT.
diff --git a/postfix/man/man1/smtp-sink.1 b/postfix/man/man1/smtp-sink.1
index 07818735b..d040ed8f0 100644
--- a/postfix/man/man1/smtp-sink.1
+++ b/postfix/man/man1/smtp-sink.1
@@ -118,7 +118,7 @@ DATA, ., RSET, NOOP, and QUIT. Separate command names by
white space or commas, and use quotes to protect white space
from the shell. Command names are case-insensitive.
.IP "\fB-Q \fIcommand,command,...\fR"
-Disconnect after sending a 431 reply after receiving one
+Send a 421 reply and disconnect after receiving one
of the specified commands.
.sp
Examples of commands are CONNECT, HELO, EHLO, LHLO, MAIL, RCPT, VRFY,
diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h
index 993237c64..e6708f71a 100644
--- a/postfix/src/global/mail_version.h
+++ b/postfix/src/global/mail_version.h
@@ -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 "20071221"
+#define MAIL_RELEASE_DATE "20071222"
#define MAIL_VERSION_NUMBER "2.5"
#ifdef SNAPSHOT
diff --git a/postfix/src/milter/milter8.c b/postfix/src/milter/milter8.c
index 09437630f..43d2d7800 100644
--- a/postfix/src/milter/milter8.c
+++ b/postfix/src/milter/milter8.c
@@ -123,6 +123,26 @@
/* Introduced with Sendmail 8.14. */
#define SMFIC_QUIT_NC 'K' /* Quit + new connection */
+static NAME_CODE smfic_table[] = {
+ "SMFIC_ABORT", SMFIC_ABORT,
+ "SMFIC_BODY", SMFIC_BODY,
+ "SMFIC_CONNECT", SMFIC_CONNECT,
+ "SMFIC_MACRO", SMFIC_MACRO,
+ "SMFIC_BODYEOB", SMFIC_BODYEOB,
+ "SMFIC_HELO", SMFIC_HELO,
+ "SMFIC_HEADER", SMFIC_HEADER,
+ "SMFIC_MAIL", SMFIC_MAIL,
+ "SMFIC_EOH", SMFIC_EOH,
+ "SMFIC_OPTNEG", SMFIC_OPTNEG,
+ "SMFIC_QUIT", SMFIC_QUIT,
+ "SMFIC_RCPT", SMFIC_RCPT,
+ "SMFIC_DATA", SMFIC_DATA,
+ "SMFIC_UNKNOWN", SMFIC_UNKNOWN,
+ /* Introduced with Sendmail 8.14. */
+ "SMFIC_QUIT_NC", SMFIC_QUIT_NC,
+ 0, 0,
+};
+
/*
* Responses from filter to MTA.
*/
@@ -148,6 +168,31 @@
#define SMFIR_ADDRCPT_PAR '2' /* add recipient (incl. ESMTP args) */
#define SMFIR_SETSYMLIST 'l' /* set list of symbols (macros) */
+static NAME_CODE smfir_table[] = {
+ "SMFIR_ADDRCPT", SMFIR_ADDRCPT,
+ "SMFIR_DELRCPT", SMFIR_DELRCPT,
+ "SMFIR_ACCEPT", SMFIR_ACCEPT,
+ "SMFIR_REPLBODY", SMFIR_REPLBODY,
+ "SMFIR_CONTINUE", SMFIR_CONTINUE,
+ "SMFIR_DISCARD", SMFIR_DISCARD,
+ "SMFIR_CONN_FAIL", SMFIR_CONN_FAIL,
+ "SMFIR_CHGHEADER", SMFIR_CHGHEADER,
+ "SMFIR_PROGRESS", SMFIR_PROGRESS,
+ "SMFIR_REJECT", SMFIR_REJECT,
+ "SMFIR_TEMPFAIL", SMFIR_TEMPFAIL,
+ "SMFIR_SHUTDOWN", SMFIR_SHUTDOWN,
+ "SMFIR_ADDHEADER", SMFIR_ADDHEADER,
+ "SMFIR_INSHEADER", SMFIR_INSHEADER,
+ "SMFIR_REPLYCODE", SMFIR_REPLYCODE,
+ "SMFIR_QUARANTINE", SMFIR_QUARANTINE,
+ /* Introduced with Sendmail 8.14. */
+ "SMFIR_SKIP", SMFIR_SKIP,
+ "SMFIR_CHGFROM", SMFIR_CHGFROM,
+ "SMFIR_ADDRCPT_PAR", SMFIR_ADDRCPT_PAR,
+ "SMFIR_SETSYMLIST", SMFIR_SETSYMLIST,
+ 0, 0,
+};
+
/*
* Commands that the filter does not want to receive, and replies that the
* filter will not send. Plus some other random stuff.
@@ -186,6 +231,32 @@
| SMFIP_NR_DATA | SMFIP_NR_UNKN | SMFIP_NR_HDR | SMFIP_NR_EOH | \
SMFIP_NR_BODY)
+static NAME_MASK smfip_table[] = {
+ "SMFIP_NOCONNECT", SMFIP_NOCONNECT,
+ "SMFIP_NOHELO", SMFIP_NOHELO,
+ "SMFIP_NOMAIL", SMFIP_NOMAIL,
+ "SMFIP_NORCPT", SMFIP_NORCPT,
+ "SMFIP_NOBODY", SMFIP_NOBODY,
+ "SMFIP_NOHDRS", SMFIP_NOHDRS,
+ "SMFIP_NOEOH", SMFIP_NOEOH,
+ "SMFIP_NR_HDR", SMFIP_NR_HDR,
+ "SMFIP_NOUNKNOWN", SMFIP_NOUNKNOWN,
+ "SMFIP_NODATA", SMFIP_NODATA,
+ /* Introduced with Sendmail 8.14. */
+ "SMFIP_SKIP", SMFIP_SKIP,
+ "SMFIP_RCPT_REJ", SMFIP_RCPT_REJ,
+ "SMFIP_NR_CONN", SMFIP_NR_CONN,
+ "SMFIP_NR_HELO", SMFIP_NR_HELO,
+ "SMFIP_NR_MAIL", SMFIP_NR_MAIL,
+ "SMFIP_NR_RCPT", SMFIP_NR_RCPT,
+ "SMFIP_NR_DATA", SMFIP_NR_DATA,
+ "SMFIP_NR_UNKN", SMFIP_NR_UNKN,
+ "SMFIP_NR_EOH", SMFIP_NR_EOH,
+ "SMFIP_NR_BODY", SMFIP_NR_BODY,
+ "SMFIP_HDR_LEADSPC", SMFIP_HDR_LEADSPC,
+ 0, 0,
+};
+
/*
* Options that the filter may send at initial handshake time, and message
* modifications that the filter may request at the end of the message body.
@@ -201,6 +272,20 @@
#define SMFIF_ADDRCPT_PAR (1L<<7) /* filter may add recipients + args */
#define SMFIF_SETSYMLIST (1L<<8) /* filter may send macro names */
+static NAME_MASK smfif_table[] = {
+ "SMFIF_ADDHDRS", SMFIF_ADDHDRS,
+ "SMFIF_CHGBODY", SMFIF_CHGBODY,
+ "SMFIF_ADDRCPT", SMFIF_ADDRCPT,
+ "SMFIF_DELRCPT", SMFIF_DELRCPT,
+ "SMFIF_CHGHDRS", SMFIF_CHGHDRS,
+ "SMFIF_QUARANTINE", SMFIF_QUARANTINE,
+ /* Introduced with Sendmail 8.14. */
+ "SMFIF_CHGFROM", SMFIF_CHGFROM,
+ "SMFIF_ADDRCPT_PAR", SMFIF_ADDRCPT_PAR,
+ "SMFIF_SETSYMLIST", SMFIF_SETSYMLIST,
+ 0, 0,
+};
+
/*
* Network protocol families, used when sending CONNECT information.
*/
@@ -213,7 +298,6 @@
* External macro set numbers, to identify the optional macro name lists
* that may be sent after the initial negotiation header.
*/
-#define SMFIM_FIRST 0
#define SMFIM_CONNECT 0 /* macros for connect */
#define SMFIM_HELO 1 /* macros for HELO */
#define SMFIM_ENVFROM 2 /* macros for MAIL */
@@ -221,7 +305,17 @@
#define SMFIM_DATA 4 /* macros for DATA */
#define SMFIM_EOM 5 /* macros for end-of-message */
#define SMFIM_EOH 6 /* macros for end-of-header */
-#define SMFIM_LAST 6
+
+static NAME_CODE smfim_table[] = {
+ "SMFIM_CONNECT", SMFIM_CONNECT,
+ "SMFIM_HELO", SMFIM_HELO,
+ "SMFIM_ENVFROM", SMFIM_ENVFROM,
+ "SMFIM_ENVRCPT", SMFIM_ENVRCPT,
+ "SMFIM_DATA", SMFIM_DATA,
+ "SMFIM_EOM", SMFIM_EOM,
+ "SMFIM_EOH", SMFIM_EOH,
+ 0, 0,
+};
/*
* Mapping from external macro set numbers to our internal MILTERS structure
@@ -308,7 +402,7 @@ typedef struct {
#define MILTER8_DATA_NSHORT 4 /* network short */
#define MILTER8_DATA_ARGV 5 /* array of null-terminated strings */
#define MILTER8_DATA_OCTET 6 /* byte */
-#define MILTER8_DATA_MACROS 7 /* macro lists (receive-only) */
+#define MILTER8_DATA_MORE 7 /* more arguments in next call */
/*
* We don't accept insane amounts of data.
@@ -375,95 +469,6 @@ static NAME_CODE milter8_versions[] = {
0, -1,
};
- /*
- * Tables to map the above symbolic constants to printable strings. We use
- * NAME_CODE for commands and replies, and NAME_MASK for bit mask values.
- */
-static NAME_CODE smfic_table[] = {
- "SMFIC_ABORT", SMFIC_ABORT,
- "SMFIC_BODY", SMFIC_BODY,
- "SMFIC_CONNECT", SMFIC_CONNECT,
- "SMFIC_MACRO", SMFIC_MACRO,
- "SMFIC_BODYEOB", SMFIC_BODYEOB,
- "SMFIC_HELO", SMFIC_HELO,
- "SMFIC_HEADER", SMFIC_HEADER,
- "SMFIC_MAIL", SMFIC_MAIL,
- "SMFIC_EOH", SMFIC_EOH,
- "SMFIC_OPTNEG", SMFIC_OPTNEG,
- "SMFIC_QUIT", SMFIC_QUIT,
- "SMFIC_RCPT", SMFIC_RCPT,
- "SMFIC_DATA", SMFIC_DATA,
- "SMFIC_UNKNOWN", SMFIC_UNKNOWN,
- /* Introduced with Sendmail 8.14. */
- "SMFIC_QUIT_NC", SMFIC_QUIT_NC,
- 0, 0,
-};
-
-static NAME_CODE smfir_table[] = {
- "SMFIR_ADDRCPT", SMFIR_ADDRCPT,
- "SMFIR_DELRCPT", SMFIR_DELRCPT,
- "SMFIR_ACCEPT", SMFIR_ACCEPT,
- "SMFIR_REPLBODY", SMFIR_REPLBODY,
- "SMFIR_CONTINUE", SMFIR_CONTINUE,
- "SMFIR_DISCARD", SMFIR_DISCARD,
- "SMFIR_CONN_FAIL", SMFIR_CONN_FAIL,
- "SMFIR_CHGHEADER", SMFIR_CHGHEADER,
- "SMFIR_PROGRESS", SMFIR_PROGRESS,
- "SMFIR_REJECT", SMFIR_REJECT,
- "SMFIR_TEMPFAIL", SMFIR_TEMPFAIL,
- "SMFIR_SHUTDOWN", SMFIR_SHUTDOWN,
- "SMFIR_ADDHEADER", SMFIR_ADDHEADER,
- "SMFIR_INSHEADER", SMFIR_INSHEADER,
- "SMFIR_REPLYCODE", SMFIR_REPLYCODE,
- "SMFIR_QUARANTINE", SMFIR_QUARANTINE,
- /* Introduced with Sendmail 8.14. */
- "SMFIR_SKIP", SMFIR_SKIP,
- "SMFIR_CHGFROM", SMFIR_CHGFROM,
- "SMFIR_ADDRCPT_PAR", SMFIR_ADDRCPT_PAR,
- "SMFIR_SETSYMLIST", SMFIR_SETSYMLIST,
- 0, 0,
-};
-
-static NAME_MASK smfip_table[] = {
- "SMFIP_NOCONNECT", SMFIP_NOCONNECT,
- "SMFIP_NOHELO", SMFIP_NOHELO,
- "SMFIP_NOMAIL", SMFIP_NOMAIL,
- "SMFIP_NORCPT", SMFIP_NORCPT,
- "SMFIP_NOBODY", SMFIP_NOBODY,
- "SMFIP_NOHDRS", SMFIP_NOHDRS,
- "SMFIP_NOEOH", SMFIP_NOEOH,
- "SMFIP_NR_HDR", SMFIP_NR_HDR,
- "SMFIP_NOUNKNOWN", SMFIP_NOUNKNOWN,
- "SMFIP_NODATA", SMFIP_NODATA,
- /* Introduced with Sendmail 8.14. */
- "SMFIP_SKIP", SMFIP_SKIP,
- "SMFIP_RCPT_REJ", SMFIP_RCPT_REJ,
- "SMFIP_NR_CONN", SMFIP_NR_CONN,
- "SMFIP_NR_HELO", SMFIP_NR_HELO,
- "SMFIP_NR_MAIL", SMFIP_NR_MAIL,
- "SMFIP_NR_RCPT", SMFIP_NR_RCPT,
- "SMFIP_NR_DATA", SMFIP_NR_DATA,
- "SMFIP_NR_UNKN", SMFIP_NR_UNKN,
- "SMFIP_NR_EOH", SMFIP_NR_EOH,
- "SMFIP_NR_BODY", SMFIP_NR_BODY,
- "SMFIP_HDR_LEADSPC", SMFIP_HDR_LEADSPC,
- 0, 0,
-};
-
-static NAME_MASK smfif_table[] = {
- "SMFIF_ADDHDRS", SMFIF_ADDHDRS,
- "SMFIF_CHGBODY", SMFIF_CHGBODY,
- "SMFIF_ADDRCPT", SMFIF_ADDRCPT,
- "SMFIF_DELRCPT", SMFIF_DELRCPT,
- "SMFIF_CHGHDRS", SMFIF_CHGHDRS,
- "SMFIF_QUARANTINE", SMFIF_QUARANTINE,
- /* Introduced with Sendmail 8.14. */
- "SMFIF_CHGFROM", SMFIF_CHGFROM,
- "SMFIF_ADDRCPT_PAR", SMFIF_ADDRCPT_PAR,
- "SMFIF_SETSYMLIST", SMFIF_SETSYMLIST,
- 0, 0,
-};
-
/* SLMs. */
#define STR(x) vstring_str(x)
@@ -613,11 +618,8 @@ static int vmilter8_read_data(MILTER8 *milter, ssize_t *data_len, va_list ap)
UINT32_TYPE *host_long_ptr;
VSTRING *buf;
int ch;
- int ret;
- UINT32_TYPE mac_type;
- char **mac_value_ptr;
- while ((arg_type = va_arg(ap, int)) > 0) {
+ while ((arg_type = va_arg(ap, int)) > 0 && arg_type != MILTER8_DATA_MORE) {
switch (arg_type) {
/*
@@ -690,34 +692,6 @@ static int vmilter8_read_data(MILTER8 *milter, ssize_t *data_len, va_list ap)
VSTRING_TERMINATE(buf);
break;
- /*
- * Sequence of macro (state, names) without explicit terminator.
- */
- case MILTER8_DATA_MACROS:
- if (*data_len <= 0)
- break;
- buf = vstring_alloc(100);
- ret = 0;
- while (*data_len > 0
- && (ret = milter8_read_data(milter, data_len,
- MILTER8_DATA_HLONG, &mac_type,
- MILTER8_DATA_STRING, buf,
- MILTER8_DATA_END)) == 0) {
- if (((unsigned) mac_type) > SMFIM_LAST) {
- msg_warn("milter %s: ignoring unknown macro type %u",
- milter->m.name, (unsigned) mac_type);
- } else {
- mac_value_ptr = MILTER8_MACRO_PTR(milter->m.parent, mac_type);
- if (*mac_value_ptr != 0)
- myfree(*mac_value_ptr);
- *mac_value_ptr = mystrdup(STR(buf));
- }
- }
- vstring_free(buf);
- if (ret != 0)
- return (ret);
- break;
-
/*
* Error.
*/
@@ -730,7 +704,7 @@ static int vmilter8_read_data(MILTER8 *milter, ssize_t *data_len, va_list ap)
* Sanity checks. We may have excess data when the sender is confused. We
* may have a negative count when we're confused ourselves.
*/
- if (*data_len > 0) {
+ if (arg_type != MILTER8_DATA_MORE && *data_len > 0) {
msg_warn("%s: left-over data %ld bytes", myname, (long) *data_len);
return (milter8_comm_error(milter));
}
@@ -1079,7 +1053,7 @@ static const char *milter8_event(MILTER8 *milter, int event,
*/
if (skip_reply) {
if (msg_verbose)
- msg_info("skipping reply %s for milter %s",
+ msg_info("skipping reply for event %s from milter %s",
(smfic_name = str_name_code(smfic_table, event)) != 0 ?
smfic_name : "(unknown MTA event)", milter->m.name);
return (milter->def_reply);
@@ -1557,7 +1531,6 @@ static void milter8_connect(MILTER8 *milter)
#endif
| SMFIF_SETSYMLIST
);
-
UINT32_TYPE my_version = 0;
UINT32_TYPE my_events = 0;
char *saved_version;
@@ -1689,59 +1662,100 @@ static void milter8_connect(MILTER8 *milter)
MILTER8_DATA_END) != 0) {
msg_warn("milter %s: write error in initial handshake",
milter->m.name);
+ /* milter8_write_cmd() called milter8_comm_error() */
+ return;
}
/*
* Receive the filter's response and verify that we are compatible.
*/
- else if (milter8_read_resp(milter, SMFIC_OPTNEG, &cmd, &data_len) != 0) {
+ if (milter8_read_resp(milter, SMFIC_OPTNEG, &cmd, &data_len) != 0) {
msg_warn("milter %s: read error in initial handshake", milter->m.name);
/* milter8_read_resp() called milter8_comm_error() */
- } else if (cmd != SMFIC_OPTNEG) {
+ return;
+ }
+ if (cmd != SMFIC_OPTNEG) {
msg_warn("milter %s: unexpected reply \"%c\" in initial handshake",
milter->m.name, cmd);
(void) milter8_comm_error(milter);
- } else if (milter8_read_data(milter, &data_len,
- MILTER8_DATA_HLONG, &milter->version,
- MILTER8_DATA_HLONG, &milter->rq_mask,
- MILTER8_DATA_HLONG, &milter->ev_mask,
- MILTER8_DATA_MACROS,
- MILTER8_DATA_END) != 0) {
+ return;
+ }
+ if (milter8_read_data(milter, &data_len,
+ MILTER8_DATA_HLONG, &milter->version,
+ MILTER8_DATA_HLONG, &milter->rq_mask,
+ MILTER8_DATA_HLONG, &milter->ev_mask,
+ MILTER8_DATA_MORE) != 0) {
msg_warn("milter %s: read error in initial handshake", milter->m.name);
/* milter8_read_data() called milter8_comm_error() */
- } else if (milter->version > my_version) {
+ return;
+ }
+ if (milter->version > my_version) {
msg_warn("milter %s: protocol version %d conflict"
" with MTA protocol version %d",
milter->m.name, milter->version, my_version);
(void) milter8_comm_error(milter);
- } else if ((milter->rq_mask & my_actions) != milter->rq_mask) {
+ return;
+ }
+ if ((milter->rq_mask & my_actions) != milter->rq_mask) {
msg_warn("milter %s: request mask 0x%x conflict"
" with MTA request mask 0x%lx",
milter->m.name, milter->rq_mask, (long) my_actions);
(void) milter8_comm_error(milter);
+ return;
}
/*
- * Successful negotiations completed.
+ * Initial negotiations completed.
*/
- else {
- if (msg_verbose) {
- if ((milter->ev_mask & my_events) != milter->ev_mask)
- msg_info("milter %s: event mask 0x%x includes features not"
- " offered in MTA event mask 0x%lx",
- milter->m.name, milter->ev_mask, (long) my_events);
- msg_info("%s: milter %s version %d",
- myname, milter->m.name, milter->version);
- msg_info("%s: events %s", myname,
- str_name_mask_opt(milter->buf, "event mask",
+ if (msg_verbose) {
+ if ((milter->ev_mask & my_events) != milter->ev_mask)
+ msg_info("milter %s: event mask 0x%x includes features not"
+ " offered in MTA event mask 0x%lx",
+ milter->m.name, milter->ev_mask, (long) my_events);
+ msg_info("%s: milter %s version %d",
+ myname, milter->m.name, milter->version);
+ msg_info("%s: events %s", myname,
+ str_name_mask_opt(milter->buf, "event mask",
smfip_table, milter->ev_mask, NAME_MASK_NUMBER));
- msg_info("%s: requests %s", myname,
- str_name_mask_opt(milter->buf, "request mask",
+ msg_info("%s: requests %s", myname,
+ str_name_mask_opt(milter->buf, "request mask",
smfif_table, milter->rq_mask, NAME_MASK_NUMBER));
+ }
+ milter->state = MILTER8_STAT_READY;
+ milter8_def_reply(milter, 0);
+ milter->skip_event_type = 0;
+
+ /*
+ * Secondary negotiations: override lists of macro names.
+ */
+ if (data_len > 0) {
+ VSTRING *buf = vstring_alloc(100);
+ UINT32_TYPE mac_type;
+ const char *smfim_name;
+ char **mac_value_ptr;
+
+ while (data_len > 0
+ && milter8_read_data(milter, &data_len,
+ MILTER8_DATA_HLONG, &mac_type,
+ MILTER8_DATA_STRING, buf,
+ MILTER8_DATA_END) == 0) {
+ smfim_name = str_name_code(smfim_table, mac_type);
+ if (smfim_name == 0) {
+ msg_warn("milter %s: ignoring unknown macro type %u",
+ milter->m.name, (unsigned) mac_type);
+ } else {
+ if (msg_verbose)
+ msg_info("override %s macro list with \"%s\"",
+ smfim_name, STR(buf));
+ mac_value_ptr = MILTER8_MACRO_PTR(milter->m.parent, mac_type);
+ if (*mac_value_ptr != 0)
+ myfree(*mac_value_ptr);
+ *mac_value_ptr = mystrdup(STR(buf));
+ }
}
- milter->state = MILTER8_STAT_READY;
- milter8_def_reply(milter, 0);
- milter->skip_event_type = 0;
+ /* milter8_read_data() calls milter8_comm_error() after error. */
+ vstring_free(buf);
+ /* At this point the filter state is either READY or ERROR. */
}
}
diff --git a/postfix/src/smtpstone/smtp-sink.c b/postfix/src/smtpstone/smtp-sink.c
index 8ce348703..d9aac9b08 100644
--- a/postfix/src/smtpstone/smtp-sink.c
+++ b/postfix/src/smtpstone/smtp-sink.c
@@ -112,7 +112,7 @@
/* white space or commas, and use quotes to protect white space
/* from the shell. Command names are case-insensitive.
/* .IP "\fB-Q \fIcommand,command,...\fR"
-/* Disconnect after sending a 431 reply after receiving one
+/* Send a 421 reply and disconnect after receiving one
/* of the specified commands.
/* .sp
/* Examples of commands are CONNECT, HELO, EHLO, LHLO, MAIL, RCPT, VRFY,