Dnssec observability: add variable $lookup_dnssec_authenticated
[exim.git] / src / src / deliver.c
index 34f75fe13c42a03f2cebc1f13f67b3c2362208a9..1e7a8a18a124fe2ab3111aba1eaedce0774424c0 100644 (file)
@@ -2,7 +2,7 @@
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
-/* Copyright (c) University of Cambridge 1995 - 2009 */
+/* Copyright (c) University of Cambridge 1995 - 2014 */
 /* See the file NOTICE for conditions of use and distribution. */
 
 /* The main code for delivering a message. */
@@ -673,8 +673,36 @@ while (addr->parent != NULL)
 
 
 
+static uschar *
+d_hostlog(uschar * s, int * sizep, int * ptrp, address_item * addr)
+{
+  s = string_append(s, sizep, ptrp, 5, US" H=", addr->host_used->name,
+    US" [", addr->host_used->address, US"]");
+  if ((log_extra_selector & LX_outgoing_port) != 0)
+    s = string_append(s, sizep, ptrp, 2, US":", string_sprintf("%d",
+      addr->host_used->port));
+  return s;
+}
+
+#ifdef SUPPORT_TLS
+static uschar *
+d_tlslog(uschar * s, int * sizep, int * ptrp, address_item * addr)
+{
+  if ((log_extra_selector & LX_tls_cipher) != 0 && addr->cipher != NULL)
+    s = string_append(s, sizep, ptrp, 2, US" X=", addr->cipher);
+  if ((log_extra_selector & LX_tls_certificate_verified) != 0 &&
+       addr->cipher != NULL)
+    s = string_append(s, sizep, ptrp, 2, US" CV=",
+      testflag(addr, af_cert_verified)? "yes":"no");
+  if ((log_extra_selector & LX_tls_peerdn) != 0 && addr->peerdn != NULL)
+    s = string_append(s, sizep, ptrp, 3, US" DN=\"",
+      string_printing(addr->peerdn), US"\"");
+  return s;
+}
+#endif
+
 /* If msg is NULL this is a delivery log and logchar is used. Otherwise
-this is a nonstandard call; no two-characher delivery flag is written
+this is a nonstandard call; no two-character delivery flag is written
 but sender-host and sender are prefixed and "msg" is inserted in the log line.
 
 Arguments:
@@ -695,6 +723,15 @@ the log line, and reset the store afterwards. Remote deliveries should always
 have a pointer to the host item that succeeded; local deliveries can have a
 pointer to a single host item in their host list, for use by the transport. */
 
+#ifdef EXPERIMENTAL_TPDA
+  tpda_delivery_ip = NULL;     /* presume no successful remote delivery */
+  tpda_delivery_port = 0;
+  tpda_delivery_fqdn = NULL;
+  tpda_delivery_local_part = NULL;
+  tpda_delivery_domain = NULL;
+  tpda_delivery_confirmation = NULL;
+#endif
+
 s = reset_point = store_get(size);
 
 log_address = string_log_address(addr, (log_write_selector & L_all_parents) != 0, TRUE);
