X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/d7d7b7b91dd75cec636fc144da7e27eed860f971..184e88237dea64ce48076cdd0184612d057cbafd:/src/src/retry.c diff --git a/src/src/retry.c b/src/src/retry.c index b54bb7d4a..02eda07ff 100644 --- a/src/src/retry.c +++ b/src/src/retry.c @@ -1,10 +1,10 @@ -/* $Cambridge: exim/src/src/retry.c,v 1.5 2006/02/07 11:19:00 ph10 Exp $ */ +/* $Cambridge: exim/src/src/retry.c,v 1.12 2007/01/08 10:50:18 ph10 Exp $ */ /************************************************* * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2006 */ +/* Copyright (c) University of Cambridge 1995 - 2007 */ /* See the file NOTICE for conditions of use and distribution. */ /* Functions concerned with retrying unsuccessful deliveries. */ @@ -46,8 +46,16 @@ 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(" 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_printf("no retry rule found: assume timed out\n"); + } return address_timeout; } @@ -201,8 +209,14 @@ if (host_retry_record != NULL) 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"); + debug_printf(" now=%d first_failed=%d next_try=%d expired=%d\n", + (int)now, (int)host_retry_record->first_failed, + (int)host_retry_record->next_try, + host_retry_record->expired); + } if (!host_retry_record->expired && ultimate_address_timeout(host_key, domain, @@ -238,8 +252,13 @@ if (message_retry_record != NULL) 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"); + debug_printf(" now=%d first_failed=%d next_try=%d expired=%d\n", + (int)now, (int)message_retry_record->first_failed, + (int)message_retry_record->next_try, message_retry_record->expired); + } if (!ultimate_address_timeout(host_key, domain, 0, 0, now)) { host->status = hstatus_unusable; @@ -340,23 +359,38 @@ retry_config * retry_find_config(uschar *key, uschar *alternate, int basic_errno, int more_errno) { -int replace; +int replace = 0; uschar *use_key, *use_alternate; uschar *colon = Ustrchr(key, ':'); retry_config *yield; -/* If there's a colon in the key, temporarily replace it with -a zero to terminate the string there. */ +/* If there's a colon in the key, there are two possibilities: + +(1) This is a key for a host, ip address, and possibly port, in the format + + hostname:ip+port + + In this case, we temporarily replace the colon with a zero, to terminate + the string after the host name. + +(2) This is a key for a pipe, file, or autoreply delivery, in the format + + pipe-or-file-or-auto:x@y + + where x@y is the original address that provoked the delivery. The pipe or + file or auto will start with | or / or >, whereas a host name will start + with a letter or a digit. In this case we want to use the original address + to search for a retry rule. */ if (colon != NULL) { - replace = ':'; - } -else - { - colon = key + Ustrlen(key); - replace = 0; + if (isalnum(*key)) + replace = ':'; + else + key = Ustrrchr(key, ':') + 1; /* Take from the last colon */ } + +if (replace == 0) colon = key + Ustrlen(key); *colon = 0; /* Sort out the keys */ @@ -390,20 +424,31 @@ for (yield = retries; yield != NULL; yield = yield->next) 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) { @@ -619,10 +664,12 @@ for (i = 0; i < 3; i++) DEBUG(D_retry) { if ((rti->flags & rf_host) != 0) - debug_printf("retry for %s (%s) = %s\n", rti->key, - addr->domain, retry->pattern); + debug_printf("retry for %s (%s) = %s %d %d\n", rti->key, + addr->domain, retry->pattern, retry->basic_errno, + retry->more_errno); else - debug_printf("retry for %s = %s\n", rti->key, retry->pattern); + debug_printf("retry for %s = %s %d %d\n", rti->key, retry->pattern, + retry->basic_errno, retry->more_errno); } /* Set up the message for the database retry record. Because DBM @@ -658,6 +705,17 @@ for (i = 0; i < 3; i++) /* Compute how long this destination has been failing */ failing_interval = now - retry_record->first_failed; + DEBUG(D_retry) debug_printf("failing_interval=%d message_age=%d\n", + failing_interval, message_age); + + /* 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 ((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 @@ -712,7 +770,14 @@ for (i = 0; i < 3; i++) This implements "timeout this rule if EITHER the host (or routing or directing) has been failing for more than the maximum time, OR if the - message has been on the queue for more than the maximum time." */ + message has been on the queue for more than the maximum time." + + February 2006: It is possible that this code is no longer needed + following the change to the retry calculation to use the message age if + it is larger than the time since first failure. It may be that the + expired flag is always set when the other conditions are met. However, + this is a small bit of code, and it does no harm to leave it in place, + just in case. */ if (received_time <= retry_record->first_failed && addr == endaddr && !retry_record->expired && rule != NULL) @@ -755,7 +820,8 @@ for (i = 0; i < 3; i++) { 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; } } }