X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/44b6e099b76f403a55e77650821f8a69e9d2682e..fa1c8faf169384bebaa8d172f491574c45ae2aa4:/src/src/transport.c diff --git a/src/src/transport.c b/src/src/transport.c index d6cedf911..a0f097579 100644 --- a/src/src/transport.c +++ b/src/src/transport.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) The Exim Maintainers 2020 - 2022 */ +/* Copyright (c) The Exim Maintainers 2020 - 2023 */ /* 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 */ @@ -434,10 +434,10 @@ Returns: TRUE on success, FALSE on failure (with errno preserved) */ BOOL -write_chunk(transport_ctx * tctx, uschar *chunk, int len) +write_chunk(transport_ctx * tctx, const uschar * chunk, int len) { -uschar *start = chunk; -uschar *end = chunk + len; +const uschar * start = chunk; +const uschar * end = chunk + len; int mlen = DELIVER_OUT_BUFFER_SIZE - nl_escape_length - 2; /* The assumption is made that the check string will never stretch over move @@ -474,7 +474,7 @@ if (nl_partial_match >= 0) for possible escaping. The code for the non-NL route should be as fast as possible. */ -for (uschar * ptr = start; ptr < end; ptr++) +for (const uschar * ptr = start; ptr < end; ptr++) { int ch, len; @@ -580,7 +580,7 @@ Arguments: Returns: a string */ -uschar * +const uschar * transport_rcpt_address(address_item *addr, BOOL include_affixes) { uschar *at; @@ -704,9 +704,9 @@ Returns: TRUE on success; FALSE on failure. */ BOOL transport_headers_send(transport_ctx * tctx, - BOOL (*sendfn)(transport_ctx * tctx, uschar * s, int len)) + BOOL (*sendfn)(transport_ctx * tctx, const uschar * s, int len)) { -const uschar *list; +const uschar * list; transport_instance * tblock = tctx ? tctx->tblock : NULL; address_item * addr = tctx ? tctx->addr : NULL; @@ -761,15 +761,18 @@ for (header_line * h = header_list; h; h = h->next) if (h->type != htype_old) if (include_header) { + int len; if (tblock && tblock->rewrite_rules) { rmark reset_point = store_mark(); - header_line *hh; + header_line * hh; if ((hh = rewrite_header(h, NULL, NULL, tblock->rewrite_rules, tblock->rewrite_existflags, FALSE))) { - if (!sendfn(tctx, hh->text, hh->slen)) return FALSE; + len = hh->slen; + if (tctx->options & topt_truncate_headers && len > 998) len = 998; + if (!sendfn(tctx, hh->text, len)) return FALSE; store_reset(reset_point); continue; /* With the next header line */ } @@ -777,7 +780,9 @@ for (header_line * h = header_list; h; h = h->next) if (h->type != htype_old) /* Either no rewriting rules, or it didn't get rewritten */ - if (!sendfn(tctx, h->text, h->slen)) return FALSE; + len = h->slen; + if (tctx->options & topt_truncate_headers && len > 998) len = 998; + if (!sendfn(tctx, h->text, len)) return FALSE; } /* Header removed */ @@ -1038,7 +1043,7 @@ if (tctx->options & topt_use_bdat) if (!(tctx->options & topt_no_body)) { if ((fsize = lseek(deliver_datafile, 0, SEEK_END)) < 0) return FALSE; - fsize -= SPOOL_DATA_START_OFFSET; + fsize -= spool_data_start_offset(message_id); if (size_limit > 0 && fsize > size_limit) fsize = size_limit; size = hsize + fsize; @@ -1096,7 +1101,7 @@ if ( f.spool_file_wireformat ) { ssize_t copied = 0; - off_t offset = SPOOL_DATA_START_OFFSET; + off_t offset = spool_data_start_offset(message_id); /* Write out any header data in the buffer */ @@ -1134,7 +1139,7 @@ if (!(tctx->options & topt_no_body)) nl_check_length = abs(nl_check_length); nl_partial_match = 0; - if (lseek(deliver_datafile, SPOOL_DATA_START_OFFSET, SEEK_SET) < 0) + if (lseek(deliver_datafile, spool_data_start_offset(message_id), SEEK_SET) < 0) return FALSE; while ( (len = MIN(DELIVER_IN_BUFFER_SIZE, size)) > 0 && (len = read(deliver_datafile, deliver_in_buffer, len)) > 0) @@ -1492,12 +1497,19 @@ Returns: nothing */ void -transport_update_waiting(host_item *hostlist, uschar *tpname) +transport_update_waiting(host_item * hostlist, uschar * tpname) { const uschar *prevname = US""; open_db dbblock; open_db *dbm_file; +if (!is_new_message_id(message_id)) + { + DEBUG(D_transport) debug_printf("message_id %s is not new format; " + "skipping wait-%s database update\n", tpname); + return; + } + DEBUG(D_transport) debug_printf("updating wait-%s database\n", tpname); /* Open the database for this transport */ @@ -1512,7 +1524,7 @@ that the message id is in each host record. */ for (host_item * host = hostlist; host; host = host->next) { BOOL already = FALSE; - dbdata_wait *host_record; + dbdata_wait * host_record; int host_length; uschar buffer[256]; @@ -1538,8 +1550,27 @@ for (host_item * host = hostlist; host; host = host->next) for (uschar * s = host_record->text; s < host_record->text + host_length; s += MESSAGE_ID_LENGTH) + { + /* If any ID is seen which is not new-format, wipe the record and + any continuations */ + + if (!is_new_message_id(s)) + { + DEBUG(D_hints_lookup) + debug_printf_indent("NOTE: old or corrupt message-id found in wait=%.200s" + " hints DB; deleting records for %s\n", tpname, host->name); + + (void) dbfn_delete(dbm_file, host->name); + for (int i = host_record->sequence - 1; i >= 0; i--) + (void) dbfn_delete(dbm_file, + (sprintf(CS buffer, "%.200s:%d", host->name, i), buffer)); + + host_record->count = host_record->sequence = 0; + break; + } if (Ustrncmp(s, message_id, MESSAGE_ID_LENGTH) == 0) { already = TRUE; break; } + } /* If we haven't found this message in the main record, search any continuation records that exist. */ @@ -1647,13 +1678,14 @@ typedef struct msgq_s } msgq_t; BOOL -transport_check_waiting(const uschar *transport_name, const uschar *hostname, - int local_message_max, uschar *new_message_id, oicf oicf_func, void *oicf_data) +transport_check_waiting(const uschar * transport_name, const uschar * hostname, + int local_message_max, uschar * new_message_id, + oicf oicf_func, void * oicf_data) { -dbdata_wait *host_record; +dbdata_wait * host_record; int host_length; open_db dbblock; -open_db *dbm_file; +open_db * dbm_file; int i; struct stat statbuf; @@ -1730,6 +1762,22 @@ while (1) for (i = 0; i < host_record->count; ++i) { + /* If any ID is seen which is not new-format, wipe the record and + any continuations */ + + if (!is_new_message_id(host_record->text + (i * MESSAGE_ID_LENGTH))) + { + uschar buffer[256]; + DEBUG(D_hints_lookup) + debug_printf_indent("NOTE: old or corrupt message-id found in wait=%.200s" + " hints DB; deleting records for %s\n", transport_name, hostname); + (void) dbfn_delete(dbm_file, hostname); + for (int i = host_record->sequence - 1; i >= 0; i--) + (void) dbfn_delete(dbm_file, + (sprintf(CS buffer, "%.200s:%d", hostname, i), buffer)); + dbfn_close(dbm_file); + goto retfalse; + } msgq[i].bKeep = TRUE; Ustrncpy_nt(msgq[i].message_id, host_record->text + (i * MESSAGE_ID_LENGTH), @@ -1880,8 +1928,8 @@ return FALSE; /* Just the regain-root-privilege exec portion */ void -transport_do_pass_socket(const uschar *transport_name, const uschar *hostname, - const uschar *hostaddress, uschar *id, int socket_fd) +transport_do_pass_socket(const uschar * transport_name, const uschar * hostname, + const uschar * hostaddress, uschar * id, int socket_fd) { int i = 13; const uschar **argv; @@ -2085,18 +2133,18 @@ return FALSE; /* This function is called when a command line is to be parsed and executed directly, without the use of /bin/sh. It is called by the pipe transport, -the queryprogram router, and also from the main delivery code when setting up a +the queryprogram router, for any ${run } expansion, +and also from the main delivery code when setting up a transport filter process. The code for ETRN also makes use of this; in that case, no addresses are passed. Arguments: argvptr pointer to anchor for argv vector cmd points to the command string (modified IN PLACE) - expand_arguments true if expansion is to occur + flags bits for expand-args, allow taint, allow $recipients expand_failed error value to set if expansion fails; not relevant if addr == NULL addr chain of addresses, or NULL - allow_tainted_args as it says; used for ${run} etext text for use in error messages errptr where to put error message if addr is NULL; otherwise it is put in the first address @@ -2107,8 +2155,8 @@ Returns: TRUE if all went well; otherwise an error will be BOOL transport_set_up_command(const uschar *** argvptr, const uschar * cmd, - BOOL expand_arguments, int expand_failed, address_item * addr, - BOOL allow_tainted_args, const uschar * etext, uschar ** errptr) + unsigned flags, int expand_failed, address_item * addr, + const uschar * etext, uschar ** errptr) { const uschar ** argv, * s; int address_count = 0, argcount = 0, max_args; @@ -2183,10 +2231,10 @@ DEBUG(D_transport) debug_printf(" argv[%d] = '%s'\n", i, string_printing(argv[i])); } -if (expand_arguments) +if (flags & TSUC_EXPAND_ARGS) { - BOOL allow_dollar_recipients = addr && addr->parent - && Ustrcmp(addr->parent->address, "system-filter") == 0; + BOOL allow_dollar_recipients = (flags & TSUC_ALLOW_RECIPIENTS) + || (addr && addr->parent && Ustrcmp(addr->parent->address, "system-filter") == 0); /*XXX could we check this at caller? */ for (int i = 0; argv[i]; i++) { @@ -2254,7 +2302,7 @@ if (expand_arguments) address_pipe_argv = store_get((address_pipe_max_args+1)*sizeof(uschar *), GET_UNTAINTED); /* +1 because addr->local_part[0] == '|' since af_force_command is set */ - s = expand_string(addr->local_part + 1); + s = expand_cstring(addr->local_part + 1); if (!s || !*s) { @@ -2373,7 +2421,7 @@ if (expand_arguments) debug_printf("SPECIFIC TESTSUITE EXEMPTION: tainted arg '%s'\n", expanded_arg); } - else if ( !allow_tainted_args + else if ( !(flags & TSUC_ALLOW_TAINTED_ARGS) && arg_is_tainted(expanded_arg, i, addr, etext, errptr)) return FALSE; argv[i] = expanded_arg;