@@ -741,7 +778,12 @@ if ((log_extra_selector & LX_delivery_size) != 0)
 if (addr->transport->info->local)
   {
   if (addr->host_list != NULL)
+    {
     s = string_append(s, &size, &ptr, 2, US" H=", addr->host_list->name);
+    #ifdef EXPERIMENTAL_TPDA
+      tpda_delivery_fqdn = addr->host_list->name;
+    #endif
+    }
   if (addr->shadow_message != NULL)
     s = string_cat(s, &size, &ptr, addr->shadow_message,
       Ustrlen(addr->shadow_message));
@@ -753,25 +795,22 @@ else
   {
   if (addr->host_used != NULL)
     {
-    s = string_append(s, &size, &ptr, 5, US" H=", addr->host_used->name,
-      US" [", addr->host_used->address, US"]");
-    if ((log_extra_selector & LX_outgoing_port) != 0)
-      s = string_append(s, &size, &ptr, 2, US":", string_sprintf("%d",
-        addr->host_used->port));
+    s = d_hostlog(s, &size, &ptr, addr);
     if (continue_sequence > 1)
       s = string_cat(s, &size, &ptr, US"*", 1);
+
+    #ifdef EXPERIMENTAL_TPDA
+    tpda_delivery_ip =           addr->host_used->address;
+    tpda_delivery_port =         addr->host_used->port;
+    tpda_delivery_fqdn =         addr->host_used->name;
+    tpda_delivery_local_part =   addr->local_part;
+    tpda_delivery_domain =       addr->domain;
+    tpda_delivery_confirmation = 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);
-  if ((log_extra_selector & LX_tls_certificate_verified) != 0 &&
-       addr->cipher != NULL)
-    s = string_append(s, &size, &ptr, 2, US" CV=",
-      testflag(addr, af_cert_verified)? "yes":"no");
-  if ((log_extra_selector & LX_tls_peerdn) != 0 && addr->peerdn != NULL)
-    s = string_append(s, &size, &ptr, 3, US" DN=\"",
-      string_printing(addr->peerdn), US"\"");
+  s = d_tlslog(s, &size, &ptr, addr);
   #endif
 
   if (addr->authenticator)
@@ -785,22 +824,30 @@ else
       }
     }
 
-  if ((log_extra_selector & LX_smtp_confirmation) != 0 &&
-      addr->message != NULL)
-    {
-    int i;
-    uschar *p = big_buffer;
-    uschar *ss = addr->message;
-    *p++ = '\"';
-    for (i = 0; i < 100 && ss[i] != 0; i++)
-      {
-      if (ss[i] == '\"' || ss[i] == '\\') *p++ = '\\';
-      *p++ = ss[i];
-      }
-    *p++ = '\"';
-    *p = 0;
-    s = string_append(s, &size, &ptr, 2, US" C=", big_buffer);
-    }
+  #ifdef EXPERIMENTAL_PRDR
+  if (addr->flags & af_prdr_used)
+    s = string_append(s, &size, &ptr, 1, US" PRDR");
+  #endif
+  }
+
+/* confirmation message (SMTP (host_used) and LMTP (driver_name)) */
+
+if ((log_extra_selector & LX_smtp_confirmation) != 0 &&
+    addr->message != NULL &&
+    ((addr->host_used != NULL) || (Ustrcmp(addr->transport->driver_name, "lmtp") == 0)))
+  {
+  int i;
+  uschar *p = big_buffer;
+  uschar *ss = addr->message;
+  *p++ = '\"';
+  for (i = 0; i < 100 && ss[i] != 0; i++)
+    {
+    if (ss[i] == '\"' || ss[i] == '\\') *p++ = '\\';
+    *p++ = ss[i];
+    }
+  *p++ = '\"';
+  *p = 0;
+  s = string_append(s, &size, &ptr, 2, US" C=", big_buffer);
   }
 
 /* Time on queue and actual time taken to deliver */
@@ -822,6 +869,23 @@ store we used to build the line after writing it. */
 
 s[ptr] = 0;
 log_write(0, flags, "%s", s);
+
+#ifdef EXPERIMENTAL_TPDA
+if (addr->transport->tpda_delivery_action)
+  {
+  DEBUG(D_deliver)
+    debug_printf("  TPDA(Delivery): tpda_deliver_action=|%s| tpda_delivery_IP=%s\n",
+      addr->transport->tpda_delivery_action, tpda_delivery_ip);
+
+  router_name =    addr->router->name;
+  transport_name = addr->transport->name;
+  if (!expand_string(addr->transport->tpda_delivery_action) && *expand_string_message)
+    log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand tpda_deliver_action in %s: %s\n",
+      transport_name, expand_string_message);
+  router_name = NULL;
+  transport_name = NULL;
+  }
+#endif
 store_reset(reset_point);
 return;
 }
