Add the NOTQUIT ACL.
authorPhilip Hazel <ph10@hermes.cam.ac.uk>
Wed, 22 Aug 2007 10:10:23 +0000 (10:10 +0000)
committerPhilip Hazel <ph10@hermes.cam.ac.uk>
Wed, 22 Aug 2007 10:10:23 +0000 (10:10 +0000)
19 files changed:
doc/doc-txt/ChangeLog
doc/doc-txt/NewStuff
src/ACKNOWLEDGMENTS
src/src/acl.c
src/src/expand.c
src/src/functions.h
src/src/globals.c
src/src/globals.h
src/src/log.c
src/src/macros.h
src/src/readconf.c
src/src/receive.c
src/src/smtp_in.c
test/confs/0562 [new file with mode: 0644]
test/log/0562 [new file with mode: 0644]
test/rejectlog/0562 [new file with mode: 0644]
test/scripts/0000-Basic/0562 [new file with mode: 0644]
test/stderr/0562 [new file with mode: 0644]
test/stdout/0562 [new file with mode: 0644]

index efe8f66..94d00cc 100644 (file)
@@ -1,4 +1,4 @@
-$Cambridge: exim/doc/doc-txt/ChangeLog,v 1.522 2007/08/17 11:16:45 ph10 Exp $
+$Cambridge: exim/doc/doc-txt/ChangeLog,v 1.523 2007/08/22 10:10:23 ph10 Exp $
 
 Change log file for Exim from version 4.21
 -------------------------------------------
@@ -81,6 +81,8 @@ PH/15 Removed an incorrect (int) cast when reading the value of SIZE in a
 
 PH/16 Another patch from the Sieve maintainer.
 
+PH/17 Added the NOTQUIT ACL, based on a patch from Ted Cooper.
+
 
 
 Exim version 4.67
index 0dc3660..e3fddf3 100644 (file)
@@ -1,4 +1,4 @@
-$Cambridge: exim/doc/doc-txt/NewStuff,v 1.154 2007/06/27 11:01:51 ph10 Exp $
+$Cambridge: exim/doc/doc-txt/NewStuff,v 1.155 2007/08/22 10:10:23 ph10 Exp $
 
 New Features in Exim
 --------------------
@@ -87,6 +87,38 @@ Version 4.68
     the load for each incoming message in an SMTP session. Otherwise, once one
     message is queued, the remainder are also.
 
+ 9. There is a new ACL, specified by smtp_notquit_acl, which is run in most
+    cases when an SMTP session ends without sending QUIT. However, when Exim
+    itself is is bad trouble, such as being unable to write to its log files,
+    this ACL is not run, because it might try to do things (such as write to
+    log files) that make the situation even worse.
+
+    Like the QUIT ACL, this new ACL is provided to make it possible to gather
+    statistics. Whatever it returns (accept or deny) is immaterial. The "delay"
+    modifier is forbidden in this ACL.
+
+    When the NOTQUIT ACL is running, the variable $smtp_notquit_reason is set
+    to a string that indicates the reason for the termination of the SMTP
+    connection. The possible values are:
+
+      acl-drop                 Another ACL issued a "drop" command
+      bad-commands             Too many unknown or non-mail commands
+      command-timeout          Timeout while reading SMTP commands
+      connection-lost          The SMTP connection has been lost
+      data-timeout             Timeout while reading message data
+      local-scan-error         The local_scan() function crashed
+      local-scan-timeout       The local_scan() function timed out
+      signal-exit              SIGTERM or SIGINT
+      synchronization-error    SMTP synchronization error
+      tls-failed               TLS failed to start
+
+    In most cases when an SMTP connection is closed without having received
+    QUIT, Exim sends an SMTP response message before actually closing the
+    connection. With the exception of acl-drop, the default message can be
+    overridden by the "message" modifier in the NOTQUIT ACL. In the case of a
+    "drop" verb in another ACL, it is the message from the other ACL that is
+    used.
+
 
 Version 4.67
 ------------
index 9903b2c..62af09e 100644 (file)
@@ -1,4 +1,4 @@
-$Cambridge: exim/src/ACKNOWLEDGMENTS,v 1.78 2007/06/20 14:13:39 ph10 Exp $
+$Cambridge: exim/src/ACKNOWLEDGMENTS,v 1.79 2007/08/22 10:10:23 ph10 Exp $
 
 EXIM ACKNOWLEDGEMENTS
 
