X-Git-Url: https://vcs.fsf.org/?p=exim.git;a=blobdiff_plain;f=src%2Fsrc%2Ftransport.c;h=14bd91cdb3e2160c0ee47f65a2c2a8e302e07cc0;hp=7806e39576a1ecb6b82d13e88e6edafe676e0af1;hb=73a10da9bbc6aadd03c3aff7a12307252e617a71;hpb=328c5688dbe0f4c14418f22350ccd99b3fe8ac71 diff --git a/src/src/transport.c b/src/src/transport.c index 7806e3957..14bd91cdb 100644 --- a/src/src/transport.c +++ b/src/src/transport.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2016 */ +/* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ /* General functions concerned with transportation, and generic options for all @@ -11,35 +11,13 @@ transports. */ #include "exim.h" -#ifdef HAVE_LINUX_SENDFILE -# include -#endif - -/* Structure for keeping list of addresses that have been added to -Envelope-To:, in order to avoid duplication. */ - -struct aci { - struct aci *next; - address_item *ptr; - }; - - -/* Static data for write_chunk() */ - -static uschar *chunk_ptr; /* chunk pointer */ -static uschar *nl_check; /* string to look for at line start */ -static int nl_check_length; /* length of same */ -static uschar *nl_escape; /* string to insert */ -static int nl_escape_length; /* length of same */ -static int nl_partial_match; /* length matched at chunk end */ - - /* Generic options for transports, all of which live inside transport_instance data blocks and which therefore have the opt_public flag set. Note that there are other options living inside this structure which can be set only from certain transports. */ optionlist optionlist_transports[] = { + /* name type value */ { "*expand_group", opt_stringptr|opt_hidden|opt_public, (void *)offsetof(transport_instance, expand_gid) }, { "*expand_user", opt_stringptr|opt_hidden|opt_public, @@ -110,21 +88,46 @@ optionlist optionlist_transports[] = { int optionlist_transports_size = nelem(optionlist_transports); +#ifdef MACRO_PREDEF + +# include "macro_predef.h" void -readconf_options_transports(void) +options_transports(void) { -struct transport_info * ti; +uschar buf[64]; -readconf_options_from_list(optionlist_transports, nelem(optionlist_transports), US"TRANSPORTS", NULL); +options_from_list(optionlist_transports, nelem(optionlist_transports), US"TRANSPORTS", NULL); -for (ti = transports_available; ti->driver_name[0]; ti++) +for (transport_info * ti = transports_available; ti->driver_name[0]; ti++) { - macro_create(string_sprintf("_DRIVER_TRANSPORT_%T", ti->driver_name), US"y", FALSE, TRUE); - readconf_options_from_list(ti->options, (unsigned)*ti->options_count, US"TRANSPORT", ti->driver_name); + spf(buf, sizeof(buf), US"_DRIVER_TRANSPORT_%T", ti->driver_name); + builtin_macro_create(buf); + options_from_list(ti->options, (unsigned)*ti->options_count, US"TRANSPORT", ti->driver_name); } } +#else /*!MACRO_PREDEF*/ + +/* Structure for keeping list of addresses that have been added to +Envelope-To:, in order to avoid duplication. */ + +struct aci { + struct aci *next; + address_item *ptr; + }; + + +/* Static data for write_chunk() */ + +static uschar *chunk_ptr; /* chunk pointer */ +static uschar *nl_check; /* string to look for at line start */ +static int nl_check_length; /* length of same */ +static uschar *nl_escape; /* string to insert */ +static int nl_escape_length; /* length of same */ +static int nl_partial_match; /* length matched at chunk end */ + + /************************************************* * Initialize transport list * *************************************************/ @@ -138,8 +141,6 @@ the work. */ void transport_init(void) { -transport_instance *t; - readconf_driver_init(US"transport", (driver_instance **)(&transports), /* chain anchor */ (driver_info *)transports_available, /* available drivers */ @@ -152,7 +153,7 @@ readconf_driver_init(US"transport", /* Now scan the configured transports and check inconsistencies. A shadow transport is permitted only for local transports. */ -for (t = transports; t; t = t->next) +for (transport_instance * t = transports; t; t = t->next) { if (!t->info->local && t->shadow) log_write(0, LOG_PANIC_DIE|LOG_CONFIG, @@ -171,6 +172,20 @@ for (t = transports; t; t = t->next) * Write block of data * *************************************************/ +static int +tpt_write(int fd, uschar * block, int len, BOOL more, int options) +{ +return +#ifndef DISABLE_TLS + tls_out.active.sock == fd + ? tls_write(tls_out.active.tls_ctx, block, len, more) : +#endif +#ifdef MSG_MORE + more && !(options & topt_not_socket) ? send(fd, block, len, MSG_MORE) : +#endif + write(fd, block, len); +} + /* Subroutine called by write_chunk() and at the end of the message actually to write a data block. Also called directly by some transports to write additional data to the file descriptor (e.g. prefix, suffix). @@ -207,67 +222,65 @@ Arguments: tctx transport context: file descriptor or string to write to block block of bytes to write len number of bytes to write + more further data expected soon Returns: TRUE on success, FALSE on failure (with errno preserved); transport_count is incremented by the number of bytes written */ static BOOL -transport_write_block_fd(transport_ctx * tctx, uschar *block, int len, BOOL more) +transport_write_block_fd(transport_ctx * tctx, uschar * block, int len, BOOL more) { -int i, rc, save_errno; +int rc, save_errno; int local_timeout = transport_write_timeout; +int connretry = 1; int fd = tctx->u.fd; /* This loop is for handling incomplete writes and other retries. In most normal cases, it is only ever executed once. */ -for (i = 0; i < 100; i++) +for (int i = 0; i < 100; i++) { DEBUG(D_transport) debug_printf("writing data block fd=%d size=%d timeout=%d%s\n", fd, len, local_timeout, more ? " (more expected)" : ""); - /* This code makes use of alarm() in order to implement the timeout. This - isn't a very tidy way of doing things. Using non-blocking I/O with select() - provides a neater approach. However, I don't know how to do this when TLS is - in use. */ - - if (transport_write_timeout <= 0) /* No timeout wanted */ - { - rc = -#ifdef SUPPORT_TLS - (tls_out.active == fd) ? tls_write(FALSE, block, len) : -#endif -#ifdef MSG_MORE - more ? send(fd, block, len, MSG_MORE) : -#endif - write(fd, block, len); - save_errno = errno; - } - - /* Timeout wanted. */ + /* When doing TCP Fast Open we may get this far before the 3-way handshake + is complete, and write returns ENOTCONN. Detect that, wait for the socket + to become writable, and retry once only. */ - else + for(;;) { - alarm(local_timeout); + fd_set fds; + /* This code makes use of alarm() in order to implement the timeout. This + isn't a very tidy way of doing things. Using non-blocking I/O with select() + provides a neater approach. However, I don't know how to do this when TLS is + in use. */ - rc = -#ifdef SUPPORT_TLS - (tls_out.active == fd) ? tls_write(FALSE, block, len) : -#endif -#ifdef MSG_MORE - more ? send(fd, block, len, MSG_MORE) : -#endif - write(fd, block, len); - - save_errno = errno; - local_timeout = alarm(0); - if (sigalrm_seen) + if (transport_write_timeout <= 0) /* No timeout wanted */ { - errno = ETIMEDOUT; - return FALSE; + rc = tpt_write(fd, block, len, more, tctx->options); + save_errno = errno; } + else /* Timeout wanted. */ + { + ALARM(local_timeout); + rc = tpt_write(fd, block, len, more, tctx->options); + save_errno = errno; + local_timeout = ALARM_CLR(0); + if (sigalrm_seen) + { + errno = ETIMEDOUT; + return FALSE; + } + } + + if (rc >= 0 || errno != ENOTCONN || connretry <= 0) + break; + + FD_ZERO(&fds); FD_SET(fd, &fds); + select(fd+1, NULL, &fds, NULL, NULL); /* could set timout? */ + connretry--; } /* Hopefully, the most common case is success, so test that first. */ @@ -341,12 +354,9 @@ if (!(tctx->options & topt_output_string)) /* Write to expanding-string. NOTE: not NUL-terminated */ if (!tctx->u.msg) - { - tctx->u.msg = store_get(tctx->msg_size = 1024); - tctx->msg_ptr = 0; - } + tctx->u.msg = string_get(1024); -tctx->u.msg = string_catn(tctx->u.msg, &tctx->msg_size, &tctx->msg_ptr, block, len); +tctx->u.msg = string_catn(tctx->u.msg, block, len); return TRUE; } @@ -370,14 +380,19 @@ Returns: the yield of transport_write_block() BOOL transport_write_string(int fd, const char *format, ...) { -transport_ctx tctx = {0}; +transport_ctx tctx = {{0}}; +gstring gs = { .size = big_buffer_size, .ptr = 0, .s = big_buffer }; va_list ap; + +/* Use taint-unchecked routines for writing into big_buffer, trusting +that the result will never be expanded. */ + va_start(ap, format); -if (!string_vformat(big_buffer, big_buffer_size, format, ap)) +if (!string_vformat(&gs, SVFMT_TAINT_NOCHK, format, ap)) log_write(0, LOG_MAIN|LOG_PANIC_DIE, "overlong formatted string in transport"); va_end(ap); tctx.u.fd = fd; -return transport_write_block(&tctx, big_buffer, Ustrlen(big_buffer), FALSE); +return transport_write_block(&tctx, gs.s, gs.ptr, FALSE); } @@ -421,7 +436,6 @@ write_chunk(transport_ctx * tctx, uschar *chunk, int len) { uschar *start = chunk; uschar *end = chunk + len; -uschar *ptr; int mlen = DELIVER_OUT_BUFFER_SIZE - nl_escape_length - 2; /* The assumption is made that the check string will never stretch over move @@ -458,7 +472,7 @@ if (nl_partial_match >= 0) for possible escaping. The code for the non-NL route should be as fast as possible. */ -for (ptr = start; ptr < end; ptr++) +for (uschar * ptr = start; ptr < end; ptr++) { int ch, len; @@ -473,7 +487,7 @@ for (ptr = start; ptr < end; ptr++) /* If CHUNKING, prefix with BDAT (size) NON-LAST. Also, reap responses from previous SMTP commands. */ - if (tctx && tctx->options & topt_use_bdat && tctx->chunk_cb) + if (tctx->options & topt_use_bdat && tctx->chunk_cb) { if ( tctx->chunk_cb(tctx, (unsigned)len, 0) != OK || !transport_write_block(tctx, deliver_out_buffer, len, FALSE) @@ -490,8 +504,8 @@ for (ptr = start; ptr < end; ptr++) /* Remove CR before NL if required */ if ( *ptr == '\r' && ptr[1] == '\n' - && (!tctx || !(tctx->options & topt_use_crlf)) - && spool_file_wireformat + && !(tctx->options & topt_use_crlf) + && f.spool_file_wireformat ) ptr++; @@ -501,7 +515,7 @@ for (ptr = start; ptr < end; ptr++) /* Insert CR before NL if required */ - if (tctx && tctx->options & topt_use_crlf && !spool_file_wireformat) + if (tctx->options & topt_use_crlf && !f.spool_file_wireformat) *chunk_ptr++ = '\r'; *chunk_ptr++ = '\n'; transport_newlines++; @@ -586,7 +600,7 @@ at = Ustrrchr(addr->address, '@'); plen = (addr->prefix == NULL)? 0 : Ustrlen(addr->prefix); slen = Ustrlen(addr->suffix); -return string_sprintf("%.*s@%s", (at - addr->address - plen - slen), +return string_sprintf("%.*s@%s", (int)(at - addr->address - plen - slen), addr->address + plen, at + 1); } @@ -636,7 +650,7 @@ so that we don't handle it again. */ for (ppp = *pdlist; ppp; ppp = ppp->next) if (p == ppp->ptr) return TRUE; -ppp = store_get(sizeof(struct aci)); +ppp = store_get(sizeof(struct aci), FALSE); ppp->next = *pdlist; *pdlist = ppp; ppp->ptr = p; @@ -660,7 +674,7 @@ if (ppp) return TRUE; /* Remember what we have output, and output it. */ -ppp = store_get(sizeof(struct aci)); +ppp = store_get(sizeof(struct aci), FALSE); ppp->next = *pplist; *pplist = ppp; ppp->ptr = pp; @@ -690,7 +704,6 @@ BOOL transport_headers_send(transport_ctx * tctx, BOOL (*sendfn)(transport_ctx * tctx, uschar * s, int len)) { -header_line *h; const uschar *list; transport_instance * tblock = tctx ? tctx->tblock : NULL; address_item * addr = tctx ? tctx->addr : NULL; @@ -702,13 +715,12 @@ match any entries therein. It is a colon-sep list; expand the items separately and squash any empty ones. Then check addr->prop.remove_headers too, provided that addr is not NULL. */ -for (h = header_list; h; h = h->next) if (h->type != htype_old) +for (header_line * h = header_list; h; h = h->next) if (h->type != htype_old) { - int i; BOOL include_header = TRUE; list = tblock ? tblock->remove_headers : NULL; - for (i = 0; i < 2; i++) /* For remove_headers && addr->prop.remove_headers */ + for (int i = 0; i < 2; i++) /* For remove_headers && addr->prop.remove_headers */ { if (list) { @@ -719,7 +731,7 @@ for (h = header_list; h; h = h->next) if (h->type != htype_old) int len; if (i == 0) - if (!(s = expand_string(s)) && !expand_string_forcedfail) + if (!(s = expand_string(s)) && !f.expand_string_forcedfail) { errno = ERRNO_CHHEADER_FAIL; return FALSE; @@ -742,7 +754,7 @@ for (h = header_list; h; h = h->next) if (h->type != htype_old) { if (tblock && tblock->rewrite_rules) { - void *reset_point = store_get(0); + rmark reset_point = store_mark(); header_line *hh; if ((hh = rewrite_header(h, NULL, NULL, tblock->rewrite_rules, @@ -778,10 +790,9 @@ Headers added to an address by a router are guaranteed to end with a newline. if (addr) { - int i; header_line *hprev = addr->prop.extra_headers; - header_line *hnext; - for (i = 0; i < 2; i++) + header_line *hnext, * h; + for (int i = 0; i < 2; i++) for (h = hprev, hprev = NULL; h; h = hnext) { hnext = h->next; @@ -825,7 +836,7 @@ if (tblock && (list = CUS tblock->add_headers)) } } } - else if (!expand_string_forcedfail) + else if (!f.expand_string_forcedfail) { errno = ERRNO_CHHEADER_FAIL; return FALSE; } } @@ -898,7 +909,7 @@ Returns: TRUE on success; FALSE (with errno) on failure. is incremented by the number of bytes written. */ -BOOL +static BOOL internal_transport_write_message(transport_ctx * tctx, int size_limit) { int len, size = 0; @@ -930,8 +941,8 @@ so temporarily hide the global that adjusts for its format. */ if (!(tctx->options & topt_no_headers)) { - BOOL save_wireformat = spool_file_wireformat; - spool_file_wireformat = FALSE; + BOOL save_wireformat = f.spool_file_wireformat; + f.spool_file_wireformat = FALSE; /* Add return-path: if requested. */ @@ -948,10 +959,9 @@ if (!(tctx->options & topt_no_headers)) if (tctx->options & topt_add_envelope_to) { BOOL first = TRUE; - address_item *p; struct aci *plist = NULL; struct aci *dlist = NULL; - void *reset_point = store_get(0); + rmark reset_point = store_mark(); if (!write_chunk(tctx, US"Envelope-to: ", 13)) goto bad; @@ -959,8 +969,9 @@ if (!(tctx->options & topt_no_headers)) anchors for lists of addresses already handled; they have to be defined at this level because write_env_to() calls itself recursively. */ - for (p = tctx->addr; p; p = p->next) - if (!write_env_to(p, &plist, &dlist, &first, tctx)) goto bad; + for (address_item * p = tctx->addr; p; p = p->next) + if (!write_env_to(p, &plist, &dlist, &first, tctx)) + goto bad; /* Add a final newline and reset the store used for tracking duplicates */ @@ -972,9 +983,11 @@ if (!(tctx->options & topt_no_headers)) if (tctx->options & topt_add_delivery_date) { - uschar buffer[100]; - int n = sprintf(CS buffer, "Delivery-date: %s\n", tod_stamp(tod_full)); - if (!write_chunk(tctx, buffer, n)) goto bad; + uschar * s = tod_stamp(tod_full); + + if ( !write_chunk(tctx, US"Delivery-date: ", 15) + || !write_chunk(tctx, s, Ustrlen(s)) + || !write_chunk(tctx, US"\n", 1)) goto bad; } /* Then the message's headers. Don't write any that are flagged as "old"; @@ -986,11 +999,11 @@ if (!(tctx->options & topt_no_headers)) if (!transport_headers_send(tctx, &write_chunk)) { bad: - spool_file_wireformat = save_wireformat; + f.spool_file_wireformat = save_wireformat; return FALSE; } - spool_file_wireformat = save_wireformat; + f.spool_file_wireformat = save_wireformat; } /* When doing RFC3030 CHUNKING output, work out how much data would be in a @@ -1019,7 +1032,7 @@ if (tctx->options & topt_use_bdat) if (size_limit > 0 && fsize > size_limit) fsize = size_limit; size = hsize + fsize; - if (tctx->options & topt_use_crlf && !spool_file_wireformat) + if (tctx->options & topt_use_crlf && !f.spool_file_wireformat) size += body_linecount; /* account for CRLF-expansion */ /* With topt_use_bdat we never do dot-stuffing; no need to @@ -1065,11 +1078,11 @@ then we can just dump it using sendfile. This should get used for CHUNKING output and also for writing the -K file for dkim signing, when we had CHUNKING input. */ -#ifdef HAVE_LINUX_SENDFILE -if ( spool_file_wireformat +#ifdef OS_SENDFILE +if ( f.spool_file_wireformat && !(tctx->options & (topt_no_body | topt_end_dot)) && !nl_check_length - && tls_out.active != tctx->u.fd + && tls_out.active.sock != tctx->u.fd ) { ssize_t copied = 0; @@ -1088,7 +1101,7 @@ if ( spool_file_wireformat while(size > 0) { - if ((copied = sendfile(tctx->u.fd, deliver_datafile, &offset, size)) <= 0) break; + if ((copied = os_sendfile(tctx->u.fd, deliver_datafile, &offset, size)) <= 0) break; size -= copied; } return copied >= 0; @@ -1100,20 +1113,20 @@ DEBUG(D_transport) debug_printf("cannot use sendfile for body: no support\n"); DEBUG(D_transport) if (!(tctx->options & topt_no_body)) debug_printf("cannot use sendfile for body: %s\n", - !spool_file_wireformat ? "spoolfile not wireformat" + !f.spool_file_wireformat ? "spoolfile not wireformat" : tctx->options & topt_end_dot ? "terminating dot wanted" : nl_check_length ? "dot- or From-stuffing wanted" : "TLS output wanted"); if (!(tctx->options & topt_no_body)) { - int size = size_limit; + unsigned long size = size_limit > 0 ? size_limit : ULONG_MAX; nl_check_length = abs(nl_check_length); nl_partial_match = 0; if (lseek(deliver_datafile, SPOOL_DATA_START_OFFSET, SEEK_SET) < 0) return FALSE; - while ( (len = MAX(DELIVER_IN_BUFFER_SIZE, size)) > 0 + while ( (len = MIN(DELIVER_IN_BUFFER_SIZE, size)) > 0 && (len = read(deliver_datafile, deliver_in_buffer, len)) > 0) { if (!write_chunk(tctx, deliver_in_buffer, len)) @@ -1126,9 +1139,10 @@ if (!(tctx->options & topt_no_body)) if (len != 0) return FALSE; } -/* Finished with the check string */ +/* Finished with the check string, and spool-format consideration */ nl_check_length = nl_escape_length = 0; +f.spool_file_wireformat = FALSE; /* If requested, add a terminating "." line (SMTP output). */ @@ -1165,13 +1179,12 @@ BOOL transport_write_message(transport_ctx * tctx, int size_limit) { BOOL last_filter_was_NL = TRUE; -BOOL save_spool_file_wireformat = spool_file_wireformat; +BOOL save_spool_file_wireformat = f.spool_file_wireformat; int rc, len, yield, fd_read, fd_write, save_errno; int pfd[2] = {-1, -1}; pid_t filter_pid, write_pid; -static transport_ctx dummy_tctx = {0}; -transport_filter_timed_out = FALSE; +f.transport_filter_timed_out = FALSE; /* If there is no filter command set up, call the internal function that does the actual work, passing it the incoming fd, and return its result. */ @@ -1247,9 +1260,11 @@ if ((write_pid = fork()) == 0) != sizeof(int) || write(pfd[pipe_write], (void *)&tctx->addr->more_errno, sizeof(int)) != sizeof(int) + || write(pfd[pipe_write], (void *)&tctx->addr->delivery_usec, sizeof(int)) + != sizeof(int) ) rc = FALSE; /* compiler quietening */ - _exit(0); + exim_underbar_exit(0); } save_errno = errno; @@ -1269,7 +1284,7 @@ if (write_pid < 0) /* When testing, let the subprocess get going */ -if (running_in_test_harness) millisleep(250); +testharness_pause_ms(250); DEBUG(D_transport) debug_printf("process %d writing to transport filter\n", (int)write_pid); @@ -1286,19 +1301,19 @@ no data is returned, that counts as "ended with NL" (default setting of the variable is TRUE). The output should always be unix-format as we converted any wireformat source on writing input to the filter. */ -spool_file_wireformat = FALSE; +f.spool_file_wireformat = FALSE; chunk_ptr = deliver_out_buffer; for (;;) { sigalrm_seen = FALSE; - alarm(transport_filter_timeout); + ALARM(transport_filter_timeout); len = read(fd_read, deliver_in_buffer, DELIVER_IN_BUFFER_SIZE); - alarm(0); + ALARM_CLR(0); if (sigalrm_seen) { errno = ETIMEDOUT; - transport_filter_timed_out = TRUE; + f.transport_filter_timed_out = TRUE; goto TIDY_UP; } @@ -1326,7 +1341,7 @@ there has been an error, kill the processes before waiting for them, just to be sure. Also apply a paranoia timeout. */ TIDY_UP: -spool_file_wireformat = save_spool_file_wireformat; +f.spool_file_wireformat = save_spool_file_wireformat; save_errno = errno; (void)close(fd_read); @@ -1371,7 +1386,9 @@ if (write_pid > 0) else if (!ok) { int dummy = read(pfd[pipe_read], (void *)&save_errno, sizeof(int)); - dummy = read(pfd[pipe_read], (void *)&(tctx->addr->more_errno), sizeof(int)); + dummy = read(pfd[pipe_read], (void *)&tctx->addr->more_errno, sizeof(int)); + dummy = read(pfd[pipe_read], (void *)&tctx->addr->delivery_usec, sizeof(int)); + dummy = dummy; /* compiler quietening */ yield = FALSE; } } @@ -1392,6 +1409,7 @@ filter was not NL, insert a NL to make the SMTP protocol work. */ if (yield) { nl_check_length = nl_escape_length = 0; + f.spool_file_wireformat = FALSE; if ( tctx->options & topt_end_dot && ( last_filter_was_NL ? !write_chunk(tctx, US".\n", 2) @@ -1461,7 +1479,6 @@ void transport_update_waiting(host_item *hostlist, uschar *tpname) { const uschar *prevname = US""; -host_item *host; open_db dbblock; open_db *dbm_file; @@ -1470,18 +1487,17 @@ DEBUG(D_transport) debug_printf("updating wait-%s database\n", tpname); /* Open the database for this transport */ if (!(dbm_file = dbfn_open(string_sprintf("wait-%.200s", tpname), - O_RDWR, &dbblock, TRUE))) + O_RDWR, &dbblock, TRUE, TRUE))) return; /* Scan the list of hosts for which this message is waiting, and ensure that the message id is in each host record. */ -for (host = hostlist; host; host = host->next) +for (host_item * host = hostlist; host; host = host->next) { BOOL already = FALSE; dbdata_wait *host_record; - uschar *s; - int i, host_length; + int host_length; uschar buffer[256]; /* Skip if this is the same host as we just processed; otherwise remember @@ -1494,7 +1510,7 @@ for (host = hostlist; host; host = host->next) if (!(host_record = dbfn_read(dbm_file, host->name))) { - host_record = store_get(sizeof(dbdata_wait) + MESSAGE_ID_LENGTH); + host_record = store_get(sizeof(dbdata_wait) + MESSAGE_ID_LENGTH, FALSE); host_record->count = host_record->sequence = 0; } @@ -1504,7 +1520,7 @@ for (host = hostlist; host; host = host->next) /* Search the record to see if the current message is already in it. */ - for (s = host_record->text; s < host_record->text + host_length; + for (uschar * s = host_record->text; s < host_record->text + host_length; s += MESSAGE_ID_LENGTH) if (Ustrncmp(s, message_id, MESSAGE_ID_LENGTH) == 0) { already = TRUE; break; } @@ -1512,14 +1528,14 @@ for (host = hostlist; host; host = host->next) /* If we haven't found this message in the main record, search any continuation records that exist. */ - for (i = host_record->sequence - 1; i >= 0 && !already; i--) + for (int i = host_record->sequence - 1; i >= 0 && !already; i--) { dbdata_wait *cont; sprintf(CS buffer, "%.200s:%d", host->name, i); if ((cont = dbfn_read(dbm_file, buffer))) { int clen = cont->count * MESSAGE_ID_LENGTH; - for (s = cont->text; s < cont->text + clen; s += MESSAGE_ID_LENGTH) + for (uschar * s = cont->text; s < cont->text + clen; s += MESSAGE_ID_LENGTH) if (Ustrncmp(s, message_id, MESSAGE_ID_LENGTH) == 0) { already = TRUE; break; } } @@ -1553,7 +1569,7 @@ for (host = hostlist; host; host = host->next) else { dbdata_wait *newr = - store_get(sizeof(dbdata_wait) + host_length + MESSAGE_ID_LENGTH); + store_get(sizeof(dbdata_wait) + host_length + MESSAGE_ID_LENGTH, FALSE); memcpy(newr, host_record, sizeof(dbdata_wait) + host_length); host_record = newr; } @@ -1644,7 +1660,7 @@ if (local_message_max > 0 && continue_sequence >= local_message_max) /* Open the waiting information database. */ if (!(dbm_file = dbfn_open(string_sprintf("wait-%.200s", transport_name), - O_RDWR, &dbblock, TRUE))) + O_RDWR, &dbblock, TRUE, TRUE))) return FALSE; /* See if there is a record for this host; if not, there's nothing to do. */ @@ -1688,7 +1704,7 @@ while (1) /* create an array to read entire message queue into memory for processing */ - msgq = store_malloc(sizeof(msgq_t) * host_record->count); + msgq = store_get(sizeof(msgq_t) * host_record->count, FALSE); msgq_count = host_record->count; msgq_actual = msgq_count; @@ -1696,7 +1712,7 @@ while (1) { msgq[i].bKeep = TRUE; - Ustrncpy(msgq[i].message_id, host_record->text + (i * MESSAGE_ID_LENGTH), + Ustrncpy_nt(msgq[i].message_id, host_record->text + (i * MESSAGE_ID_LENGTH), MESSAGE_ID_LENGTH); msgq[i].message_id[MESSAGE_ID_LENGTH] = 0; } @@ -1715,16 +1731,14 @@ while (1) for (i = msgq_count - 1; i >= 0; --i) if (msgq[i].bKeep) { uschar subdir[2]; + uschar * mid = msgq[i].message_id; - subdir[0] = split_spool_directory ? msgq[i].message_id[5] : 0; - subdir[1] = 0; - - if (Ustat(spool_fname(US"input", subdir, msgq[i].message_id, US"-D"), - &statbuf) != 0) + set_subdir_str(subdir, mid, 0); + if (Ustat(spool_fname(US"input", subdir, mid, US"-D"), &statbuf) != 0) msgq[i].bKeep = FALSE; - else if (!oicf_func || oicf_func(msgq[i].message_id, oicf_data)) + else if (!oicf_func || oicf_func(mid, oicf_data)) { - Ustrcpy(new_message_id, msgq[i].message_id); + Ustrcpy_nt(new_message_id, mid); msgq[i].bKeep = FALSE; bFound = TRUE; break; @@ -1765,13 +1779,12 @@ while (1) while (host_length <= 0) { - int i; dbdata_wait * newr = NULL; uschar buffer[256]; /* Search for a continuation */ - for (i = host_record->sequence - 1; i >= 0 && !newr; i--) + for (int i = host_record->sequence - 1; i >= 0 && !newr; i--) { sprintf(CS buffer, "%.200s:%d", hostname, i); newr = dbfn_read(dbm_file, buffer); @@ -1795,10 +1808,7 @@ while (1) } if (bFound) /* Usual exit from main loop */ - { - store_free (msgq); break; - } /* If host_length <= 0 we have emptied a record and not found a good message, and there are no continuation records. Otherwise there is a continuation @@ -1821,8 +1831,6 @@ while (1) dbfn_close(dbm_file); return FALSE; } - - store_free(msgq); } /* we need to process a continuation record */ /* Control gets here when an existing message has been encountered; its @@ -1859,19 +1867,19 @@ but we have a number of extras that may be added. */ argv = CUSS child_exec_exim(CEE_RETURN_ARGV, TRUE, &i, FALSE, 0); -if (smtp_authenticated) argv[i++] = US"-MCA"; -if (smtp_peer_options & PEER_OFFERED_CHUNKING) argv[i++] = US"-MCK"; -if (smtp_peer_options & PEER_OFFERED_DSN) argv[i++] = US"-MCD"; -if (smtp_peer_options & PEER_OFFERED_PIPE) argv[i++] = US"-MCP"; -if (smtp_peer_options & PEER_OFFERED_SIZE) argv[i++] = US"-MCS"; -#ifdef SUPPORT_TLS -if (smtp_peer_options & PEER_OFFERED_TLS) - if (tls_out.active >= 0 || continue_proxy_cipher) +if (f.smtp_authenticated) argv[i++] = US"-MCA"; +if (smtp_peer_options & OPTION_CHUNKING) argv[i++] = US"-MCK"; +if (smtp_peer_options & OPTION_DSN) argv[i++] = US"-MCD"; +if (smtp_peer_options & OPTION_PIPE) argv[i++] = US"-MCP"; +if (smtp_peer_options & OPTION_SIZE) argv[i++] = US"-MCS"; +#ifndef DISABLE_TLS +if (smtp_peer_options & OPTION_TLS) + if (tls_out.active.sock >= 0 || continue_proxy_cipher) { argv[i++] = US"-MCt"; argv[i++] = sending_ip_address; argv[i++] = string_sprintf("%d", sending_port); - argv[i++] = tls_out.active >= 0 ? tls_out.cipher : continue_proxy_cipher; + argv[i++] = tls_out.active.sock >= 0 ? tls_out.cipher : continue_proxy_cipher; } else argv[i++] = US"-MCT"; @@ -1940,8 +1948,12 @@ if ((pid = fork()) == 0) write the log, etc., so that the output is always in the same order for automatic comparison. */ - if ((pid = fork()) != 0) _exit(EXIT_SUCCESS); - if (running_in_test_harness) sleep(1); + if ((pid = fork()) != 0) + { + DEBUG(D_transport) debug_printf("transport_pass_socket succeeded (final-pid %d)\n", pid); + _exit(EXIT_SUCCESS); + } + testharness_pause_ms(1000); transport_do_pass_socket(transport_name, hostname, hostaddress, id, socket_fd); @@ -1955,7 +1967,7 @@ if (pid > 0) { int rc; while ((rc = wait(&status)) != pid && (rc >= 0 || errno != ECHILD)); - DEBUG(D_transport) debug_printf("transport_pass_socket succeeded\n"); + DEBUG(D_transport) debug_printf("transport_pass_socket succeeded (inter-pid %d)\n", pid); return TRUE; } else @@ -1998,21 +2010,20 @@ transport_set_up_command(const uschar ***argvptr, uschar *cmd, BOOL expand_arguments, int expand_failed, address_item *addr, uschar *etext, uschar **errptr) { -address_item *ad; const uschar **argv; uschar *s, *ss; int address_count = 0; int argcount = 0; -int i, max_args; +int max_args; /* Get store in which to build an argument list. Count the number of addresses supplied, and allow for that many arguments, plus an additional 60, which should be enough for anybody. Multiple addresses happen only when the local delivery batch option is set. */ -for (ad = addr; ad != NULL; ad = ad->next) address_count++; +for (address_item * ad = addr; ad; ad = ad->next) address_count++; max_args = address_count + 60; -*argvptr = argv = store_get((max_args+1)*sizeof(uschar *)); +*argvptr = argv = store_get((max_args+1)*sizeof(uschar *), FALSE); /* Split the command up into arguments terminated by white space. Lose trailing space at the start and end. Double-quoted arguments can contain \\ and @@ -2022,22 +2033,23 @@ arguments are verbatim. Copy each argument into a new string. */ s = cmd; while (isspace(*s)) s++; -while (*s != 0 && argcount < max_args) +for (; *s != 0 && argcount < max_args; argcount++) { if (*s == '\'') { ss = s + 1; while (*ss != 0 && *ss != '\'') ss++; - argv[argcount++] = ss = store_get(ss - s++); + argv[argcount] = ss = store_get(ss - s++, is_tainted(cmd)); while (*s != 0 && *s != '\'') *ss++ = *s++; if (*s != 0) s++; *ss++ = 0; } - else argv[argcount++] = string_copy(string_dequote(CUSS &s)); + else + argv[argcount] = string_dequote(CUSS &s); while (isspace(*s)) s++; } -argv[argcount] = (uschar *)0; +argv[argcount] = US 0; /* If *s != 0 we have run out of argument slots. */ @@ -2073,8 +2085,8 @@ $recipients. */ DEBUG(D_transport) { debug_printf("direct command:\n"); - for (i = 0; argv[i] != (uschar *)0; i++) - debug_printf(" argv[%d] = %s\n", i, string_printing(argv[i])); + for (int i = 0; argv[i]; i++) + debug_printf(" argv[%d] = '%s'\n", i, string_printing(argv[i])); } if (expand_arguments) @@ -2083,7 +2095,7 @@ if (expand_arguments) addr->parent != NULL && Ustrcmp(addr->parent->address, "system-filter") == 0; - for (i = 0; argv[i] != (uschar *)0; i++) + for (int i = 0; argv[i] != US 0; i++) { /* Handle special fudge for passing an address list */ @@ -2107,10 +2119,11 @@ if (expand_arguments) memmove(argv + i + 1 + additional, argv + i + 1, (argcount - i)*sizeof(uschar *)); - for (ad = addr; ad != NULL; ad = ad->next) { - argv[i++] = ad->address; - argcount++; - } + for (address_item * ad = addr; ad; ad = ad->next) + { + argv[i++] = ad->address; + argcount++; + } /* Subtract one since we replace $pipe_addresses */ argcount--; @@ -2123,10 +2136,10 @@ if (expand_arguments) (Ustrcmp(argv[i], "$address_pipe") == 0 || Ustrcmp(argv[i], "${address_pipe}") == 0)) { - int address_pipe_i; int address_pipe_argcount = 0; int address_pipe_max_args; uschar **address_pipe_argv; + BOOL tainted; /* We can never have more then the argv we will be loading into */ address_pipe_max_args = max_args - argcount + 1; @@ -2135,10 +2148,11 @@ if (expand_arguments) debug_printf("address_pipe_max_args=%d\n", address_pipe_max_args); /* We allocate an additional for (uschar *)0 */ - address_pipe_argv = store_get((address_pipe_max_args+1)*sizeof(uschar *)); + address_pipe_argv = store_get((address_pipe_max_args+1)*sizeof(uschar *), FALSE); /* +1 because addr->local_part[0] == '|' since af_force_command is set */ s = expand_string(addr->local_part + 1); + tainted = is_tainted(s); if (s == NULL || *s == '\0') { @@ -2157,7 +2171,7 @@ if (expand_arguments) { ss = s + 1; while (*ss != 0 && *ss != '\'') ss++; - address_pipe_argv[address_pipe_argcount++] = ss = store_get(ss - s++); + address_pipe_argv[address_pipe_argcount++] = ss = store_get(ss - s++, tainted); while (*s != 0 && *s != '\'') *ss++ = *s++; if (*s != 0) s++; *ss++ = 0; @@ -2167,7 +2181,7 @@ if (expand_arguments) while (isspace(*s)) s++; /* strip space after arg */ } - address_pipe_argv[address_pipe_argcount] = (uschar *)0; + address_pipe_argv[address_pipe_argcount] = US 0; /* If *s != 0 we have run out of argument slots. */ if (*s != 0) @@ -2214,8 +2228,8 @@ if (expand_arguments) /* Now we fill in the slots we just moved argv out of * [argv 0][argv 1][argv 2=pipeargv[0]][argv 3=pipeargv[1]][old argv 3][0] */ - for (address_pipe_i = 0; - address_pipe_argv[address_pipe_i] != (uschar *)0; + for (int address_pipe_i = 0; + address_pipe_argv[address_pipe_i] != US 0; address_pipe_i++) { argv[i++] = address_pipe_argv[address_pipe_i]; @@ -2232,16 +2246,16 @@ if (expand_arguments) else { const uschar *expanded_arg; - enable_dollar_recipients = allow_dollar_recipients; + f.enable_dollar_recipients = allow_dollar_recipients; expanded_arg = expand_cstring(argv[i]); - enable_dollar_recipients = FALSE; + f.enable_dollar_recipients = FALSE; - if (expanded_arg == NULL) + if (!expanded_arg) { uschar *msg = string_sprintf("Expansion of \"%s\" " "from command \"%s\" in %s failed: %s", argv[i], cmd, etext, expand_string_message); - if (addr != NULL) + if (addr) { addr->transport_return = expand_failed; addr->message = msg; @@ -2256,7 +2270,7 @@ if (expand_arguments) DEBUG(D_transport) { debug_printf("direct command after expansion:\n"); - for (i = 0; argv[i] != (uschar *)0; i++) + for (int i = 0; argv[i] != US 0; i++) debug_printf(" argv[%d] = %s\n", i, string_printing(argv[i])); } } @@ -2264,6 +2278,7 @@ if (expand_arguments) return TRUE; } +#endif /*!MACRO_PREDEF*/ /* vi: aw ai sw=2 */ /* End of transport.c */