@@ -900,6 +964,9 @@ if (addr->message != NULL)
   if (((Ustrstr(addr->message, "failed to expand") != NULL) || (Ustrstr(addr->message, "expansion of ") != NULL)) &&
       (Ustrstr(addr->message, "mysql") != NULL ||
        Ustrstr(addr->message, "pgsql") != NULL ||
+#ifdef EXPERIMENTAL_REDIS
+       Ustrstr(addr->message, "redis") != NULL ||
+#endif
        Ustrstr(addr->message, "sqlite") != NULL ||
        Ustrstr(addr->message, "ldap:") != NULL ||
        Ustrstr(addr->message, "ldapdn:") != NULL ||
@@ -1188,9 +1255,7 @@ else
 
   if (used_return_path != NULL &&
       (log_extra_selector & LX_return_path_on_delivery) != 0)
-    {
     s = string_append(s, &size, &ptr, 3, US" P=<", used_return_path, US">");
-    }
 
   if (addr->router != NULL)
     s = string_append(s, &size, &ptr, 2, US" R=", addr->router->name);
@@ -1198,8 +1263,11 @@ else
     s = string_append(s, &size, &ptr, 2, US" T=", addr->transport->name);
 
   if (addr->host_used != NULL)
-    s = string_append(s, &size, &ptr, 5, US" H=", addr->host_used->name,
-      US" [", addr->host_used->address, US"]");
+    s = d_hostlog(s, &size, &ptr, addr);
+
+  #ifdef SUPPORT_TLS
+  s = d_tlslog(s, &size, &ptr, addr);
+  #endif
 
   if (addr->basic_errno > 0)
     s = string_append(s, &size, &ptr, 2, US": ",
@@ -1871,6 +1939,9 @@ if ((pid = fork()) == 0)
     set_process_info("delivering %s to %s using %s", message_id,
      addr->local_part, addr->transport->name);
 
+    /* Setting this global in the subprocess means we need never clear it */
+    transport_name = addr->transport->name;
+
     /* If a transport filter has been specified, set up its argument list.
     Any errors will get put into the address, and FALSE yielded. */
 
@@ -2386,45 +2457,8 @@ while (addr_local != NULL)
           to do, which is for the ultimate address timeout. */
 
           if (!ok)
-            {
-            retry_config *retry =
-              retry_find_config(retry_key+2, addr2->domain,
-                retry_record->basic_errno,
-                retry_record->more_errno);
-
-            DEBUG(D_deliver|D_retry)
-              {
-              debug_printf("retry time not reached for %s: "
-                "checking ultimate address timeout\n", addr2->address);
-              debug_printf("  now=%d first_failed=%d next_try=%d expired=%d\n",
-                (int)now, (int)retry_record->first_failed,
-                (int)retry_record->next_try, retry_record->expired);
-              }
-
-            if (retry != NULL && retry->rules != NULL)
-              {
-              retry_rule *last_rule;
-              for (last_rule = retry->rules;
-                   last_rule->next != NULL;
-                   last_rule = last_rule->next);
-              DEBUG(D_deliver|D_retry)
-                debug_printf("  received_time=%d diff=%d timeout=%d\n",
-                  received_time, (int)now - received_time, last_rule->timeout);
-              if (now - received_time > last_rule->timeout) ok = TRUE;
-              }
-            else
-              {
-              DEBUG(D_deliver|D_retry)
-                debug_printf("no retry rule found: assume timed out\n");
-              ok = TRUE;    /* No rule => timed out */
-              }
-
-            DEBUG(D_deliver|D_retry)
-              {
-              if (ok) debug_printf("on queue longer than maximum retry for "
-                "address - allowing delivery\n");
-              }
-            }
+            ok = retry_ultimate_address_timeout(retry_key, addr2->domain,
+                retry_record, now);
           }
         }
       else DEBUG(D_retry) debug_printf("no retry record exists\n");
@@ -2947,6 +2981,11 @@ while (!done)
     while (*ptr++);
     break;
 
+#ifdef EXPERIMENTAL_PRDR
+    case 'P':
+      addr->flags |= af_prdr_used; break;
+#endif
+
     case 'A':
     if (addr == NULL)
       {
@@ -3892,8 +3931,10 @@ for (delivery_count = 0; addr_remote != NULL; delivery_count++)
     int fd = pfd[pipe_write];
     host_item *h;
 
-    /* There are weird circumstances in which logging is disabled */
+    /* Setting this global in the subprocess means we need never clear it */
+    transport_name = tp->name;
 
+    /* There are weird circumstances in which logging is disabled */
     disable_logging = tp->disable_logging;
 
     /* Show pids on debug output if parallelism possible */
@@ -4049,6 +4090,10 @@ for (delivery_count = 0; addr_remote != NULL; delivery_count++)
         rmt_dlv_checked_write(fd, big_buffer, ptr - big_buffer);
        }
 
+      #ifdef EXPERIMENTAL_PRDR
+      if (addr->flags & af_prdr_used) rmt_dlv_checked_write(fd, "P", 1);
+      #endif
+
       /* Retry information: for most success cases this will be null. */
 
       for (r = addr->retries; r != NULL; r = r->next)
@@ -5656,7 +5701,18 @@ while (addr_new != NULL)           /* Loop until all addresses dealt with */
     will be far too many attempts for an address that gets a 4xx error. In
     fact, after such an error, we should not get here because, the host should
     not be remembered as one this message needs. However, there was a bug that
-    used to cause this to  happen, so it is best to be on the safe side. */
+    used to cause this to  happen, so it is best to be on the safe side.
+
+    Even if we haven't reached the retry time in the hints, there is one more
+    check to do, which is for the ultimate address timeout. We only do this
+    check if there is an address retry record and there is not a domain retry
+    record; this implies that previous attempts to handle the address had the
+    retry_use_local_parts option turned on. We use this as an approximation
+    for the destination being like a local delivery, for example delivery over
+    LMTP to an IMAP message store. In this situation users are liable to bump
+    into their quota and thereby have intermittently successful deliveries,
+    which keep the retry record fresh, which can lead to us perpetually
+    deferring messages. */
 
     else if (((queue_running && !deliver_force) || continue_hostname != NULL)
             &&
@@ -5666,7 +5722,11 @@ while (addr_new != NULL)           /* Loop until all addresses dealt with */
             ||
             (address_retry_record != NULL &&
               now < address_retry_record->next_try))
-            )
+            &&
+           (domain_retry_record != NULL ||
+            address_retry_record == NULL ||
+            !retry_ultimate_address_timeout(addr->address_retry_key,
+              addr->domain, address_retry_record, now)))
       {
       addr->message = US"retry time not reached";
       addr->basic_errno = ERRNO_RRETRY;
@@ -6118,6 +6178,11 @@ if (addr_remote != NULL)
     regex_must_compile(US"\\n250[\\s\\-]STARTTLS(\\s|\\n|$)", FALSE, TRUE);
   #endif
 
+  #ifdef EXPERIMENTAL_PRDR
+  if (regex_PRDR == NULL) regex_PRDR =
+    regex_must_compile(US"\\n250[\\s\\-]PRDR(\\s|\\n|$)", FALSE, TRUE);
+  #endif
+
   /* Now sort the addresses if required, and do the deliveries. The yield of
   do_remote_deliveries is FALSE when mua_wrapper is set and all addresses
   cannot be delivered in one transaction. */
@@ -7120,4 +7185,6 @@ acl_where = ACL_WHERE_UNKNOWN;
 return final_yield;
 }
 
+/* vi: aw ai sw=2
+*/
 /* End of deliver.c */