@@ -20,7 +20,7 @@ relatively small patches.
 Philip Hazel
 
 Lists created: 20 November 2002
-Last updated:  20 June 2007
+Last updated:  22 August 2007
 
 
 THE OLD LIST
@@ -93,6 +93,7 @@ Pete Carah                Patch for change to radiusclient API
 Oliver Cook               Suggested patch for exigrep & rejected messages
                             Patch to add sender/host info to local_scan() rejects
                             Suggested patch to add queue time to "Completed"
+Ted Cooper                Suggested patch for NOTQUIT ACL
 Jennifer Corley           Designing the new Exim logo
 John Dalbec               Patch for quota_warn_threshold bug
 Vivek Dasmohapatra        Suggested patch for CRL support
index 3b00be5..9c61344 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/acl.c,v 1.77 2007/06/20 14:13:39 ph10 Exp $ */
+/* $Cambridge: exim/src/src/acl.c,v 1.78 2007/08/22 10:10:23 ph10 Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
@@ -350,8 +350,9 @@ static uschar cond_modifiers[] = {
 };
 
 /* Bit map vector of which conditions and modifiers are not allowed at certain
-times. For each condition, there's a bitmap of dis-allowed times. For some, it
-is easier to specify the negation of a small number of allowed times. */
+times. For each condition and modifier, there's a bitmap of dis-allowed times.
+For some, it is easier to specify the negation of a small number of allowed
+times. */
 
 static unsigned int cond_forbids[] = {
   0,                                               /* acl */
@@ -391,7 +392,7 @@ static unsigned int cond_forbids[] = {
   ~(1<<ACL_WHERE_MIME),                            /* decode */
   #endif
 
-  0,                                               /* delay */
+  (1<<ACL_WHERE_NOTQUIT),                          /* delay */
 
   #ifdef WITH_OLD_DEMIME
   (unsigned int)
index d86ea46..be066a5 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/expand.c,v 1.88 2007/06/27 11:01:51 ph10 Exp $ */
+/* $Cambridge: exim/src/src/expand.c,v 1.89 2007/08/22 10:10:23 ph10 Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
@@ -549,6 +549,7 @@ static var_entry var_table[] = {
   { "smtp_command",        vtype_stringptr,   &smtp_cmd_buffer },
   { "smtp_command_argument", vtype_stringptr, &smtp_cmd_argument },
   { "smtp_count_at_connection_start", vtype_int, &smtp_accept_count },
+  { "smtp_notquit_reason", vtype_stringptr,   &smtp_notquit_reason },
   { "sn0",                 vtype_filter_int,  &filter_sn[0] },
   { "sn1",                 vtype_filter_int,  &filter_sn[1] },
   { "sn2",                 vtype_filter_int,  &filter_sn[2] },
index d6262d1..40281bd 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/functions.h,v 1.37 2007/04/13 15:13:47 ph10 Exp $ */
+/* $Cambridge: exim/src/src/functions.h,v 1.38 2007/08/22 10:10:23 ph10 Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
@@ -210,7 +210,7 @@ extern int     readconf_readtime(uschar *, int, BOOL);
 extern void    readconf_rest(BOOL);
 extern uschar *readconf_retry_error(uschar *, uschar *, int *, int *);
 extern void    read_message_body(BOOL);
-extern void    receive_bomb_out(uschar *);
+extern void    receive_bomb_out(uschar *, uschar *);
 extern BOOL    receive_check_fs(int);
 extern BOOL    receive_check_set_sender(uschar *);
 extern BOOL    receive_msg(BOOL);
@@ -277,6 +277,7 @@ extern void    smtp_log_no_mail(void);
 extern void    smtp_message_code(uschar **, int *, uschar **, uschar **);
 extern BOOL    smtp_read_response(smtp_inblock *, uschar *, int, int, int);
 extern void    smtp_respond(uschar *, int, BOOL, uschar *);
+extern void    smtp_notquit_exit(uschar *, uschar *, uschar *, ...);
 extern void    smtp_send_prohibition_message(int, uschar *);
 extern int     smtp_setup_msg(void);
 extern BOOL    smtp_start_session(void);
index 4a75aed..55e9cbd 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/globals.c,v 1.76 2007/06/27 11:01:52 ph10 Exp $ */
+/* $Cambridge: exim/src/src/globals.c,v 1.77 2007/08/22 10:10:23 ph10 Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
@@ -172,11 +172,13 @@ int address_expansions_count = sizeof(address_expansions)/sizeof(uschar **);
 
 header_line *acl_added_headers = NULL;
 tree_node *acl_anchor          = NULL;
+
 uschar *acl_not_smtp           = NULL;
 #ifdef WITH_CONTENT_SCAN
 uschar *acl_not_smtp_mime      = NULL;
 #endif
 uschar *acl_not_smtp_start     = NULL;
+
 uschar *acl_smtp_auth          = NULL;
 uschar *acl_smtp_connect       = NULL;
 uschar *acl_smtp_data          = NULL;
@@ -188,11 +190,13 @@ uschar *acl_smtp_mailauth      = NULL;
 #ifdef WITH_CONTENT_SCAN
 uschar *acl_smtp_mime          = NULL;
 #endif
+uschar *acl_smtp_notquit       = NULL;
 uschar *acl_smtp_predata       = NULL;
 uschar *acl_smtp_quit          = NULL;
 uschar *acl_smtp_rcpt          = NULL;
 uschar *acl_smtp_starttls      = NULL;
 uschar *acl_smtp_vrfy          = NULL;
+
 BOOL    acl_temp_details       = FALSE;
 tree_node *acl_var_c           = NULL;
 tree_node *acl_var_m           = NULL;
@@ -215,6 +219,7 @@ uschar *acl_wherenames[]       = { US"RCPT",
                                    US"EHLO or HELO",
                                    US"MAILAUTH",
                                    US"non-SMTP-start",
+                                   US"NOTQUIT",
                                    US"QUIT",
                                    US"STARTTLS",
                                    US"VRFY"
@@ -233,6 +238,7 @@ uschar *acl_wherecodes[]       = { US"550",     /* RCPT */
                                    US"550",     /* HELO/EHLO */
                                    US"0",       /* MAILAUTH; not relevant */
                                    US"0",       /* not SMTP; not relevant */
+                                   US"0",       /* NOTQUIT; not relevant */
                                    US"0",       /* QUIT; not relevant */
                                    US"550",     /* STARTTLS */
                                    US"252"      /* VRFY */
@@ -1087,6 +1093,7 @@ uschar *smtp_etrn_command      = NULL;
 BOOL    smtp_etrn_serialize    = TRUE;
 int     smtp_max_synprot_errors= 3;
 int     smtp_max_unknown_commands = 3;
+uschar *smtp_notquit_reason    = NULL;
 uschar *smtp_ratelimit_hosts   = NULL;
 uschar *smtp_ratelimit_mail    = NULL;
 uschar *smtp_ratelimit_rcpt    = NULL;
index fd20c94..473b311 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/globals.h,v 1.57 2007/07/27 13:56:24 magnus Exp $ */
+/* $Cambridge: exim/src/src/globals.h,v 1.58 2007/08/22 10:10:23 ph10 Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
@@ -126,6 +126,7 @@ extern uschar *acl_smtp_mailauth;      /* ACL run for MAIL AUTH */
 #ifdef WITH_CONTENT_SCAN
 extern uschar *acl_smtp_mime;          /* ACL run after DATA, before acl_smtp_data, for each MIME part */
 #endif
+extern uschar *acl_smtp_notquit;       /* ACL run for disconnects */
 extern uschar *acl_smtp_predata;       /* ACL run for DATA command */
 extern uschar *acl_smtp_quit;          /* ACL run for QUIT */
 extern uschar *acl_smtp_rcpt;          /* ACL run for RCPT */
@@ -660,6 +661,7 @@ extern int     smtp_load_reserve;      /* Only from reserved if load > this */
 extern int     smtp_mailcmd_count;     /* Count of MAIL commands */
 extern int     smtp_max_synprot_errors;/* Max syntax/protocol errors */
 extern int     smtp_max_unknown_commands; /* As it says */
+extern uschar *smtp_notquit_reason;    /* Global for disconnect reason */
 extern FILE   *smtp_out;               /* Incoming SMTP output file */
 extern uschar *smtp_ratelimit_hosts;   /* Rate limit these hosts */
 extern uschar *smtp_ratelimit_mail;    /* Parameters for MAIL limiting */
index 5cc3092..ac351c4 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/log.c,v 1.12 2007/01/31 16:52:12 ph10 Exp $ */
+/* $Cambridge: exim/src/src/log.c,v 1.13 2007/08/22 10:10:23 ph10 Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
@@ -135,9 +135,11 @@ for (pass = 0; pass < 2; pass++)
 /* This is called when Exim is dying as a result of something going wrong in
 the logging, or after a log call with LOG_PANIC_DIE set. Optionally write a
 message to debug_file or a stderr file, if they exist. Then, if in the middle
-of accepting a message, throw it away tidily; this will attempt to send an SMTP
-response if appropriate. Otherwise, try to close down an outstanding SMTP call
-tidily.
+of accepting a message, throw it away tidily by calling receive_bomb_out();
+this will attempt to send an SMTP response if appropriate. Passing NULL as the
+first argument stops it trying to run the NOTQUIT ACL (which might try further
+logging and thus cause problems). Otherwise, try to close down an outstanding
+SMTP call tidily.
 
 Arguments:
   s1         Error message to write to debug_file and/or stderr and syslog
@@ -155,7 +157,7 @@ if (s1 != NULL)
   if (log_stderr != NULL && log_stderr != debug_file)
     fprintf(log_stderr, "%s\n", s1);
   }
-if (receive_call_bombout) receive_bomb_out(s2);  /* does not return */
+if (receive_call_bombout) receive_bomb_out(NULL, s2);  /* does not return */
 if (smtp_input) smtp_closedown(s2);
 exim_exit(EXIT_FAILURE);
 }
index f33ac74..161a6a1 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/macros.h,v 1.35 2007/06/27 11:01:52 ph10 Exp $ */
+/* $Cambridge: exim/src/src/macros.h,v 1.36 2007/08/22 10:10:23 ph10 Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
@@ -809,6 +809,7 @@ enum { ACL_WHERE_RCPT,       /* Some controls are for RCPT only */
        ACL_WHERE_HELO,
        ACL_WHERE_MAILAUTH,
        ACL_WHERE_NOTSMTP_START,
+       ACL_WHERE_NOTQUIT,
        ACL_WHERE_QUIT,
        ACL_WHERE_STARTTLS,
        ACL_WHERE_VRFY
index 6021d74..2ac260d 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/readconf.c,v 1.31 2007/07/04 11:03:46 ph10 Exp $ */
+/* $Cambridge: exim/src/src/readconf.c,v 1.32 2007/08/22 10:10:23 ph10 Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
@@ -150,6 +150,7 @@ static optionlist optionlist_config[] = {
 #ifdef WITH_CONTENT_SCAN
   { "acl_smtp_mime",            opt_stringptr,   &acl_smtp_mime },
 #endif
+  { "acl_smtp_notquit",         opt_stringptr,   &acl_smtp_notquit },
   { "acl_smtp_predata",         opt_stringptr,   &acl_smtp_predata },
   { "acl_smtp_quit",            opt_stringptr,   &acl_smtp_quit },
   { "acl_smtp_rcpt",            opt_stringptr,   &acl_smtp_rcpt },
index 6d07e5c..e97314a 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/receive.c,v 1.39 2007/06/29 09:20:37 ph10 Exp $ */
+/* $Cambridge: exim/src/src/receive.c,v 1.40 2007/08/22 10:10:23 ph10 Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
@@ -178,7 +178,7 @@ else
     }
   }
 
-/* We now have the patch; do the business */
+/* We now have the path; do the business */
 
 memset(&statbuf, 0, sizeof(statbuf));
 
@@ -283,12 +283,14 @@ that case is done by setting a flag to cause the log functions to call this
 function if there is an ultimate disaster. That is why it is globally
 accessible.
 
-Arguments:   SMTP response to give if in an SMTP session
+Arguments:
+  reason     text reason to pass to the not-quit ACL
+  msg        default SMTP response to give if in an SMTP session
 Returns:     it doesn't
 */
 
 void
-receive_bomb_out(uschar *msg)
+receive_bomb_out(uschar *reason, uschar *msg)
 {
 /* If spool_name is set, it contains the name of the data file that is being
 written. Unlink it before closing so that it cannot be picked up by a delivery
@@ -306,20 +308,16 @@ if (spool_name[0] != 0)
 if (data_file != NULL) (void)fclose(data_file);
   else if (data_fd >= 0) (void)close(data_fd);
 
-/* Attempt to close down an SMTP connection tidily. */
+/* Attempt to close down an SMTP connection tidily. For non-batched SMTP, call
+smtp_notquit_exit(), which runs the NOTQUIT ACL, if present, and handles the
+SMTP response. */
 
 if (smtp_input)
   {
-  if (!smtp_batched_input)
-    {
-    smtp_printf("421 %s %s - closing connection.\r\n", smtp_active_hostname,
-      msg);
-    mac_smtp_fflush();
-    }
-
-  /* Control does not return from moan_smtp_batch(). */
-
-  else moan_smtp_batch(NULL, "421 %s - message abandoned", msg);
+  if (smtp_batched_input)
+    moan_smtp_batch(NULL, "421 %s - message abandoned", msg);  /* No return */
+  smtp_notquit_exit(reason, US"421", US"%s %s - closing connection.",
+    smtp_active_hostname, msg);
   }
 
 /* Exit from the program (non-BSMTP cases) */
@@ -362,7 +360,7 @@ else
             LOG_MAIN, "timed out while reading local message");
   }
 
