X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/cab0c27721a3c1f3a146e44bcc6462eefb9eb9e7..340e5f5781abb5388c41fa326d6e1cabf7ba96ff:/src/src/retry.c diff --git a/src/src/retry.c b/src/src/retry.c index 91c27104a..a34bf80ca 100644 --- a/src/src/retry.c +++ b/src/src/retry.c @@ -2,8 +2,10 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2015 */ +/* Copyright (c) The Exim Maintainers 2020 - 2022 */ +/* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* Functions concerned with retrying unsuccessful deliveries. */ @@ -33,28 +35,27 @@ retry_ultimate_address_timeout(uschar *retry_key, const uschar *domain, dbdata_retry *retry_record, time_t now) { BOOL address_timeout; +retry_config * retry; 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); + debug_printf(" now=" TIME_T_FMT " first_failed=" TIME_T_FMT + " next_try=" TIME_T_FMT " expired=%c\n", + now, retry_record->first_failed, + retry_record->next_try, retry_record->expired ? 'T' : 'F'); } -retry_config *retry = - retry_find_config(retry_key+2, domain, +retry = retry_find_config(retry_key+2, domain, retry_record->basic_errno, retry_record->more_errno); -if (retry != NULL && retry->rules != NULL) +if (retry && retry->rules) { retry_rule *last_rule; - for (last_rule = retry->rules; - last_rule->next != NULL; - last_rule = last_rule->next); + for (last_rule = retry->rules; last_rule->next; last_rule = last_rule->next) ; DEBUG(D_retry) - debug_printf(" received_time=%d diff=%d timeout=%d\n", - (int)received_time.tv_sec, (int)(now - received_time.tv_sec), last_rule->timeout); + debug_printf(" received_time=" TIME_T_FMT " diff=%d timeout=%d\n", + received_time.tv_sec, (int)(now - received_time.tv_sec), last_rule->timeout); address_timeout = (now - received_time.tv_sec > last_rule->timeout); } else @@ -127,15 +128,14 @@ retry_check_address(const uschar *domain, host_item *host, uschar *portstring, { BOOL yield = FALSE; time_t now = time(NULL); -uschar *host_key, *message_key; -open_db dbblock; -open_db *dbm_file; -tree_node *node; -dbdata_retry *host_retry_record, *message_retry_record; +uschar * host_key, * message_key; +open_db dbblock, * dbm_file; +tree_node * node; +dbdata_retry * host_retry_record, * message_retry_record; *retry_host_key = *retry_message_key = NULL; -DEBUG(D_transport|D_retry) debug_printf("checking status of %s\n", host->name); +DEBUG(D_transport|D_retry) debug_printf("checking retry status of %s\n", host->name); /* Do nothing if status already set; otherwise initialize status as usable. */ @@ -145,9 +145,9 @@ host->status = hstatus_usable; /* Generate the host key for the unusable tree and the retry database. Ensure host names are lower cased (that's what %S does). */ -host_key = include_ip_address? - string_sprintf("T:%S:%s%s", host->name, host->address, portstring) : - string_sprintf("T:%S%s", host->name, portstring); +host_key = include_ip_address + ? string_sprintf("T:%S:%s%s", host->name, host->address, portstring) + : string_sprintf("T:%S%s", host->name, portstring); /* Generate the message-specific key */ @@ -171,7 +171,7 @@ if ((node = tree_search(tree_unusable, host_key))) /* Open the retry database, giving up if there isn't one. Otherwise, search for the retry records, and then close the database again. */ -if (!(dbm_file = dbfn_open(US"retry", O_RDONLY, &dbblock, FALSE))) +if (!(dbm_file = dbfn_open(US"retry", O_RDONLY, &dbblock, FALSE, TRUE))) { DEBUG(D_deliver|D_retry|D_hints_lookup) debug_printf("no retry data available\n"); @@ -217,7 +217,7 @@ if (host_retry_record) /* We have not reached the next try time. Check for the ultimate address timeout if the host has not expired. */ - if (now < host_retry_record->next_try && !deliver_force) + if (now < host_retry_record->next_try && !f.deliver_force) { if (!host_retry_record->expired && retry_ultimate_address_timeout(host_key, domain, @@ -245,7 +245,7 @@ unless the ultimate address timeout has been reached. */ if (message_retry_record) { *retry_message_key = message_key; - if (now < message_retry_record->next_try && !deliver_force) + if (now < message_retry_record->next_try && !f.deliver_force) { if (!retry_ultimate_address_timeout(host_key, domain, message_retry_record, now)) @@ -292,7 +292,7 @@ Returns: nothing void retry_add_item(address_item *addr, uschar *key, int flags) { -retry_item *rti = store_get(sizeof(retry_item)); +retry_item * rti = store_get(sizeof(retry_item), GET_UNTAINTED); host_item * host = addr->host_used; rti->next = addr->retries; @@ -524,7 +524,6 @@ retry_update(address_item **addr_defer, address_item **addr_failed, open_db dbblock; open_db *dbm_file = NULL; time_t now = time(NULL); -int i; DEBUG(D_retry) debug_printf("Processing retry items\n"); @@ -532,7 +531,7 @@ DEBUG(D_retry) debug_printf("Processing retry items\n"); Deferred addresses must be handled after failed ones, because some may be moved to the failed chain if they have timed out. */ -for (i = 0; i < 3; i++) +for (int i = 0; i < 3; i++) { address_item *endaddr, *addr; address_item *last_first = NULL; @@ -556,7 +555,6 @@ for (i = 0; i < 3; i++) while ((endaddr = *paddr)) { BOOL timed_out = FALSE; - retry_item *rti; for (addr = endaddr; addr; addr = addr->parent) { @@ -568,7 +566,7 @@ for (i = 0; i < 3; i++) /* Loop for each retry item. */ - for (rti = addr->retries; rti; rti = rti->next) + for (retry_item * rti = addr->retries; rti; rti = rti->next) { uschar *message; int message_length, message_space, failing_interval, next_try; @@ -583,7 +581,7 @@ for (i = 0; i < 3; i++) reached their retry next try time. */ if (!dbm_file) - dbm_file = dbfn_open(US"retry", O_RDWR, &dbblock, TRUE); + dbm_file = dbfn_open(US"retry", O_RDWR, &dbblock, TRUE, TRUE); if (!dbm_file) { @@ -657,19 +655,21 @@ for (i = 0; i < 3; i++) ? US string_printing(rti->message) : US"unknown error"; message_length = Ustrlen(message); - if (message_length > 150) message_length = 150; + if (message_length > EXIM_DB_RLIMIT) message_length = EXIM_DB_RLIMIT; /* Read a retry record from the database or construct a new one. Ignore an old one if it is too old since it was last updated. */ - retry_record = dbfn_read(dbm_file, rti->key); + retry_record = dbfn_read_with_length(dbm_file, rti->key, + &message_space); if ( retry_record && now - retry_record->time_stamp > retry_data_expire) retry_record = NULL; if (!retry_record) { - retry_record = store_get(sizeof(dbdata_retry) + message_length); + retry_record = store_get(sizeof(dbdata_retry) + message_length, + message); message_space = message_length; retry_record->first_failed = now; retry_record->last_try = now; @@ -677,7 +677,7 @@ for (i = 0; i < 3; i++) retry_record->expired = FALSE; retry_record->text[0] = 0; /* just in case */ } - else message_space = Ustrlen(retry_record->text); + else message_space -= sizeof(dbdata_retry); /* Compute how long this destination has been failing */ @@ -808,15 +808,17 @@ for (i = 0; i < 3; i++) if (next_try - now > retry_interval_max) next_try = now + retry_interval_max; - /* If the new message length is greater than the previous one, we - have to copy the record first. */ + /* If the new message length is greater than the previous one, we have + to copy the record first. If we're using an old one, the read used + tainted memory so we're ok to write into it. */ - if (message_length > message_space) - { - dbdata_retry *newr = store_get(sizeof(dbdata_retry) + message_length); - memcpy(newr, retry_record, sizeof(dbdata_retry)); - retry_record = newr; - } + if (message_length > message_space) + { + dbdata_retry * newr = + store_get(sizeof(dbdata_retry) + message_length, message); + memcpy(newr, retry_record, sizeof(dbdata_retry)); + retry_record = newr; + } /* Set up the retry record; message_length may be less than the string length for very long error strings. */ @@ -888,16 +890,17 @@ for (i = 0; i < 3; i++) for (;; addr = addr->next) { setflag(addr, af_retry_timedout); - addr->message = (addr->message == NULL)? US"retry timeout exceeded" : - string_sprintf("%s: retry timeout exceeded", addr->message); - addr->user_message = (addr->user_message == NULL)? - US"retry timeout exceeded" : - string_sprintf("%s: retry timeout exceeded", addr->user_message); + addr->message = addr->message + ? string_sprintf("%s: retry timeout exceeded", addr->message) + : US"retry timeout exceeded"; + addr->user_message = addr->user_message + ? string_sprintf("%s: retry timeout exceeded", addr->user_message) + : US"retry timeout exceeded"; log_write(0, LOG_MAIN, "** %s%s%s%s: retry timeout exceeded", addr->address, - (addr->parent == NULL)? US"" : US" <", - (addr->parent == NULL)? US"" : addr->parent->address, - (addr->parent == NULL)? US"" : US">"); + addr->parent ? US" <" : US"", + addr->parent ? addr->parent->address : US"", + addr->parent ? US">" : US""); if (addr == endaddr) break; }