Patch imported from bugzilla 1031. Axel Rau.
authorJeremy Harris <jgh146exb@wizmail.org>
Sun, 30 Jun 2013 15:17:55 +0000 (16:17 +0100)
committerJeremy Harris <jgh146exb@wizmail.org>
Sun, 30 Jun 2013 15:17:55 +0000 (16:17 +0100)
doc/doc-txt/experimental-spec.txt
src/src/EDITME
src/src/config.h.defaults
src/src/deliver.c
src/src/exim.c
src/src/expand.c
src/src/globals.c
src/src/globals.h
src/src/readconf.c
src/src/transports/smtp.c
src/src/transports/smtp.h

index 5dd6832b1f6382cc71aa32f4f0736e99d5a161e8..047440611d0047c1436009393cc77b46f28f9533 100644 (file)
@@ -574,6 +574,85 @@ to tell the DCC routines add more information; eg, you might set
 this to some results from ClamAV.  Be careful.  Header syntax is
 not checked and is added "as is".
 
+DBL (Database Logging)
+--------------------------------------------------------------
+
+This feature allows to write exim internal log information
+(not available otherwise) into a database. 
+Initially implemented is logging of details about successfully
+completed remote deliveries, which are needed for reputation
+systems, and deferrals caused by a host error.
+
+In order to use DBL, you must set
+
+EXPERIMENTAL_DBL=yes
+
+in your Local/Makefile
+
+and define the database queries in the runtime config file, to
+be executed at end of delivery.
+
+Additionally, there are 8 more variables, available at end of
+delivery:
+
+dbl_delivery_ip             IP of host, which has accepted delivery
+dbl_delivery_port           Port of remote host which has accepted delivery
+dbl_delivery_fqdn           FQDN of host, which has accepted delivery
+dbl_delivery_local_part     local part of address being delivered
+dbl_delivery_domain         domain part of address being delivered
+dbl_delivery_confirmation   SMTP confirmation message
+
+In case of a deferral caused by a host-error:
+dbl_defer_errno             Error number
+dbl_defer_errstr            Error string possibly containing more details
+
+
+To log successful deliveries, set the following option in the main
+option part of runtime config.
+
+dbl_delivery_query
+
+An example might look like:
+
+dbl_delivery_query = \
+${lookup pgsql {SELECT * FROM record_Delivery( \
+    '${quote_pgsql:$sender_address_domain}',\
+    '${quote_pgsql:${lc:$sender_address_local_part}}', \
+    '${quote_pgsql:$dbl_delivery_domain}', \
+    '${quote_pgsql:${lc:$dbl_delivery_local_part}}', \
+    '${quote_pgsql:$dbl_delivery_ip}', \
+    '${quote_pgsql:${lc:$dbl_delivery_fqdn}}', \
+    '${quote_pgsql:$message_exim_id}')}}
+
+
+In order to log host deferrals, add the following option to an SMTP
+transport:
+
+dbl_host_defer_query
+
+This is a private option of the SMTP transport. It is intended to
+log failures of remote hosts. It is executed only when exim has
+attempted to deliver a message to a remote host and failed due to
+an error which doesn't seem to be related to the individual
+message, sender, or recipient address.
+See section 45.2 of the exim documentation for more details on how
+this is determined.
+
+Example:
+
+dbl_host_defer_query = \
+${lookup mysql {insert into delivlog set \
+    msgid = '${quote_mysql:$message_exim_id}', \
+    senderlp = '${quote_mysql:${lc:$sender_address_local_part}}', \
+    senderdom = '${quote_mysql:$sender_address_domain}', \
+    delivlp = '${quote_mysql:${lc:$dbl_delivery_local_part}}', \
+    delivdom = '${quote_mysql:$dbl_delivery_domain}', \
+    delivip = '${quote_mysql:$dbl_delivery_ip}', \
+    delivport = '${quote_mysql:$dbl_delivery_port}', \
+    delivfqdn = '${quote_mysql:$dbl_delivery_fqdn}', \
+    deliverrno = '${quote_mysql:$dbl_defer_errno}', \
+    deliverrstr = '${quote_mysql:$dbl_defer_errstr}' \
+    }}
 
 --------------------------------------------------------------
 End of file
index 95857c707ea163b9667d0e6cb0c03f4c1825fde2..1387b980aa2ea278538da5a112f7312af3fa9030 100644 (file)
@@ -452,7 +452,9 @@ EXIM_MONITOR=eximon.bin
 
 # EXPERIMENTAL_OCSP=yes
 
