Added the hosts_max_try_hardlimit option. (The removed file is left over
[exim.git] / src / src / transports / smtp.c
index 424f9afa92a394a604a5cd4fe3e5f285cd3385aa..738fd1cc282fffe3d64251a03b485abb76ddf955 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/transports/smtp.c,v 1.3 2005/01/04 10:00:45 ph10 Exp $ */
+/* $Cambridge: exim/src/src/transports/smtp.c,v 1.4 2005/01/04 16:36:28 ph10 Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
@@ -57,6 +57,8 @@ optionlist smtp_transport_options[] = {
   #endif
   { "hosts_max_try",        opt_int,
       (void *)offsetof(smtp_transport_options_block, hosts_max_try) },
+  { "hosts_max_try_hardlimit", opt_int,
+      (void *)offsetof(smtp_transport_options_block, hosts_max_try_hardlimit) },
   #ifdef SUPPORT_TLS
   { "hosts_nopass_tls",     opt_stringptr,
       (void *)offsetof(smtp_transport_options_block, hosts_nopass_tls) },
@@ -138,6 +140,7 @@ smtp_transport_options_block smtp_transport_option_defaults = {
   10*60,               /* final timeout */
   1024,                /* size_addition */
   5,                   /* hosts_max_try */
+  50,                  /* hosts_max_try_hardlimit */ 
   FALSE,               /* allow_localhost */
   FALSE,               /* gethostbyname */
   TRUE,                /* dns_qualify_single */
@@ -1926,6 +1929,7 @@ int hosts_looked_up = 0;
 int hosts_retry = 0;
 int hosts_serial = 0;
 int hosts_total = 0;
+int total_hosts_tried = 0; 
 address_item *addr;
 BOOL expired = TRUE;
 BOOL continuing = continue_hostname != NULL;
@@ -2091,7 +2095,9 @@ if (Ustrcmp(pistring, ":25") == 0) pistring = US"";
 
 .  If there are any addresses whose status is still DEFER, carry on to the
    next host/IPaddress, unless we have tried the number of hosts given
-   by hosts_max_try; otherwise return.
+   by hosts_max_try or hosts_max_try_hardlimit; otherwise return. Note that
+   there is some fancy logic for hosts_max_try that means its limit can be 
+   overstepped in some circumstances. 
 
 If we get to the end of the list, all hosts have deferred at least one address,
 or not reached their retry times. If delay_after_cutoff is unset, it requests a
@@ -2108,7 +2114,9 @@ for (cutoff_retry = 0; expired &&
   int unexpired_hosts_tried = 0;
 
   for (host = hostlist;
-       host != NULL && unexpired_hosts_tried < ob->hosts_max_try;
+       host != NULL && 
+         unexpired_hosts_tried < ob->hosts_max_try &&
+         total_hosts_tried < ob->hosts_max_try_hardlimit;
        host = nexthost)
     {
     int rc;
@@ -2413,13 +2421,16 @@ for (cutoff_retry = 0; expired &&
 
     /* This is for real. If the host is expired, we don't count it for
     hosts_max_retry. This ensures that all hosts must expire before an address
-    is timed out. Otherwise, if we are about to hit the hosts_max_retry limit,
-    check to see if there is a subsequent hosts with a different MX value. If
-    so, make that the next host, and don't count this one. This is a heuristic
-    to make sure that different MXs do get tried. With a normal kind of retry
-    rule, they would get tried anyway when the earlier hosts were delayed, but
-    if the domain has a "retry every time" type of rule - as is often used for
-    the the very large ISPs, that won't happen. */
+    is timed out, unless hosts_max_try_hardlimit (which protects against 
+    lunatic DNS configurations) is reached.
+    
+    If the host is not expired and we are about to hit the hosts_max_retry
+    limit, check to see if there is a subsequent hosts with a different MX
+    value. If so, make that the next host, and don't count this one. This is a
+    heuristic to make sure that different MXs do get tried. With a normal kind
+    of retry rule, they would get tried anyway when the earlier hosts were
+    delayed, but if the domain has a "retry every time" type of rule - as is
+    often used for the the very large ISPs, that won't happen. */
 
     else
       {
@@ -2441,6 +2452,7 @@ for (cutoff_retry = 0; expired &&
 
       /* Attempt the delivery. */
 
+      total_hosts_tried++;
       rc = smtp_deliver(addrlist, host, host_af, port, interface, tblock,
         expanded_hosts != NULL, &message_defer, FALSE);
 
@@ -2606,7 +2618,7 @@ for (cutoff_retry = 0; expired &&
     maximum retry time for this host. This means we may try try all hosts,
     ignoring the limit, when messages have been around for some time. This is
     important because if we don't try all hosts, the address will never time
-    out. */
+    out. NOTE: this does not apply to hosts_max_try_hardlimit. */
 
     if ((rc == DEFER || some_deferred) && nexthost != NULL)
       {
@@ -2671,15 +2683,26 @@ found, we end up here, but can detect these cases and handle them specially. */
 for (addr = addrlist; addr != NULL; addr = addr->next)
   {
   /* If host is not NULL, it means that we stopped processing the host list
-  because of hosts_max_try. This means we need to behave as if some hosts were
-  skipped because their retry time had not come. Specifically, this prevents
-  the address from timing out. */
+  because of hosts_max_try or hosts_max_try_hardlimit. In the former case, this
+  means we need to behave as if some hosts were skipped because their retry
+  time had not come. Specifically, this prevents the address from timing out.
+  However, if we have hit hosts_max_try_hardlimit, we want to behave as if all 
+  hosts were tried. */
 
   if (host != NULL)
     {
-    DEBUG(D_transport)
-      debug_printf("hosts_max_try limit caused some hosts to be skipped\n");
-    setflag(addr, af_retry_skipped);
+    if (total_hosts_tried >= ob->hosts_max_try_hardlimit)
+      {
+      DEBUG(D_transport)
+        debug_printf("hosts_max_try_hardlimit reached: behave as if all "
+          "hosts were tried\n");
+      }
+    else
+      {       
+      DEBUG(D_transport)
+        debug_printf("hosts_max_try limit caused some hosts to be skipped\n");
+      setflag(addr, af_retry_skipped);
+      } 
     }
 
   if (queue_smtp)    /* no deliveries attempted */