-receive_bomb_out(msg);   /* Does not return */
+receive_bomb_out(US"data-timeout", msg);   /* Does not return */
 }
 
 
@@ -384,7 +382,8 @@ local_scan_timeout_handler(int sig)
 sig = sig;    /* Keep picky compilers happy */
 log_write(0, LOG_MAIN|LOG_REJECT, "local_scan() function timed out - "
   "message temporarily rejected (size %d)", message_size);
-receive_bomb_out(US"local verification problem");   /* Does not return */
+/* Does not return */
+receive_bomb_out(US"local-scan-timeout", US"local verification problem");
 }
 
 
@@ -405,7 +404,8 @@ local_scan_crash_handler(int sig)
 {
 log_write(0, LOG_MAIN|LOG_REJECT, "local_scan() function crashed with "
   "signal %d - message temporarily rejected (size %d)", sig, message_size);
-receive_bomb_out(US"local verification problem");   /* Does not return */
+/* Does not return */
+receive_bomb_out(US"local-scan-error", US"local verification problem");
 }
 
 
@@ -442,7 +442,7 @@ else
     }
   }
 
-receive_bomb_out(msg);    /* Does not return */
+receive_bomb_out(US"signal-exit", msg);    /* Does not return */
 }
 
 
index ca198af..d336f0e 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/smtp_in.c,v 1.59 2007/07/04 10:37:03 ph10 Exp $ */
+/* $Cambridge: exim/src/src/smtp_in.c,v 1.60 2007/08/22 10:10:23 ph10 Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
@@ -123,6 +123,7 @@ 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;
@@ -470,9 +471,8 @@ log_write(L_lost_incoming_connection,
           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);
 }
 
@@ -495,8 +495,8 @@ sig = sig;    /* Keep picky compilers happy */
 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);
 }
 