-
+# This feature allows to write log information about a completed
+#  delivery into a database
+# EXPERIMENTAL_DBL=yes
 
 ###############################################################################
 #                 THESE ARE THINGS YOU MIGHT WANT TO SPECIFY                  #
index 92a4cd348373f9deb014704bb882f01eaa459ead..1652c784254178a8ae7f46a9a730412f542563b1 100644 (file)
@@ -166,6 +166,7 @@ it's a default value. */
 #define EXPERIMENTAL_OCSP
 #define EXPERIMENTAL_SPF
 #define EXPERIMENTAL_SRS
+#define EXPERIMENTAL_DBL
 
 /* For developers */
 #define WANT_DEEPER_PRINTF_CHECKS
index d4ea2d868c2d45916a1048985c982f432b9107d6..1c3c4bafd392b002789fa57d625e0d2c8c4845e5 100644 (file)
@@ -885,6 +885,9 @@ if (result == OK)
   if(addr->p.srs_sender)
     s = string_append(s, &size, &ptr, 3, US" SRS=<", addr->p.srs_sender, US">");
   #endif
+  #ifdef EXPERIMENTAL_DBL
+  dbl_delivery_ip = NULL;      /* presume no successful remote delivery */
+  #endif
 
   /* You might think that the return path must always be set for a successful
   delivery; indeed, I did for some time, until this statement crashed. The case
@@ -930,8 +933,15 @@ if (result == OK)
           addr->host_used->port));
       if (continue_sequence > 1)
         s = string_cat(s, &size, &ptr, US"*", 1);
+      #ifdef EXPERIMENTAL_DBL
+      dbl_delivery_ip = string_copy(addr->host_used->address);
+      dbl_delivery_port = addr->host_used->port;
+      dbl_delivery_fqdn = string_copy(addr->host_used->name);
+      dbl_delivery_local_part = string_copy(addr->local_part);
+      dbl_delivery_domain = string_copy(addr->domain);
+      dbl_delivery_confirmation = string_copy(addr->message);
+      #endif
       }
-
     #ifdef SUPPORT_TLS
     if ((log_extra_selector & LX_tls_cipher) != 0 && addr->cipher != NULL)
       s = string_append(s, &size, &ptr, 2, US" X=", addr->cipher);
@@ -981,6 +991,14 @@ if (result == OK)
 
   s[ptr] = 0;
   log_write(0, LOG_MAIN, "%s", s);
+  #ifdef EXPERIMENTAL_DBL
+  DEBUG(D_deliver)
+    {
+    debug_printf("  DBL(Delivery): dbl_delivery_query=|%s| dbl_delivery_IP=%s\n", dbl_delivery_query, dbl_delivery_ip);
+       }
+  if (dbl_delivery_ip != NULL && dbl_delivery_query != NULL)
+       expand_string(dbl_delivery_query);
+  #endif
   store_reset(reset_point);
   }
 
index 76355afcc7bc9d6c6bfcf798c8462cce12cce13c..587e136856693f36012e6a0246f114765194b9a9 100644 (file)
@@ -806,6 +806,9 @@ fprintf(f, "Support for:");
 #ifdef EXPERIMENTAL_DCC
   fprintf(f, " Experimental_DCC");
 #endif
+#ifdef EXPERIMENTAL_DBL
+  fprintf(f, " EXPERIMENTAL_DBL");
+#endif
 fprintf(f, "\n");
 
 fprintf(f, "Lookups (built-in):");
index 84167b688d60c756ac0eac726622e7c7c99963b8..31d51d79ea9a59de7627861a157ef69c7b84977e 100644 (file)
@@ -410,6 +410,16 @@ static var_entry var_table[] = {
   { "compile_date",        vtype_stringptr,   &version_date },
   { "compile_number",      vtype_stringptr,   &version_cnumber },
   { "csa_status",          vtype_stringptr,   &csa_status },
+#ifdef EXPERIMENTAL_DBL
+  { "dbl_defer_errno",     vtype_int,         &dbl_defer_errno },
+  { "dbl_defer_errstr",    vtype_stringptr,   &dbl_defer_errstr },
+  { "dbl_delivery_confirmation", vtype_stringptr,   &dbl_delivery_confirmation },
+  { "dbl_delivery_domain", vtype_stringptr,   &dbl_delivery_domain },
+  { "dbl_delivery_fqdn",   vtype_stringptr,   &dbl_delivery_fqdn },
+  { "dbl_delivery_ip",     vtype_stringptr,   &dbl_delivery_ip },
+  { "dbl_delivery_local_part",vtype_stringptr,&dbl_delivery_local_part },
+  { "dbl_delivery_port",   vtype_int,         &dbl_delivery_port },
+#endif
 #ifdef EXPERIMENTAL_DCC
   { "dcc_header",          vtype_stringptr,   &dcc_header },
   { "dcc_result",          vtype_stringptr,   &dcc_result },
index f29fb3c49cddd9eab46546c0a1ec292725c59fb4..47f78e9dc00f87364087b20cc6e95f03c2df7d0e 100644 (file)
@@ -435,6 +435,18 @@ uschar *daemon_smtp_port       = US"smtp";
 int     daemon_startup_retries = 9;
 int     daemon_startup_sleep   = 30;
 
+#ifdef EXPERIMENTAL_DBL
+int     dbl_defer_errno        = 0;
+uschar *dbl_defer_errstr       = NULL;
+uschar *dbl_delivery_query     = NULL;
+uschar *dbl_delivery_ip        = NULL;
+int     dbl_delivery_port      = 0;
+uschar *dbl_delivery_fqdn      = NULL;
+uschar *dbl_delivery_local_part= NULL;
+uschar *dbl_delivery_domain    = NULL;
+uschar *dbl_delivery_confirmation = NULL;
+#endif
+
 #ifdef EXPERIMENTAL_DCC
 BOOL    dcc_direct_add_header  = FALSE;
 uschar *dcc_header             = NULL;
index fbbec3230bb8efde8b236b65899d162ef6737fd8..2e1ed46184a74e794e476fa90d4a1b1f24567870 100644 (file)
@@ -265,6 +265,18 @@ extern uschar *dccifd_address;         /* address of the dccifd daemon */
 extern uschar *dccifd_options;         /* options for the dccifd daemon */
 #endif
 
