X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/fb6833e0a5599216f7f0834eb608f0bdf771b2ee..5013d912e961203f2ab2d5f64be90255cda81b80:/src/src/transport.c diff --git a/src/src/transport.c b/src/src/transport.c index 8e0a80240..e6e327822 100644 --- a/src/src/transport.c +++ b/src/src/transport.c @@ -108,10 +108,23 @@ optionlist optionlist_transports[] = { (void *)offsetof(transport_instance, uid) } }; -int optionlist_transports_size = - sizeof(optionlist_transports)/sizeof(optionlist); +int optionlist_transports_size = nelem(optionlist_transports); +void +readconf_options_transports(void) +{ +struct transport_info * ti; + +readconf_options_from_list(optionlist_transports, nelem(optionlist_transports), US"TRANSPORTS", NULL); + +for (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); + } +} + /************************************************* * Initialize transport list * *************************************************/ @@ -139,14 +152,11 @@ 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 != NULL; t = t->next) +for (t = transports; t; t = t->next) { - if (!t->info->local) - { - if (t->shadow != NULL) - log_write(0, LOG_PANIC_DIE|LOG_CONFIG, - "shadow transport not allowed on non-local transport %s", t->name); - } + if (!t->info->local && t->shadow) + log_write(0, LOG_PANIC_DIE|LOG_CONFIG, + "shadow transport not allowed on non-local transport %s", t->name); if (t->body_only && t->headers_only) log_write(0, LOG_PANIC_DIE|LOG_CONFIG, @@ -236,10 +246,12 @@ for (i = 0; i < 100; i++) else { alarm(local_timeout); - #ifdef SUPPORT_TLS - if (tls_out.active == fd) rc = tls_write(FALSE, block, len); else - #endif - rc = write(fd, block, len); +#ifdef SUPPORT_TLS + if (tls_out.active == fd) + rc = tls_write(FALSE, block, len); + else +#endif + rc = write(fd, block, len); save_errno = errno; local_timeout = alarm(0); if (sigalrm_seen) @@ -357,7 +369,7 @@ Arguments: fd file descript to write to chunk pointer to data to write len length of data to write - usr_crlf TRUE if CR LF is wanted at the end of each line + tctx transport context - processing to be done during output In addition, the static nl_xxx variables must be set as required. @@ -365,7 +377,7 @@ Returns: TRUE on success, FALSE on failure (with errno preserved) */ static BOOL -write_chunk(int fd, uschar *chunk, int len, BOOL use_crlf) +write_chunk(int fd, transport_ctx * tctx, uschar *chunk, int len) { uschar *start = chunk; uschar *end = chunk + len; @@ -408,17 +420,30 @@ possible. */ for (ptr = start; ptr < end; ptr++) { - int ch; + int ch, len; /* Flush the buffer if it has reached the threshold - we want to leave enough room for the next uschar, plus a possible extra CR for an LF, plus the escape string. */ - if (chunk_ptr - deliver_out_buffer > mlen) + if ((len = chunk_ptr - deliver_out_buffer) > mlen) { - if (!transport_write_block(fd, deliver_out_buffer, - chunk_ptr - deliver_out_buffer)) - return FALSE; + DEBUG(D_transport) debug_printf("flushing headers buffer\n"); + + /* 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->chunk_cb(fd, tctx, (unsigned)len, 0) != OK + || !transport_write_block(fd, deliver_out_buffer, len) + || tctx->chunk_cb(fd, tctx, 0, tc_reap_prev) != OK + ) + return FALSE; + } + else + if (!transport_write_block(fd, deliver_out_buffer, len)) + return FALSE; chunk_ptr = deliver_out_buffer; } @@ -428,7 +453,7 @@ for (ptr = start; ptr < end; ptr++) /* Insert CR before NL if required */ - if (use_crlf) *chunk_ptr++ = '\r'; + if (tctx && tctx->options & topt_use_crlf) *chunk_ptr++ = '\r'; *chunk_ptr++ = '\n'; transport_newlines++; @@ -545,14 +570,14 @@ Arguments: pdlist address of anchor of the list of processed addresses first TRUE if this is the first address; set it FALSE afterwards fd the file descriptor to write to - use_crlf to be passed on to write_chunk() + tctx transport context - processing to be done during output Returns: FALSE if writing failed */ static BOOL write_env_to(address_item *p, struct aci **pplist, struct aci **pdlist, - BOOL *first, int fd, BOOL use_crlf) + BOOL *first, int fd, transport_ctx * tctx) { address_item *pp; struct aci *ppp; @@ -574,7 +599,7 @@ for (pp = p;; pp = pp->parent) address_item *dup; for (dup = addr_duplicate; dup; dup = dup->next) if (dup->dupof == pp) /* a dup of our address */ - if (!write_env_to(dup, pplist, pdlist, first, fd, use_crlf)) + if (!write_env_to(dup, pplist, pdlist, first, fd, tctx)) return FALSE; if (!pp->parent) break; } @@ -591,15 +616,15 @@ ppp->next = *pplist; *pplist = ppp; ppp->ptr = pp; -if (!(*first) && !write_chunk(fd, US",\n ", 3, use_crlf)) return FALSE; +if (!*first && !write_chunk(fd, tctx, US",\n ", 3)) return FALSE; *first = FALSE; -return write_chunk(fd, pp->address, Ustrlen(pp->address), use_crlf); +return write_chunk(fd, tctx, pp->address, Ustrlen(pp->address)); } -/* Add/remove/rewwrite headers, and send them plus the empty-line sparator. +/* Add/remove/rewrite headers, and send them plus the empty-line separator. Globals: header_list @@ -608,20 +633,19 @@ Arguments: addr (chain of) addresses (for extra headers), or NULL; only the first address is used fd file descriptor to write the message to - sendfn function for output - use_crlf turn NL into CR LF - rewrite_rules chain of header rewriting rules - rewrite_existflags flags for the rewriting rules + tctx transport context + sendfn function for output (transport or verify) Returns: TRUE on success; FALSE on failure. */ BOOL -transport_headers_send(address_item *addr, int fd, transport_instance * tblock, - BOOL (*sendfn)(int fd, uschar * s, int len, BOOL use_crlf), - BOOL use_crlf) +transport_headers_send(int fd, transport_ctx * tctx, + BOOL (*sendfn)(int fd, 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; /* Then the message's headers. Don't write any that are flagged as "old"; that means they were rewritten, or are a record of envelope rewriting, or @@ -676,7 +700,7 @@ for (h = header_list; h; h = h->next) if (h->type != htype_old) if ((hh = rewrite_header(h, NULL, NULL, tblock->rewrite_rules, tblock->rewrite_existflags, FALSE))) { - if (!sendfn(fd, hh->text, hh->slen, use_crlf)) return FALSE; + if (!sendfn(fd, tctx, hh->text, hh->slen)) return FALSE; store_reset(reset_point); continue; /* With the next header line */ } @@ -684,7 +708,7 @@ for (h = header_list; h; h = h->next) if (h->type != htype_old) /* Either no rewriting rules, or it didn't get rewritten */ - if (!sendfn(fd, h->text, h->slen, use_crlf)) return FALSE; + if (!sendfn(fd, tctx, h->text, h->slen)) return FALSE; } /* Header removed */ @@ -719,7 +743,7 @@ if (addr) hprev = h; if (i == 1) { - if (!sendfn(fd, h->text, h->slen, use_crlf)) return FALSE; + if (!sendfn(fd, tctx, h->text, h->slen)) return FALSE; DEBUG(D_transport) debug_printf("added header line(s):\n%s---\n", h->text); } @@ -744,8 +768,8 @@ if (tblock && (list = CUS tblock->add_headers)) int len = Ustrlen(s); if (len > 0) { - if (!sendfn(fd, s, len, use_crlf)) return FALSE; - if (s[len-1] != '\n' && !sendfn(fd, US"\n", 1, use_crlf)) + if (!sendfn(fd, tctx, s, len)) return FALSE; + if (s[len-1] != '\n' && !sendfn(fd, tctx, US"\n", 1)) return FALSE; DEBUG(D_transport) { @@ -761,7 +785,7 @@ if (tblock && (list = CUS tblock->add_headers)) /* Separate headers from body with a blank line */ -return sendfn(fd, US"\n", 1, use_crlf); +return sendfn(fd, tctx, US"\n", 1); } @@ -814,12 +838,12 @@ Arguments: end_dot if TRUE, send a terminating "." line at the end no_headers if TRUE, omit the headers no_body if TRUE, omit the body - size_limit if > 0, this is a limit to the size of message written; + check_string a string to check for at the start of lines, or NULL + escape_string a string to insert in front of any check string + size_limit if > 0, this is a limit to the size of message written; it is used when returning messages to their senders, and is approximate rather than exact, owing to chunk buffering - check_string a string to check for at the start of lines, or NULL - escape_string a string to insert in front of any check string Returns: TRUE on success; FALSE (with errno) on failure. In addition, the global variable transport_count @@ -829,9 +853,7 @@ Returns: TRUE on success; FALSE (with errno) on failure. static BOOL internal_transport_write_message(int fd, transport_ctx * tctx, int size_limit) { -int written = 0; int len; -BOOL use_crlf = (tctx->options & topt_use_crlf) != 0; /* Initialize pointer in output buffer. */ @@ -869,7 +891,7 @@ if (!(tctx->options & topt_no_headers)) uschar buffer[ADDRESS_MAXLENGTH + 20]; int n = sprintf(CS buffer, "Return-path: <%.*s>\n", ADDRESS_MAXLENGTH, return_path); - if (!write_chunk(fd, buffer, n, use_crlf)) return FALSE; + if (!write_chunk(fd, tctx, buffer, n)) return FALSE; } /* Add envelope-to: if requested */ @@ -882,19 +904,19 @@ if (!(tctx->options & topt_no_headers)) struct aci *dlist = NULL; void *reset_point = store_get(0); - if (!write_chunk(fd, US"Envelope-to: ", 13, use_crlf)) return FALSE; + if (!write_chunk(fd, tctx, US"Envelope-to: ", 13)) return FALSE; /* Pick up from all the addresses. The plist and dlist variables are anchors for lists of addresses already handled; they have to be defined at - this level becuase write_env_to() calls itself recursively. */ + 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, fd, use_crlf)) + if (!write_env_to(p, &plist, &dlist, &first, fd, tctx)) return FALSE; /* Add a final newline and reset the store used for tracking duplicates */ - if (!write_chunk(fd, US"\n", 1, use_crlf)) return FALSE; + if (!write_chunk(fd, tctx, US"\n", 1)) return FALSE; store_reset(reset_point); } @@ -904,7 +926,7 @@ if (!(tctx->options & topt_no_headers)) { uschar buffer[100]; int n = sprintf(CS buffer, "Delivery-date: %s\n", tod_stamp(tod_full)); - if (!write_chunk(fd, buffer, n, use_crlf)) return FALSE; + if (!write_chunk(fd, tctx, buffer, n)) return FALSE; } /* Then the message's headers. Don't write any that are flagged as "old"; @@ -913,8 +935,65 @@ if (!(tctx->options & topt_no_headers)) match any entries therein. Then check addr->prop.remove_headers too, provided that addr is not NULL. */ - if (!transport_headers_send(tctx->addr, fd, tctx->tblock, &write_chunk, use_crlf)) + if (!transport_headers_send(fd, tctx, &write_chunk)) + return FALSE; + } + +/* When doing RFC3030 CHUNKING output, work out how much data would be in a +last-BDAT, consisting of the current write_chunk() output buffer fill +(optimally, all of the headers - but it does not matter if we already had to +flush that buffer with non-last BDAT prependix) plus the amount of body data +(as expanded for CRLF lines). Then create and write BDAT(s), and ensure +that further use of write_chunk() will not prepend BDATs. +The first BDAT written will also first flush any outstanding MAIL and RCPT +commands which were buffered thans to PIPELINING. +Commands go out (using a send()) from a different buffer to data (using a +write()). They might not end up in the same TCP segment, which is +suboptimal. */ + +if (tctx->options & topt_use_bdat) + { + off_t fsize; + int hsize, size = 0; + + if ((hsize = chunk_ptr - deliver_out_buffer) < 0) + hsize = 0; + if (!(tctx->options & topt_no_body)) + { + if ((fsize = lseek(deliver_datafile, 0, SEEK_END)) < 0) return FALSE; + fsize -= SPOOL_DATA_START_OFFSET; + if (size_limit > 0 && fsize > size_limit) + fsize = size_limit; + size = hsize + fsize; + if (tctx->options & topt_use_crlf) + size += body_linecount; /* account for CRLF-expansion */ + } + + /* If the message is large, emit first a non-LAST chunk with just the + headers, and reap the command responses. This lets us error out early + on RCPT rejects rather than sending megabytes of data. Include headers + on the assumption they are cheap enough and some clever implementations + might errorcheck them too, on-the-fly, and reject that chunk. */ + + if (size > DELIVER_OUT_BUFFER_SIZE && hsize > 0) + { + DEBUG(D_transport) + debug_printf("sending small initial BDAT; hsize=%d\n", hsize); + if ( tctx->chunk_cb(fd, tctx, hsize, 0) != OK + || !transport_write_block(fd, deliver_out_buffer, hsize) + || tctx->chunk_cb(fd, tctx, 0, tc_reap_prev) != OK + ) + return FALSE; + chunk_ptr = deliver_out_buffer; + size -= hsize; + } + + /* Emit a LAST datachunk command. */ + + if (tctx->chunk_cb(fd, tctx, size, tc_chunk_last) != OK) return FALSE; + + tctx->options &= ~topt_use_bdat; } /* If the body is required, ensure that the data for check strings (formerly @@ -925,23 +1004,18 @@ it, applying the size limit if required. */ if (!(tctx->options & topt_no_body)) { + int size = size_limit; + 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 = read(deliver_datafile, deliver_in_buffer, - DELIVER_IN_BUFFER_SIZE)) > 0) + while ( (len = MAX(DELIVER_IN_BUFFER_SIZE, size)) > 0 + && (len = read(deliver_datafile, deliver_in_buffer, len)) > 0) { - if (!write_chunk(fd, deliver_in_buffer, len, use_crlf)) return FALSE; - if (size_limit > 0) - { - written += len; - if (written > size_limit) - { - len = 0; /* Pretend EOF */ - break; - } - } + if (!write_chunk(fd, tctx, deliver_in_buffer, len)) + return FALSE; + size -= len; } /* A read error on the body will have left len == -1 and errno set. */ @@ -955,7 +1029,7 @@ nl_check_length = nl_escape_length = 0; /* If requested, add a terminating "." line (SMTP output). */ -if (tctx->options & topt_end_dot && !write_chunk(fd, US".\n", 2, use_crlf)) +if (tctx->options & topt_end_dot && !write_chunk(fd, tctx, US".\n", 2)) return FALSE; /* Write out any remaining data in the buffer before returning. */ @@ -988,16 +1062,16 @@ Returns: TRUE on success; FALSE (with errno) for any failure BOOL dkim_transport_write_message(int out_fd, transport_ctx * tctx, - struct ob_dkim * dkim) + struct ob_dkim * dkim, const uschar ** err) { int dkim_fd; int save_errno = 0; BOOL rc; uschar * dkim_spool_name; -int sread = 0; -int wwritten = 0; -uschar *dkim_signature = NULL; +uschar * dkim_signature = NULL; +int sread = 0, wwritten = 0, siglen = 0, options; off_t k_file_size; +const uschar * errstr; /* If we can't sign, just call the original function. */ @@ -1012,12 +1086,17 @@ if ((dkim_fd = Uopen(dkim_spool_name, O_RDWR|O_CREAT|O_TRUNC, SPOOL_MODE)) < 0) /* Can't create spool file. Ugh. */ rc = FALSE; save_errno = errno; + *err = string_sprintf("dkim spoolfile create: %s", strerror(errno)); goto CLEANUP; } -/* Call original function to write the -K file; does the CRLF expansion */ +/* Call original function to write the -K file; does the CRLF expansion +(but, in the CHUNKING case, not dot-stuffing and dot-termination). */ +options = tctx->options; +tctx->options &= ~topt_use_bdat; rc = transport_write_message(dkim_fd, tctx, 0); +tctx->options = options; /* Save error state. We must clean up before returning. */ if (!rc) @@ -1026,60 +1105,57 @@ if (!rc) goto CLEANUP; } -if (dkim->dkim_private_key && dkim->dkim_domain && dkim->dkim_selector) +/* Rewind file and feed it to the goats^W DKIM lib */ +dkim->dot_stuffed = !!(options & topt_end_dot); +lseek(dkim_fd, 0, SEEK_SET); +if ((dkim_signature = dkim_exim_sign(dkim_fd, dkim, &errstr))) + siglen = Ustrlen(dkim_signature); +else if (dkim->dkim_strict) { - /* Rewind file and feed it to the goats^W DKIM lib */ - lseek(dkim_fd, 0, SEEK_SET); - dkim_signature = dkim_exim_sign(dkim_fd, - dkim->dkim_private_key, - dkim->dkim_domain, - dkim->dkim_selector, - dkim->dkim_canon, - dkim->dkim_sign_headers); - if (!dkim_signature) - { - if (dkim->dkim_strict) + uschar *dkim_strict_result = expand_string(dkim->dkim_strict); + if (dkim_strict_result) + if ( (strcmpic(dkim->dkim_strict,US"1") == 0) || + (strcmpic(dkim->dkim_strict,US"true") == 0) ) { - uschar *dkim_strict_result = expand_string(dkim->dkim_strict); - if (dkim_strict_result) - if ( (strcmpic(dkim->dkim_strict,US"1") == 0) || - (strcmpic(dkim->dkim_strict,US"true") == 0) ) - { - /* Set errno to something halfway meaningful */ - save_errno = EACCES; - log_write(0, LOG_MAIN, "DKIM: message could not be signed," - " and dkim_strict is set. Deferring message delivery."); - rc = FALSE; - goto CLEANUP; - } + /* Set errno to something halfway meaningful */ + save_errno = EACCES; + log_write(0, LOG_MAIN, "DKIM: message could not be signed," + " and dkim_strict is set. Deferring message delivery."); + *err = errstr; + rc = FALSE; + goto CLEANUP; } - } + } - if (dkim_signature) - { - int siglen = Ustrlen(dkim_signature); - while(siglen > 0) - { -#ifdef SUPPORT_TLS - wwritten = tls_out.active == out_fd - ? tls_write(FALSE, dkim_signature, siglen) - : write(out_fd, dkim_signature, siglen); -#else - wwritten = write(out_fd, dkim_signature, siglen); +#ifndef HAVE_LINUX_SENDFILE +if (options & topt_use_bdat) #endif - if (wwritten == -1) - { - /* error, bail out */ - save_errno = errno; - rc = FALSE; - goto CLEANUP; - } - siglen -= wwritten; - dkim_signature += wwritten; - } + k_file_size = lseek(dkim_fd, 0, SEEK_END); /* Fetch file size */ + +if (options & topt_use_bdat) + { + + /* On big messages output a precursor chunk to get any pipelined + MAIL & RCPT commands flushed, then reap the responses so we can + error out on RCPT rejects before sending megabytes. */ + + if (siglen + k_file_size > DELIVER_OUT_BUFFER_SIZE && siglen > 0) + { + if ( tctx->chunk_cb(out_fd, tctx, siglen, 0) != OK + || !transport_write_block(out_fd, dkim_signature, siglen) + || tctx->chunk_cb(out_fd, tctx, 0, tc_reap_prev) != OK + ) + goto err; + siglen = 0; } + + if (tctx->chunk_cb(out_fd, tctx, siglen + k_file_size, tc_chunk_last) != OK) + goto err; } +if(siglen > 0 && !transport_write_block(out_fd, dkim_signature, siglen)) + goto err; + #ifdef HAVE_LINUX_SENDFILE /* We can use sendfile() to shove the file contents to the socket. However only if we don't use TLS, @@ -1090,18 +1166,13 @@ if (tls_out.active != out_fd) ssize_t copied = 0; off_t offset = 0; - k_file_size = lseek(dkim_fd, 0, SEEK_END); /* Fetch file size */ - /* Rewind file */ lseek(dkim_fd, 0, SEEK_SET); while(copied >= 0 && offset < k_file_size) copied = sendfile(out_fd, dkim_fd, &offset, k_file_size - offset); if (copied < 0) - { - save_errno = errno; - rc = FALSE; - } + goto err; } else @@ -1114,25 +1185,20 @@ else /* Send file down the original fd */ while((sread = read(dkim_fd, deliver_out_buffer, DELIVER_OUT_BUFFER_SIZE)) >0) { - char *p = deliver_out_buffer; + uschar * p = deliver_out_buffer; /* write the chunk */ while (sread) { #ifdef SUPPORT_TLS wwritten = tls_out.active == out_fd - ? tls_write(FALSE, US p, sread) - : write(out_fd, p, sread); + ? tls_write(FALSE, p, sread) + : write(out_fd, CS p, sread); #else - wwritten = write(out_fd, p, sread); + wwritten = write(out_fd, CS p, sread); #endif if (wwritten == -1) - { - /* error, bail out */ - save_errno = errno; - rc = FALSE; - goto CLEANUP; - } + goto err; p += wwritten; sread -= wwritten; } @@ -1146,11 +1212,16 @@ else } CLEANUP: -/* unlink -K file */ -(void)close(dkim_fd); -Uunlink(dkim_spool_name); -errno = save_errno; -return rc; + /* unlink -K file */ + (void)close(dkim_fd); + Uunlink(dkim_spool_name); + errno = save_errno; + return rc; + +err: + save_errno = errno; + rc = FALSE; + goto CLEANUP; } #endif @@ -1177,12 +1248,11 @@ Returns: TRUE on success; FALSE (with errno) for any failure BOOL transport_write_message(int fd, transport_ctx * tctx, int size_limit) { -BOOL use_crlf; BOOL last_filter_was_NL = TRUE; 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 = { NULL, NULL, NULL, NULL, 0 }; +static transport_ctx dummy_tctx = {0}; if (!tctx) tctx = &dummy_tctx; @@ -1201,7 +1271,6 @@ if ( !transport_filter_argv before being written to the incoming fd. First set up the special processing to be done during the copying. */ -use_crlf = (tctx->options & topt_use_crlf) != 0; nl_partial_match = -1; if (tctx->check_string && tctx->escape_string) @@ -1225,10 +1294,13 @@ save_errno = 0; yield = FALSE; write_pid = (pid_t)(-1); -(void)fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC); -filter_pid = child_open(USS transport_filter_argv, NULL, 077, - &fd_write, &fd_read, FALSE); -(void)fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) & ~FD_CLOEXEC); + { + int bits = fcntl(fd, F_GETFD); + (void)fcntl(fd, F_SETFD, bits | FD_CLOEXEC); + filter_pid = child_open(USS transport_filter_argv, NULL, 077, + &fd_write, &fd_read, FALSE); + (void)fcntl(fd, F_SETFD, bits & ~FD_CLOEXEC); + } if (filter_pid < 0) goto TIDY_UP; /* errno set */ DEBUG(D_transport) @@ -1248,7 +1320,7 @@ if ((write_pid = fork()) == 0) nl_check_length = nl_escape_length = 0; tctx->check_string = tctx->escape_string = NULL; - tctx->options &= ~(topt_use_crlf | topt_end_dot); + tctx->options &= ~(topt_use_crlf | topt_end_dot | topt_use_bdat); rc = internal_transport_write_message(fd_write, tctx, size_limit); @@ -1317,7 +1389,7 @@ for (;;) if (len > 0) { - if (!write_chunk(fd, deliver_in_buffer, len, use_crlf)) goto TIDY_UP; + if (!write_chunk(fd, tctx, deliver_in_buffer, len)) goto TIDY_UP; last_filter_was_NL = (deliver_in_buffer[len-1] == '\n'); } @@ -1367,14 +1439,19 @@ if (write_pid > 0) { rc = child_close(write_pid, 30); if (yield) - { if (rc == 0) { BOOL ok; - int dummy = read(pfd[pipe_read], (void *)&ok, sizeof(BOOL)); - if (!ok) + if (read(pfd[pipe_read], (void *)&ok, sizeof(BOOL)) != sizeof(BOOL)) + { + DEBUG(D_transport) + debug_printf("pipe read from writing process: %s\n", strerror(errno)); + save_errno = ERRNO_FILTER_FAIL; + yield = FALSE; + } + else if (!ok) { - dummy = read(pfd[pipe_read], (void *)&save_errno, sizeof(int)); + int dummy = read(pfd[pipe_read], (void *)&save_errno, sizeof(int)); dummy = read(pfd[pipe_read], (void *)&(tctx->addr->more_errno), sizeof(int)); yield = FALSE; } @@ -1386,7 +1463,6 @@ if (write_pid > 0) tctx->addr->more_errno = rc; DEBUG(D_transport) debug_printf("writing process returned %d\n", rc); } - } } (void)close(pfd[pipe_read]); @@ -1399,8 +1475,8 @@ if (yield) nl_check_length = nl_escape_length = 0; if ( tctx->options & topt_end_dot && ( last_filter_was_NL - ? !write_chunk(fd, US".\n", 2, use_crlf) - : !write_chunk(fd, US"\n.\n", 3, use_crlf) + ? !write_chunk(fd, tctx, US".\n", 2) + : !write_chunk(fd, tctx, US"\n.\n", 3) ) ) yield = FALSE; @@ -1700,7 +1776,7 @@ while (1) /* create an array to read entire message queue into memory for processing */ - msgq = (msgq_t*) malloc(sizeof(msgq_t) * host_record->count); + msgq = store_malloc(sizeof(msgq_t) * host_record->count); msgq_count = host_record->count; msgq_actual = msgq_count; @@ -1808,7 +1884,7 @@ test but the code should work */ if (bFound) /* Usual exit from main loop */ { - free (msgq); + store_free (msgq); break; } @@ -1834,7 +1910,7 @@ test but the code should work */ return FALSE; } - free(msgq); + store_free(msgq); } /* we need to process a continuation record */ /* Control gets here when an existing message has been encountered; its @@ -1883,7 +1959,7 @@ DEBUG(D_transport) debug_printf("transport_pass_socket entered\n"); if ((pid = fork()) == 0) { - int i = 16; + int i = 20; const uschar **argv; /* Disconnect entirely from the parent process. If we are running in the @@ -1899,16 +1975,24 @@ if ((pid = fork()) == 0) argv = CUSS child_exec_exim(CEE_RETURN_ARGV, TRUE, &i, FALSE, 0); - if (smtp_use_dsn) argv[i++] = US"-MCD"; - if (smtp_authenticated) argv[i++] = US"-MCA"; - #ifdef SUPPORT_TLS - if (tls_offered) argv[i++] = US"-MCT"; - #endif - - if (smtp_use_size) argv[i++] = US"-MCS"; - if (smtp_use_pipelining) argv[i++] = US"-MCP"; + 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) + { + 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; + } + else + argv[i++] = US"-MCT"; +#endif if (queue_run_pid != (pid_t)0) { @@ -2197,7 +2281,7 @@ if (expand_arguments) */ if (address_pipe_argcount > 1) memmove( - /* current position + additonal args */ + /* current position + additional args */ argv + i + address_pipe_argcount, /* current position + 1 (for the (uschar *)0 at the end) */ argv + i + 1,