-/* $Cambridge: exim/src/src/retry.c,v 1.7 2006/02/09 14:50:58 ph10 Exp $ */
-
/*************************************************
* Exim - an Internet mail transport agent *
*************************************************/
-/* Copyright (c) University of Cambridge 1995 - 2006 */
+/* Copyright (c) University of Cambridge 1995 - 2009 */
/* See the file NOTICE for conditions of use and distribution. */
/* Functions concerned with retrying unsuccessful deliveries. */
*************************************************/
/* This function tests whether a message has been on the queue longer than
-the maximum retry time for a particular host.
+the maximum retry time for a particular host or address.
Arguments:
- host_key the key to look up a host retry rule
+ retry_key the key to look up a retry rule
domain the domain to look up a domain retry rule
- basic_errno a specific error number, or zero if none
- more_errno additional data for the error
+ retry_record contains error information for finding rule
now the time
Returns: TRUE if the ultimate timeout has been reached
*/
-static BOOL
-ultimate_address_timeout(uschar *host_key, uschar *domain, int basic_errno,
- int more_errno, time_t now)
+BOOL
+retry_ultimate_address_timeout(uschar *retry_key, uschar *domain,
+ dbdata_retry *retry_record, time_t now)
{
-BOOL address_timeout = TRUE; /* no rule => timed out */
+BOOL address_timeout;
+
+DEBUG(D_retry)
+ {
+ debug_printf("retry time not reached: checking ultimate address timeout\n");
+ 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);
+ }
retry_config *retry =
- retry_find_config(host_key+2, domain, basic_errno, more_errno);
+ retry_find_config(retry_key+2, domain,
+ retry_record->basic_errno, retry_record->more_errno);
if (retry != NULL && retry->rules != NULL)
{
for (last_rule = retry->rules;
last_rule->next != NULL;
last_rule = last_rule->next);
- DEBUG(D_transport|D_retry)
- debug_printf("now=%d received_time=%d diff=%d timeout=%d\n",
- (int)now, received_time, (int)(now - received_time),
- last_rule->timeout);
+ DEBUG(D_retry)
+ debug_printf(" received_time=%d diff=%d timeout=%d\n",
+ received_time, (int)(now - received_time), last_rule->timeout);
address_timeout = (now - received_time > last_rule->timeout);
}
else
{
- DEBUG(D_transport|D_retry)
+ DEBUG(D_retry)
debug_printf("no retry rule found: assume timed out\n");
+ address_timeout = TRUE;
}
+DEBUG(D_retry)
+ if (address_timeout)
+ debug_printf("on queue longer than maximum retry for address - "
+ "allowing delivery\n");
+
return address_timeout;
}
if (now < host_retry_record->next_try && !deliver_force)
{
- DEBUG(D_transport|D_retry)
- debug_printf("host retry time not reached: checking ultimate address "
- "timeout\n");
-
if (!host_retry_record->expired &&
- ultimate_address_timeout(host_key, domain,
- host_retry_record->basic_errno, host_retry_record->more_errno, now))
- {
- DEBUG(D_transport|D_retry)
- debug_printf("on queue longer than maximum retry for "
- "address - allowing delivery\n");
+ retry_ultimate_address_timeout(host_key, domain,
+ host_retry_record, now))
return FALSE;
- }
/* We have not hit the ultimate address timeout; host is unusable. */
*retry_message_key = message_key;
if (now < message_retry_record->next_try && !deliver_force)
{
- DEBUG(D_transport|D_retry)
- debug_printf("host+message retry time not reached: checking ultimate "
- "address timeout\n");
- if (!ultimate_address_timeout(host_key, domain, 0, 0, now))
+ if (!retry_ultimate_address_timeout(host_key, domain,
+ message_retry_record, now))
{
host->status = hstatus_unusable;
host->why = hwhy_retry;
}
- else
- {
- DEBUG(D_transport|D_retry)
- debug_printf("on queue longer than maximum retry for "
- "address - allowing delivery\n");
- }
return FALSE;
}
}
continue;
}
- /* Handle 4xx responses to RCPT. The code that was received is in the 2nd
- least significant byte of more_errno (with 400 subtracted). The required
- value is coded in the 2nd least significant byte of the yield->more_errno
- field as follows:
+ /* The TLSREQUIRED error also covers TLSFAILURE. These are subtly different
+ errors, but not worth separating at this level. */
+
+ else if (yield->basic_errno == ERRNO_TLSREQUIRED)
+ {
+ if (basic_errno != ERRNO_TLSREQUIRED && basic_errno != ERRNO_TLSFAILURE)
+ continue;
+ }
+
+ /* Handle 4xx responses to MAIL, RCPT, or DATA. The code that was received
+ is in the 2nd least significant byte of more_errno (with 400 subtracted).
+ The required value is coded in the 2nd least significant byte of the
+ yield->more_errno field as follows:
255 => any 4xx code
>= 100 => the decade must match the value less 100
< 100 => the exact value must match
*/
- else if (yield->basic_errno == ERRNO_RCPT4XX)
+ else if (yield->basic_errno == ERRNO_MAIL4XX ||
+ yield->basic_errno == ERRNO_RCPT4XX ||
+ yield->basic_errno == ERRNO_DATA4XX)
{
int wanted;
- if (basic_errno != ERRNO_RCPT4XX) continue;
+ if (basic_errno != yield->basic_errno) continue;
wanted = (yield->more_errno >> 8) & 255;
if (wanted != 255)
{
DEBUG(D_retry) debug_printf("failing_interval=%d message_age=%d\n",
failing_interval, message_age);
- /* If the message has been on the queue longer than the recorded time
- of failure, use the message's age instead. This can happen when some
- messages can be delivered and others cannot; a successful delivery will
- reset the first_failed time, and this can lead to a failing message
- being retried too often. */
+ /* For a non-host error, if the message has been on the queue longer
+ than the recorded time of failure, use the message's age instead. This
+ can happen when some messages can be delivered and others cannot; a
+ successful delivery will reset the first_failed time, and this can lead
+ to a failing message being retried too often. */
- if (message_age > failing_interval) failing_interval = message_age;
+ if ((rti->flags & rf_host) == 0 && message_age > failing_interval)
+ failing_interval = message_age;
/* Search for the current retry rule. The cutoff time of the
last rule is handled differently to the others. The rule continues
{
next_try = now + rule->p1;
if (next_gap > rule->p1)
- next_try += random_number(next_gap - rule->p1);
+ next_try += random_number(next_gap - rule->p1)/2 +
+ (next_gap - rule->p1)/2;
}
}
}