@@ -702,7 +702,9 @@ phase, sends the reply string, and gives an error to all subsequent commands
 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
 */
 
@@ -1344,6 +1346,7 @@ auth_advertised = FALSE;
 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));
 
@@ -2266,6 +2269,12 @@ if (!drop) return 0;
 
 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;
 }
 
@@ -2273,6 +2282,86 @@ 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               *
 *************************************************/
 
@@ -3786,11 +3875,29 @@ while (done <= 0)
         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;
@@ -3813,15 +3920,13 @@ while (done <= 0)
     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
@@ -3882,7 +3987,8 @@ while (done <= 0)
 
     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.
@@ -4088,7 +4194,8 @@ while (done <= 0)
       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;
 
@@ -4100,7 +4207,7 @@ while (done <= 0)
     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;
 
@@ -4113,7 +4220,8 @@ while (done <= 0)
         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),
diff --git a/test/confs/0562 b/test/confs/0562
new file mode 100644 (file)
index 0000000..51343f6
--- /dev/null
@@ -0,0 +1,28 @@
+# Exim test configuration 0562
+
+exim_path = EXIM_PATH
+host_lookup_order = bydns
+primary_hostname = myhost.test.ex
+rfc1413_query_timeout = 0s
+spool_directory = DIR/spool
+log_file_path = DIR/spool/log/%slog
+gecos_pattern = ""
+gecos_name = CALLER_NAME
+
+# ----- Main settings -----
+
+acl_smtp_rcpt = accept
+acl_smtp_data = drop
+acl_smtp_notquit = notquit
+
+smtp_accept_max_nonmail = 4
+smtp_receive_timeout = 1s
+
+# ----- ACLs -----
+
+begin acl
+
+notquit:
+  accept logwrite = NOTQUIT reason: '$smtp_notquit_reason'
+
+# End
diff --git a/test/log/0562 b/test/log/0562
new file mode 100644 (file)
index 0000000..43b67db
--- /dev/null
@@ -0,0 +1,16 @@
+1999-03-02 09:44:33 exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTP on port 1225
+1999-03-02 09:44:33 SMTP data timeout (message abandoned) on connection from (abcd) [127.0.0.1] F=<userx@test.ex>
+1999-03-02 09:44:33 NOTQUIT reason: 'data-timeout'
+1999-03-02 09:44:33 SMTP command timeout on connection from [127.0.0.1]
+1999-03-02 09:44:33 NOTQUIT reason: 'command-timeout'
+1999-03-02 09:44:33 NOTQUIT reason: 'connection-lost'
+1999-03-02 09:44:33 NOTQUIT reason: 'bad-commands'
+1999-03-02 09:44:33 SMTP call from [127.0.0.1] dropped: too many unrecognized commands (last was "unknown")
+1999-03-02 09:44:33 H=[127.0.0.1] rejected VRFY 
+1999-03-02 09:44:33 H=[127.0.0.1] rejected VRFY 
+1999-03-02 09:44:33 H=[127.0.0.1] rejected VRFY 
+1999-03-02 09:44:33 H=[127.0.0.1] rejected VRFY 
+1999-03-02 09:44:33 SMTP call from [127.0.0.1] dropped: too many nonmail commands (last was "vrfy")
+1999-03-02 09:44:33 NOTQUIT reason: 'bad-commands'
+1999-03-02 09:44:33 10HmaX-0005vi-00 H=(abcd) [127.0.0.1] F=<userx@test.ex> rejected after DATA
+1999-03-02 09:44:33 10HmaX-0005vi-00 NOTQUIT reason: 'acl-drop'
diff --git a/test/rejectlog/0562 b/test/rejectlog/0562
new file mode 100644 (file)
index 0000000..d10c450
--- /dev/null
@@ -0,0 +1,14 @@
+1999-03-02 09:44:33 SMTP call from [127.0.0.1] dropped: too many unrecognized commands (last was "unknown")
+1999-03-02 09:44:33 H=[127.0.0.1] rejected VRFY 
+1999-03-02 09:44:33 H=[127.0.0.1] rejected VRFY 
+1999-03-02 09:44:33 H=[127.0.0.1] rejected VRFY 
+1999-03-02 09:44:33 H=[127.0.0.1] rejected VRFY 
+1999-03-02 09:44:33 SMTP call from [127.0.0.1] dropped: too many nonmail commands (last was "vrfy")
+1999-03-02 09:44:33 10HmaX-0005vi-00 H=(abcd) [127.0.0.1] F=<userx@test.ex> rejected after DATA
+Envelope-from: <userx@test.ex>
+Envelope-to: <userx@test.ex>
+P Received: from [127.0.0.1] (helo=abcd)
+       by myhost.test.ex with esmtp (Exim x.yz)
+       (envelope-from <userx@test.ex>)
+       id 10HmaX-0005vi-00
+       for userx@test.ex; Tue, 2 Mar 1999 09:44:33 +0000
diff --git a/test/scripts/0000-Basic/0562 b/test/scripts/0000-Basic/0562
new file mode 100644 (file)
index 0000000..c6a72ab
--- /dev/null
@@ -0,0 +1,66 @@
+# NOTQUIT ACL
+need_ipv4
+#
+exim -DSERVER=server -bd -oX PORT_D
+****
+client 127.0.0.1 PORT_D
+??? 220
+ehlo abcd
+??? 250-
+??? 250-
+??? 250-
+??? 250
+mail from:<userx@test.ex>\r\nrcpt to:<userx@test.ex>\r\ndata
+??? 250
+??? 250
+??? 354
+This is a test message.
++++ 2
+****
+client 127.0.0.1 PORT_D
+??? 220
++++ 2
+****
+client 127.0.0.1 PORT_D
+??? 220
+****
+client 127.0.0.1 PORT_D
+??? 220
+unknown
+??? 500
+unknown
+??? 500
+unknown
+??? 500
+unknown
+??? 500
+****
+client 127.0.0.1 PORT_D
+??? 220
+vrfy
+??? 252
+vrfy
+??? 252
+vrfy
+??? 252
+vrfy
+??? 252
+vrfy
+??? 554
+****
+client 127.0.0.1 PORT_D
+??? 220
+ehlo abcd
+??? 250-
+??? 250-
+??? 250-
+??? 250
+mail from:<userx@test.ex>\r\nrcpt to:<userx@test.ex>\r\ndata
+??? 250
+??? 250
+??? 354
+This is a test message.
+.
+****
+sleep 1
+killdaemon
diff --git a/test/stderr/0562 b/test/stderr/0562
new file mode 100644 (file)
index 0000000..045fadc
--- /dev/null
@@ -0,0 +1,2 @@
+
+******** SERVER ********
diff --git a/test/stdout/0562 b/test/stdout/0562
new file mode 100644 (file)
index 0000000..a35f835
--- /dev/null
@@ -0,0 +1,88 @@
+Connecting to 127.0.0.1 port 1225 ... connected
+??? 220
+<<< 220 myhost.test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000
+>>> ehlo abcd
+??? 250-
+<<< 250-myhost.test.ex Hello abcd [127.0.0.1]
+??? 250-
+<<< 250-SIZE 52428800
+??? 250-
+<<< 250-PIPELINING
+??? 250
+<<< 250 HELP
+>>> mail from:<userx@test.ex>\r\nrcpt to:<userx@test.ex>\r\ndata
+??? 250
+<<< 250 OK
+??? 250
+<<< 250 Accepted
+??? 354
+<<< 354 Enter message, ending with "." on a line by itself
+>>> This is a test message.
++++ 2
+End of script
+Connecting to 127.0.0.1 port 1225 ... connected
+??? 220
+<<< 220 myhost.test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000
++++ 2
+End of script
+Connecting to 127.0.0.1 port 1225 ... connected
+??? 220
+<<< 220 myhost.test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000
+End of script
+Connecting to 127.0.0.1 port 1225 ... connected
+??? 220
+<<< 220 myhost.test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000
+>>> unknown
+??? 500
+<<< 500 unrecognized command
+>>> unknown
+??? 500
+<<< 500 unrecognized command
+>>> unknown
+??? 500
+<<< 500 unrecognized command
+>>> unknown
+??? 500
+<<< 500 Too many unrecognized commands
+End of script
+Connecting to 127.0.0.1 port 1225 ... connected
+??? 220
+<<< 220 myhost.test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000
+>>> vrfy
+??? 252
+<<< 252 Administrative prohibition
+>>> vrfy
+??? 252
+<<< 252 Administrative prohibition
+>>> vrfy
+??? 252
+<<< 252 Administrative prohibition
+>>> vrfy
+??? 252
+<<< 252 Administrative prohibition
+>>> vrfy
+??? 554
+<<< 554 Too many nonmail commands
+End of script
+Connecting to 127.0.0.1 port 1225 ... connected
+??? 220
+<<< 220 myhost.test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000
+>>> ehlo abcd
+??? 250-
+<<< 250-myhost.test.ex Hello abcd [127.0.0.1]
+??? 250-
+<<< 250-SIZE 52428800
+??? 250-
+<<< 250-PIPELINING
+??? 250
+<<< 250 HELP
+>>> mail from:<userx@test.ex>\r\nrcpt to:<userx@test.ex>\r\ndata
+??? 250
+<<< 250 OK
+??? 250
+<<< 250 Accepted
+??? 354
+<<< 354 Enter message, ending with "." on a line by itself
+>>> This is a test message.
+>>> .
+End of script