+#ifdef EXPERIMENTAL_DBL
+extern int     dbl_defer_errno;        /* error number set when a remote delivery is deferred with a host error */
+extern uschar *dbl_defer_errstr;       /* error string set when a remote delivery is deferred with a host error */
+extern uschar *dbl_delivery_query;     /* query string to log delivery info in DB */
+extern uschar *dbl_delivery_ip;        /* IP of host, which has accepted delivery */
+extern int     dbl_delivery_port;       /* port of host, which has accepted delivery */
+extern uschar *dbl_delivery_fqdn;      /* FQDN of host, which has accepted delivery */
+extern uschar *dbl_delivery_local_part;/* local part of address being delivered */
+extern uschar *dbl_delivery_domain;    /* domain part of address being delivered */
+extern uschar *dbl_delivery_confirmation; /* SMTP confirmation message */
+#endif
+
 extern BOOL    debug_daemon;           /* Debug the daemon process only */
 extern int     debug_fd;               /* The fd for debug_file */
 extern FILE   *debug_file;             /* Where to write debugging info */
index c3ffe4f824625c7cb853f5f433dc539f1be4dcdf..a0c5ca0cdd0d63b2898c9a684679a385f8881774 100644 (file)
@@ -192,6 +192,9 @@ static optionlist optionlist_config[] = {
   { "daemon_smtp_ports",        opt_stringptr,   &daemon_smtp_port },
   { "daemon_startup_retries",   opt_int,         &daemon_startup_retries },
   { "daemon_startup_sleep",     opt_time,        &daemon_startup_sleep },
+#ifdef EXPERIMENTAL_DBL
+  { "dbl_delivery_query",       opt_stringptr,   &dbl_delivery_query },
+#endif
 #ifdef EXPERIMENTAL_DCC
   { "dcc_direct_add_header",    opt_bool,        &dcc_direct_add_header },
   { "dccifd_address",           opt_stringptr,   &dccifd_address },
index f9f225fca341fa84afffc914e9acee62823c988a..814a63085b3ce5c50fbc6fc959671f7c8ebd4245 100644 (file)
@@ -35,6 +35,10 @@ optionlist smtp_transport_options[] = {
       (void *)offsetof(transport_instance, connection_max_messages) },
   { "data_timeout",         opt_time,
       (void *)offsetof(smtp_transport_options_block, data_timeout) },
+#ifdef EXPERIMENTAL_DBL
+  { "dbl_host_defer_query",      opt_stringptr,
+         (void *)offsetof(smtp_transport_options_block, dbl_host_defer_query) },
+#endif
   { "delay_after_cutoff", opt_bool,
       (void *)offsetof(smtp_transport_options_block, delay_after_cutoff) },
 #ifndef DISABLE_DKIM
@@ -207,6 +211,9 @@ smtp_transport_options_block smtp_transport_option_defaults = {
   NULL,                /* dkim_sign_headers */
   NULL                 /* dkim_strict */
 #endif
+#ifdef EXPERIMENTAL_DBL
+ ,NULL                 /* dbl_host_defer_query */
+#endif
 };
 
 
@@ -551,6 +558,52 @@ else
 
 
 
+#ifdef EXPERIMENTAL_DBL
+/*************************************************
+*          Write error message to database log   *
+*************************************************/
+
+/* This writes to the database log
+
+Arguments:
+  dbl_host_defer_query  dbl_host_defer_query from the transport options block
+  addr                  the address item containing error information
+  host                  the current host
+
+Returns:   nothing
+*/
+
+static void
+dbl_write_defer_log(uschar *dbl_host_defer_query, address_item *addr, host_item *host)
+{
+if (dbl_host_defer_query == NULL)
+       return;
+
+dbl_delivery_ip = string_copy(host->address);
+dbl_delivery_port = (host->port == PORT_NONE)? 25 : host->port;
+dbl_delivery_fqdn = string_copy(host->name);
+dbl_delivery_local_part = string_copy(addr->local_part);
+dbl_delivery_domain = string_copy(addr->domain);
+dbl_defer_errno = addr->basic_errno;
+
+dbl_defer_errstr = NULL;
+if (addr->message != NULL)
+  if (addr->basic_errno > 0)
+    dbl_defer_errstr = string_sprintf("%s: %s", addr->message, strerror(addr->basic_errno));
+  else
+       dbl_defer_errstr = string_copy(addr->message);
+else if (addr->basic_errno > 0)
+  dbl_defer_errstr = string_copy(strerror(addr->basic_errno));
+
+DEBUG(D_transport) {
+       debug_printf("  DBL(host defer): dbl_host_defer_query=|%s| dbl_delivery_IP=%s\n", dbl_host_defer_query, dbl_delivery_ip);
+}
+expand_string(dbl_host_defer_query);
+}
+#endif
+
+
+
 /*************************************************
 *           Synchronize SMTP responses           *
 *************************************************/
@@ -1733,7 +1786,12 @@ if (!ok) ok = TRUE; else
 
     /* Set up confirmation if needed - applies only to SMTP */
 
-    if ((log_extra_selector & LX_smtp_confirmation) != 0 && !lmtp)
+    if (
+               #ifndef EXPERIMENTAL_DBL
+                       (log_extra_selector & LX_smtp_confirmation) != 0 &&
+               #endif
+                       !lmtp
+        )
       {
       uschar *s = string_printing(buffer);
       conf = (s == buffer)? (uschar *)string_copy(s) : s;
@@ -2812,6 +2870,11 @@ for (cutoff_retry = 0; expired &&
                          first_addr->basic_errno != ERRNO_TLSFAILURE)
         write_logs(first_addr, host);
 
+      #ifdef EXPERIMENTAL_DBL
+      if (rc == DEFER)
+        dbl_write_defer_log(ob->dbl_host_defer_query, first_addr, host);
+      #endif
+
       /* If STARTTLS was accepted, but there was a failure in setting up the
       TLS session (usually a certificate screwup), and the host is not in
       hosts_require_tls, and tls_tempfail_tryclear is true, try again, with
@@ -2834,6 +2897,10 @@ for (cutoff_retry = 0; expired &&
           expanded_hosts != NULL, &message_defer, TRUE);
         if (rc == DEFER && first_addr->basic_errno != ERRNO_AUTHFAIL)
           write_logs(first_addr, host);
+        #ifdef EXPERIMENTAL_DBL
+        if (rc == DEFER)
+          dbl_write_defer_log(ob->dbl_host_defer_query, first_addr, host);
+        #endif
         }
       #endif
       }
index 621cb6ba9ee451c83f23024a4ec6196db897ac46..1f23d39c8abcaa549688622b0c1b559a500a0a1e 100644 (file)
@@ -64,6 +64,9 @@ typedef struct {
   uschar *dkim_sign_headers;
   uschar *dkim_strict;
   #endif
+  #ifdef EXPERIMENTAL_DBL
+  uschar *dbl_host_defer_query;
+  #endif
 } smtp_transport_options_block;
 
 /* Data for reading the private options. */