-/* $Cambridge: exim/src/src/smtp_in.c,v 1.54 2007/02/20 11:37:16 ph10 Exp $ */
+/* $Cambridge: exim/src/src/smtp_in.c,v 1.67 2010/06/12 15:21:26 jetmore Exp $ */
/*************************************************
* Exim - an Internet mail transport agent *
*************************************************/
-/* Copyright (c) University of Cambridge 1995 - 2007 */
+/* Copyright (c) University of Cambridge 1995 - 2009 */
/* See the file NOTICE for conditions of use and distribution. */
/* Functions for handling an incoming SMTP call. */
int allow_severity = LOG_INFO;
int deny_severity = LOG_NOTICE;
+uschar *tcp_wrappers_name;
#endif
static BOOL helo_accept_junk;
static BOOL count_nonmail;
static BOOL pipelining_advertised;
+static BOOL rcpt_smtp_response_same;
+static BOOL rcpt_in_progress;
static int nonmail_command_count;
+static BOOL smtp_exit_function_called = 0;
static int synprot_error_count;
static int unknown_command_count;
static int sync_cmd_limit;
static int smtp_write_error = 0;
+static uschar *rcpt_smtp_response;
static uschar *smtp_data_buffer;
static uschar *smtp_cmd_data;
else smtp_had_eof = 1;
return EOF;
}
+#ifndef DISABLE_DKIM
+ dkim_exim_verify_feed(smtp_inbuffer, rc);
+#endif
smtp_inend = smtp_inbuffer + rc;
smtp_inptr = smtp_inbuffer;
}
+/*************************************************
+* Test for characters in the SMTP buffer *
+*************************************************/
+
+/* Used at the end of a message
+
+Arguments: none
+Returns: TRUE/FALSE
+*/
+
+BOOL
+smtp_buffered(void)
+{
+return smtp_inptr < smtp_inend;
+}
+
+
/*************************************************
* Write formatted string to SMTP channel *
{
va_list ap;
+va_start(ap, format);
+smtp_vprintf(format, ap);
+va_end(ap);
+}
+
+/* This is split off so that verify.c:respond_printf() can, in effect, call
+smtp_printf(), bearing in mind that in C a vararg function can't directly
+call another vararg function, only a function which accepts a va_list. */
+
+void
+smtp_vprintf(char *format, va_list ap)
+{
+BOOL yield;
+
+yield = string_vformat(big_buffer, big_buffer_size, format, ap);
+
DEBUG(D_receive)
{
- uschar *cr, *end;
- va_start(ap, format);
- (void) string_vformat(big_buffer, big_buffer_size, format, ap);
- va_end(ap);
- end = big_buffer + Ustrlen(big_buffer);
- while ((cr = Ustrchr(big_buffer, '\r')) != NULL) /* lose CRs */
- memmove(cr, cr + 1, (end--) - cr);
- debug_printf("SMTP>> %s", big_buffer);
+ void *reset_point = store_get(0);
+ uschar *msg_copy, *cr, *end;
+ msg_copy = string_copy(big_buffer);
+ end = msg_copy + Ustrlen(msg_copy);
+ while ((cr = Ustrchr(msg_copy, '\r')) != NULL) /* lose CRs */
+ memmove(cr, cr + 1, (end--) - cr);
+ debug_printf("SMTP>> %s", msg_copy);
+ store_reset(reset_point);
}
-va_start(ap, format);
+if (!yield)
+ {
+ log_write(0, LOG_MAIN|LOG_PANIC, "string too large in smtp_printf()");
+ smtp_closedown(US"Unexpected error");
+ exim_exit(EXIT_FAILURE);
+ }
-/* If in a TLS session we have to format the string, and then write it using a
-TLS function. */
+/* If this is the first output for a (non-batch) RCPT command, see if all RCPTs
+have had the same. Note: this code is also present in smtp_respond(). It would
+be tidier to have it only in one place, but when it was added, it was easier to
+do it that way, so as not to have to mess with the code for the RCPT command,
+which sometimes uses smtp_printf() and sometimes smtp_respond(). */
+
+if (rcpt_in_progress)
+ {
+ if (rcpt_smtp_response == NULL)
+ rcpt_smtp_response = string_copy(big_buffer);
+ else if (rcpt_smtp_response_same &&
+ Ustrcmp(rcpt_smtp_response, big_buffer) != 0)
+ rcpt_smtp_response_same = FALSE;
+ rcpt_in_progress = FALSE;
+ }
+
+/* Now write the string */
#ifdef SUPPORT_TLS
if (tls_active >= 0)
{
- if (!string_vformat(big_buffer, big_buffer_size, format, ap))
- {
- log_write(0, LOG_MAIN|LOG_PANIC, "string too large in smtp_printf");
- smtp_closedown(US"Unexpected error");
- exim_exit(EXIT_FAILURE);
- }
if (tls_write(big_buffer, Ustrlen(big_buffer)) < 0) smtp_write_error = -1;
}
else
#endif
-/* Otherwise, just use the standard library function. */
-
-if (vfprintf(smtp_out, format, ap) < 0) smtp_write_error = -1;
-va_end(ap);
+if (fprintf(smtp_out, "%s", big_buffer) < 0) smtp_write_error = -1;
}
host_and_ident(FALSE));
if (smtp_batched_input)
moan_smtp_batch(NULL, "421 SMTP command timeout"); /* Does not return */
-smtp_printf("421 %s: SMTP command timeout - closing connection\r\n",
- smtp_active_hostname);
-mac_smtp_fflush();
+smtp_notquit_exit(US"command-timeout", US"421",
+ US"%s: SMTP command timeout - closing connection", smtp_active_hostname);
exim_exit(EXIT_FAILURE);
}
log_write(0, LOG_MAIN, "%s closed after SIGTERM", smtp_get_connection_info());
if (smtp_batched_input)
moan_smtp_batch(NULL, "421 SIGTERM received"); /* Does not return */
-smtp_printf("421 %s: Service not available - closing connection\r\n",
- smtp_active_hostname);
+smtp_notquit_exit(US"signal-exit", US"421",
+ US"%s: Service not available - closing connection", smtp_active_hostname);
exim_exit(EXIT_FAILURE);
}
+
/*************************************************
* Read one command line *
*************************************************/
+/*************************************************
+* Recheck synchronization *
+*************************************************/
+
+/* Synchronization checks can never be perfect because a packet may be on its
+way but not arrived when the check is done. Such checks can in any case only be
+done when TLS is not in use. Normally, the checks happen when commands are
+read: Exim ensures that there is no more input in the input buffer. In normal
+cases, the response to the command will be fast, and there is no further check.
+
+However, for some commands an ACL is run, and that can include delays. In those
+cases, it is useful to do another check on the input just before sending the
+response. This also applies at the start of a connection. This function does
+that check by means of the select() function, as long as the facility is not
+disabled or inappropriate. A failure of select() is ignored.
+
+When there is unwanted input, we read it so that it appears in the log of the
+error.
+
+Arguments: none
+Returns: TRUE if all is well; FALSE if there is input pending
+*/
+
+static BOOL
+check_sync(void)
+{
+int fd, rc;
+fd_set fds;
+struct timeval tzero;
+
+if (!smtp_enforce_sync || sender_host_address == NULL ||
+ sender_host_notsocket || tls_active >= 0)
+ return TRUE;
+
+fd = fileno(smtp_in);
+FD_ZERO(&fds);
+FD_SET(fd, &fds);
+tzero.tv_sec = 0;
+tzero.tv_usec = 0;
+rc = select(fd + 1, (SELECT_ARG2_TYPE *)&fds, NULL, NULL, &tzero);
+
+if (rc <= 0) return TRUE; /* Not ready to read */
+rc = smtp_getc();
+if (rc < 0) return TRUE; /* End of file or error */
+
+smtp_ungetc(rc);
+rc = smtp_inend - smtp_inptr;
+if (rc > 150) rc = 150;
+smtp_inptr[rc] = 0;
+return FALSE;
+}
+
+
+
/*************************************************
* Forced closedown of call *
*************************************************/
except QUIT. The existence of an SMTP call is detected by the non-NULLness of
smtp_in.
-Argument: SMTP reply string to send, excluding the code
+Arguments:
+ message SMTP reply string to send, excluding the code
+
Returns: nothing
*/
s = string_append(s, &size, &ptr, 2, US" CV=",
tls_certificate_verified? "yes":"no");
if ((log_extra_selector & LX_tls_peerdn) != 0 && tls_peerdn != NULL)
- s = string_append(s, &size, &ptr, 3, US" DN=\"", tls_peerdn, US"\"");
+ s = string_append(s, &size, &ptr, 3, US" DN=\"",
+ string_printing(tls_peerdn), US"\"");
#endif
sep = (smtp_connection_had[SMTP_HBUFF_SIZE-1] != SCH_NONE)?
message_size = -1;
acl_added_headers = NULL;
queue_only_policy = FALSE;
+rcpt_smtp_response = NULL;
+rcpt_smtp_response_same = TRUE;
+rcpt_in_progress = FALSE;
deliver_freeze = FALSE; /* Can be set by ACL */
freeze_tell = freeze_tell_config; /* Can be set by ACL */
fake_response = OK; /* Can be set by ACL */
bmi_run = 0;
bmi_verdicts = NULL;
#endif
-#ifdef EXPERIMENTAL_DOMAINKEYS
-dk_do_verify = 0;
+#ifndef DISABLE_DKIM
+dkim_signers = NULL;
+dkim_disable_verify = FALSE;
+dkim_collect_input = FALSE;
#endif
#ifdef EXPERIMENTAL_SPF
spf_header_comment = NULL;
pipelining_advertised = FALSE;
pipelining_enable = TRUE;
sync_cmd_limit = NON_SYNC_CMD_NON_PIPELINING;
+smtp_exit_function_called = FALSE; /* For avoiding loop in not-quit exit */
memset(sender_host_cache, 0, sizeof(sender_host_cache));
receive_ungetc = smtp_ungetc;
receive_feof = smtp_feof;
receive_ferror = smtp_ferror;
+receive_smtp_buffered = smtp_buffered;
smtp_inptr = smtp_inend = smtp_inbuffer;
smtp_had_eof = smtp_had_error = 0;
#ifdef USE_TCP_WRAPPERS
errno = 0;
- if (!hosts_ctl("exim",
+ tcp_wrappers_name = expand_string(tcp_wrappers_daemon_name);
+ if (tcp_wrappers_name == NULL)
+ {
+ log_write(0, LOG_MAIN|LOG_PANIC_DIE, "Expansion of \"%s\" "
+ "(tcp_wrappers_name) failed: %s", string_printing(tcp_wrappers_name),
+ expand_string_message);
+ }
+ if (!hosts_ctl(tcp_wrappers_name,
(sender_host_name == NULL)? STRING_UNKNOWN : CS sender_host_name,
(sender_host_address == NULL)? STRING_UNKNOWN : CS sender_host_address,
(sender_ident == NULL)? STRING_UNKNOWN : CS sender_ident))
/* Before we write the banner, check that there is no input pending, unless
this synchronisation check is disabled. */
-if (smtp_enforce_sync && sender_host_address != NULL && !sender_host_notsocket)
+if (!check_sync())
{
- fd_set fds;
- struct timeval tzero;
- tzero.tv_sec = 0;
- tzero.tv_usec = 0;
- FD_ZERO(&fds);
- FD_SET(fileno(smtp_in), &fds);
- if (select(fileno(smtp_in) + 1, (SELECT_ARG2_TYPE *)&fds, NULL, NULL,
- &tzero) > 0)
- {
- int rc = read(fileno(smtp_in), smtp_inbuffer, in_buffer_size);
- if (rc > 0)
- {
- if (rc > 150) rc = 150;
- smtp_inbuffer[rc] = 0;
- log_write(0, LOG_MAIN|LOG_REJECT, "SMTP protocol "
- "synchronization error (input sent without waiting for greeting): "
- "rejected connection from %s input=\"%s\"", host_and_ident(TRUE),
- string_printing(smtp_inbuffer));
- smtp_printf("554 SMTP synchronization error\r\n");
- return FALSE;
- }
- }
+ log_write(0, LOG_MAIN|LOG_REJECT, "SMTP protocol "
+ "synchronization error (input sent without waiting for greeting): "
+ "rejected connection from %s input=\"%s\"", host_and_ident(TRUE),
+ string_printing(smtp_inptr));
+ smtp_printf("554 SMTP synchronization error\r\n");
+ return FALSE;
}
/* Now output the banner */
esclen = codelen - 4;
}
+/* If this is the first output for a (non-batch) RCPT command, see if all RCPTs
+have had the same. Note: this code is also present in smtp_printf(). It would
+be tidier to have it only in one place, but when it was added, it was easier to
+do it that way, so as not to have to mess with the code for the RCPT command,
+which sometimes uses smtp_printf() and sometimes smtp_respond(). */
+
+if (rcpt_in_progress)
+ {
+ if (rcpt_smtp_response == NULL)
+ rcpt_smtp_response = string_copy(msg);
+ else if (rcpt_smtp_response_same &&
+ Ustrcmp(rcpt_smtp_response, msg) != 0)
+ rcpt_smtp_response_same = FALSE;
+ rcpt_in_progress = FALSE;
+ }
+
+/* Not output the message, splitting it up into multiple lines if necessary. */
+
for (;;)
{
uschar *nl = Ustrchr(msg, '\n');
if (sender_verified_failed != NULL &&
!testflag(sender_verified_failed, af_sverify_told))
{
+ BOOL save_rcpt_in_progress = rcpt_in_progress;
+ rcpt_in_progress = FALSE; /* So as not to treat these as the error */
+
setflag(sender_verified_failed, af_sverify_told);
if (rc != FAIL || (log_extra_selector & LX_sender_verify_fail) != 0)
"Verification failed for <%s>\n%s",
sender_verified_failed->address,
sender_verified_failed->user_message));
+
+ rcpt_in_progress = save_rcpt_in_progress;
}
/* Sort out text for logging */
log_write(L_smtp_connection, LOG_MAIN, "%s closed by DROP in ACL",
smtp_get_connection_info());
+
+/* Run the not-quit ACL, but without any custom messages. This should not be a
+problem, because we get here only if some other ACL has issued "drop", and
+in that case, *its* custom messages will have been used above. */
+
+smtp_notquit_exit(US"acl-drop", NULL, NULL);
return 2;
}
+/*************************************************
+* Handle SMTP exit when QUIT is not given *
+*************************************************/
+
+/* This function provides a logging/statistics hook for when an SMTP connection
+is dropped on the floor or the other end goes away. It's a global function
+because it's called from receive.c as well as this module. As well as running
+the NOTQUIT ACL, if there is one, this function also outputs a final SMTP
+response, either with a custom message from the ACL, or using a default. There
+is one case, however, when no message is output - after "drop". In that case,
+the ACL that obeyed "drop" has already supplied the custom message, and NULL is
+passed to this function.
+
+In case things go wrong while processing this function, causing an error that
+may re-enter this funtion, there is a recursion check.
+
+Arguments:
+ reason What $smtp_notquit_reason will be set to in the ACL;
+ if NULL, the ACL is not run
+ code The error code to return as part of the response
+ defaultrespond The default message if there's no user_msg
+
+Returns: Nothing
+*/
+
+void
+smtp_notquit_exit(uschar *reason, uschar *code, uschar *defaultrespond, ...)
+{
+int rc;
+uschar *user_msg = NULL;
+uschar *log_msg = NULL;
+
+/* Check for recursive acll */
+
+if (smtp_exit_function_called)
+ {
+ log_write(0, LOG_PANIC, "smtp_notquit_exit() called more than once (%s)",
+ reason);
+ return;
+ }
+smtp_exit_function_called = TRUE;
+
+/* Call the not-QUIT ACL, if there is one, unless no reason is given. */
+
+if (acl_smtp_notquit != NULL && reason != NULL)
+ {
+ smtp_notquit_reason = reason;
+ rc = acl_check(ACL_WHERE_NOTQUIT, NULL, acl_smtp_notquit, &user_msg,
+ &log_msg);
+ if (rc == ERROR)
+ log_write(0, LOG_MAIN|LOG_PANIC, "ACL for not-QUIT returned ERROR: %s",
+ log_msg);
+ }
+
+/* Write an SMTP response if we are expected to give one. As the default
+responses are all internal, they should always fit in the buffer, but code a
+warning, just in case. Note that string_vformat() still leaves a complete
+string, even if it is incomplete. */
+
+if (code != NULL && defaultrespond != NULL)
+ {
+ if (user_msg == NULL)
+ {
+ uschar buffer[128];
+ va_list ap;
+ va_start(ap, defaultrespond);
+ if (!string_vformat(buffer, sizeof(buffer), CS defaultrespond, ap))
+ log_write(0, LOG_MAIN|LOG_PANIC, "string too large in smtp_notquit_exit()");
+ smtp_printf("%s %s\r\n", code, buffer);
+ va_end(ap);
+ }
+ else
+ smtp_respond(code, 3, TRUE, user_msg);
+ mac_smtp_fflush();
+ }
+}
+
+
+
+
/*************************************************
* Verify HELO argument *
*************************************************/
spf_init(sender_helo_name, sender_host_address);
#endif
- /* Apply an ACL check if one is defined */
+ /* Apply an ACL check if one is defined; afterwards, recheck
+ synchronization in case the client started sending in a delay. */
if (acl_smtp_helo != NULL)
{
host_build_sender_fullhost(); /* Rebuild */
break;
}
+ else if (!check_sync()) goto SYNC_FAILURE;
}
/* Generate an OK reply. The default string includes the ident if present,
in order to be able to log the sender address on failure. */
if (strcmpic(name, US"SIZE") == 0 &&
- ((size = (int)Ustrtoul(value, &end, 10)), *end == 0))
+ ((size = Ustrtoul(value, &end, 10)), *end == 0))
{
if ((size == ULONG_MAX && errno == ERANGE) || size > INT_MAX)
size = INT_MAX;
}
}
- /* Apply an ACL check if one is defined, before responding */
+ /* Apply an ACL check if one is defined, before responding. Afterwards,
+ when pipelining is not advertised, do another sync check in case the ACL
+ delayed and the client started sending in the meantime. */
- rc = (acl_smtp_mail == NULL)? OK :
- acl_check(ACL_WHERE_MAIL, NULL, acl_smtp_mail, &user_msg, &log_msg);
+ if (acl_smtp_mail == NULL) rc = OK; else
+ {
+ rc = acl_check(ACL_WHERE_MAIL, NULL, acl_smtp_mail, &user_msg, &log_msg);
+ if (rc == OK && !pipelining_advertised && !check_sync())
+ goto SYNC_FAILURE;
+ }
if (rc == OK || rc == DISCARD)
{
break;
- /* The RCPT command requires an address as an operand. All we do
- here is to parse it for syntactic correctness. There may be any number
- of RCPT commands, specifying multiple senders. We build them all into
- a data structure that is in argc/argv format. The start/end values
- given by parse_extract_address are not used, as we keep only the
- extracted address. */
+ /* The RCPT command requires an address as an operand. There may be any
+ number of RCPT commands, specifying multiple recipients. We build them all
+ into a data structure. The start/end values given by parse_extract_address
+ are not used, as we keep only the extracted address. */
case RCPT_CMD:
HAD(SCH_RCPT);
rcpt_count++;
- was_rcpt = TRUE;
+ was_rcpt = rcpt_in_progress = TRUE;
/* There must be a sender address; if the sender was rejected and
pipelining was advertised, we assume the client was pipelining, and do not
}
/* If the MAIL ACL discarded all the recipients, we bypass ACL checking
- for them. Otherwise, check the access control list for this recipient. */
+ for them. Otherwise, check the access control list for this recipient. As
+ there may be a delay in this, re-check for a synchronization error
+ afterwards, unless pipelining was advertised. */
- rc = recipients_discarded? DISCARD :
- acl_check(ACL_WHERE_RCPT, recipient, acl_smtp_rcpt, &user_msg, &log_msg);
+ if (recipients_discarded) rc = DISCARD; else
+ {
+ rc = acl_check(ACL_WHERE_RCPT, recipient, acl_smtp_rcpt, &user_msg,
+ &log_msg);
+ if (rc == OK && !pipelining_advertised && !check_sync())
+ goto SYNC_FAILURE;
+ }
/* The ACL was happy */
DATA command.
The example in the pipelining RFC 2920 uses 554, but I use 503 here
- because it is the same whether pipelining is in use or not. */
+ because it is the same whether pipelining is in use or not.
+
+ If all the RCPT commands that precede DATA provoked the same error message
+ (often indicating some kind of system error), it is helpful to include it
+ with the DATA rejection (an idea suggested by Tony Finch). */
case DATA_CMD:
HAD(SCH_DATA);
if (!discarded && recipients_count <= 0)
{
+ if (rcpt_smtp_response_same && rcpt_smtp_response != NULL)
+ {
+ uschar *code = US"503";
+ int len = Ustrlen(rcpt_smtp_response);
+ smtp_respond(code, 3, FALSE, US"All RCPT commands were rejected with "
+ "this error:");
+ /* Responses from smtp_printf() will have \r\n on the end */
+ if (len > 2 && rcpt_smtp_response[len-2] == '\r')
+ rcpt_smtp_response[len-2] = 0;
+ smtp_respond(code, 3, FALSE, rcpt_smtp_response);
+ }
if (pipelining_advertised && last_was_rcpt)
- smtp_printf("503 valid RCPT command must precede DATA\r\n");
+ smtp_printf("503 Valid RCPT command must precede DATA\r\n");
else
done = synprot_error(L_smtp_protocol_error, 503, NULL,
US"valid RCPT command must precede DATA");
break;
}
+ /* If there is an ACL, re-check the synchronization afterwards, since the
+ ACL may have delayed. */
+
if (acl_smtp_predata == NULL) rc = OK; else
{
enable_dollar_recipients = TRUE;
rc = acl_check(ACL_WHERE_PREDATA, NULL, acl_smtp_predata, &user_msg,
&log_msg);
enable_dollar_recipients = FALSE;
+ if (rc == OK && !check_sync()) goto SYNC_FAILURE;
}
if (rc == OK)
else
done = smtp_handle_acl_fail(ACL_WHERE_PREDATA, rc, user_msg, log_msg);
-
break;
case EOF_CMD:
log_write(L_smtp_connection, LOG_MAIN, "%s closed by EOF",
smtp_get_connection_info());
+ smtp_notquit_exit(US"tls-failed", NULL, NULL);
done = 2;
break;
+ /* It is perhaps arguable as to which exit ACL should be called here,
+ but as it is probably a situtation that almost never arises, it
+ probably doesn't matter. We choose to call the real QUIT ACL, which in
+ some sense is perhaps "right". */
+
case QUIT_CMD:
- smtp_printf("221 %s closing connection\r\n", smtp_active_hostname);
+ user_msg = NULL;
+ if (acl_smtp_quit != NULL)
+ {
+ rc = acl_check(ACL_WHERE_QUIT, NULL, acl_smtp_quit, &user_msg,
+ &log_msg);
+ if (rc == ERROR)
+ log_write(0, LOG_MAIN|LOG_PANIC, "ACL for QUIT returned ERROR: %s",
+ log_msg);
+ }
+ if (user_msg == NULL)
+ smtp_printf("221 %s closing connection\r\n", smtp_active_hostname);
+ else
+ smtp_respond(US"221", 3, TRUE, user_msg);
log_write(L_smtp_connection, LOG_MAIN, "%s closed by QUIT",
smtp_get_connection_info());
done = 2;
case QUIT_CMD:
HAD(SCH_QUIT);
incomplete_transaction_log(US"QUIT");
-
if (acl_smtp_quit != NULL)
{
- rc = acl_check(ACL_WHERE_QUIT, NULL, acl_smtp_quit,&user_msg,&log_msg);
+ rc = acl_check(ACL_WHERE_QUIT, NULL, acl_smtp_quit, &user_msg, &log_msg);
if (rc == ERROR)
log_write(0, LOG_MAIN|LOG_PANIC, "ACL for QUIT returned ERROR: %s",
log_msg);
}
-
if (user_msg == NULL)
smtp_printf("221 %s closing connection\r\n", smtp_active_hostname);
else
break;
- /* Show ETRN/EXPN/VRFY if there's
- an ACL for checking hosts; if actually used, a check will be done for
- permitted hosts. */
+ /* Show ETRN/EXPN/VRFY if there's an ACL for checking hosts; if actually
+ used, a check will be done for permitted hosts. Show STARTTLS only if not
+ already in a TLS session and if it would be advertised in the EHLO
+ response. */
case HELP_CMD:
HAD(SCH_HELP);
buffer[0] = 0;
Ustrcat(buffer, " AUTH");
#ifdef SUPPORT_TLS
- Ustrcat(buffer, " STARTTLS");
+ if (tls_active < 0 &&
+ verify_check_host(&tls_advertise_hosts) != FAIL)
+ Ustrcat(buffer, " STARTTLS");
#endif
Ustrcat(buffer, " HELO EHLO MAIL RCPT DATA");
Ustrcat(buffer, " NOOP QUIT RSET HELP");
case EOF_CMD:
incomplete_transaction_log(US"connection lost");
- smtp_printf("421 %s lost input connection\r\n", smtp_active_hostname);
+ smtp_notquit_exit(US"connection-lost", US"421",
+ US"%s lost input connection", smtp_active_hostname);
/* Don't log by default unless in the middle of a message, as some mailers
just drop the call rather than sending QUIT, and it clutters up the logs.
case BADSYN_CMD:
+ SYNC_FAILURE:
if (smtp_inend >= smtp_inbuffer + in_buffer_size)
smtp_inend = smtp_inbuffer + in_buffer_size - 1;
c = smtp_inend - smtp_inptr;
pipelining_advertised? "" : " not",
smtp_cmd_buffer, host_and_ident(TRUE),
string_printing(smtp_inptr));
- smtp_printf("554 SMTP synchronization error\r\n");
+ smtp_notquit_exit(US"synchronization-error", US"554",
+ US"SMTP synchronization error");
done = 1; /* Pretend eof - drops connection */
break;
log_write(0, LOG_MAIN|LOG_REJECT, "SMTP call from %s dropped: too many "
"nonmail commands (last was \"%.*s\")", host_and_ident(FALSE),
s - smtp_cmd_buffer, smtp_cmd_buffer);
- smtp_printf("554 Too many nonmail commands\r\n");
+ smtp_notquit_exit(US"bad-commands", US"554", US"Too many nonmail commands");
done = 1; /* Pretend eof - drops connection */
break;
string_printing(smtp_cmd_buffer), host_and_ident(TRUE),
US"unrecognized command");
incomplete_transaction_log(US"unrecognized command");
- smtp_printf("500 Too many unrecognized commands\r\n");
+ smtp_notquit_exit(US"bad-commands", US"500",
+ US"Too many unrecognized commands");
done = 2;
log_write(0, LOG_MAIN|LOG_REJECT, "SMTP call from %s dropped: too many "
"unrecognized commands (last was \"%s\")", host_and_ident(FALSE),