DKIM: avoid use of temporary file for signing
authorJeremy Harris <jgh@wizmail.org>
Sun, 23 Apr 2017 11:20:43 +0000 (12:20 +0100)
committerJeremy Harris <jgh@wizmail.org>
Sun, 23 Apr 2017 11:20:43 +0000 (12:20 +0100)
21 files changed:
doc/doc-txt/ChangeLog
src/OS/Makefile-Base
src/scripts/MakeLinks
src/src/deliver.c
src/src/dkim.c
src/src/dkim.h
src/src/dkim_transport.c [new file with mode: 0644]
src/src/functions.h
src/src/macros.h
src/src/queue.c
src/src/smtp_out.c
src/src/string.c
src/src/structs.h
src/src/transport.c
src/src/transports/appendfile.c
src/src/transports/autoreply.c
src/src/transports/lmtp.c
src/src/transports/pipe.c
src/src/transports/smtp.c
src/src/verify.c
test/log/4521

index bebc9e70f51fc0ea92b6efb04f919b79aa98d899..1d6ad343bef471ab73f0cfc0435ff4573ffc1e50 100644 (file)
@@ -56,6 +56,11 @@ JH/07 Fix smtp transport use of limited max_rcpt under mua_wrapper. Previously
 JH/08 Pipeline CHUNKING command and data together, on kernels that support
       MSG_MORE.  Only in-clear (not on TLS connections).
 
 JH/08 Pipeline CHUNKING command and data together, on kernels that support
       MSG_MORE.  Only in-clear (not on TLS connections).
 
+JH/09 Avoid using a temporary file during transport using dkim.  Unless a
+      transport-filter is involved we can buffer the headers in memory for
+      creating the signature, and read the spool data file once for the
+      signature and again for transmission.
+
 
 Exim version 4.89
 -----------------
 
 Exim version 4.89
 -----------------
index f6b42f353ad79cc23ae08e64ac66f255031e0841..f3903180beb72e55be59ac7819734390dc1efb59 100644 (file)
@@ -331,7 +331,7 @@ OBJ_LOOKUPS = lookups/lf_quote.o lookups/lf_check_file.o lookups/lf_sqlperform.o
 
 OBJ_EXIM = acl.o base64.o child.o crypt16.o daemon.o dbfn.o debug.o deliver.o \
         directory.o dns.o drtables.o enq.o exim.o expand.o filter.o \
 
 OBJ_EXIM = acl.o base64.o child.o crypt16.o daemon.o dbfn.o debug.o deliver.o \
         directory.o dns.o drtables.o enq.o exim.o expand.o filter.o \
-        filtertest.o globals.o dkim.o hash.o \
+        filtertest.o globals.o dkim.o dkim_transport.o hash.o \
         header.o host.o ip.o log.o lss.o match.o moan.o \
         os.o parse.o queue.o \
         rda.o readconf.o receive.o retry.o rewrite.o rfc2047.o \
         header.o host.o ip.o log.o lss.o match.o moan.o \
         os.o parse.o queue.o \
         rda.o readconf.o receive.o retry.o rewrite.o rfc2047.o \
@@ -647,6 +647,7 @@ transport.o:     $(HDRS) transport.c
 tree.o:          $(HDRS) tree.c
 verify.o:        $(HDRS) transports/smtp.h verify.c
 dkim.o:          $(HDRS) pdkim/pdkim.h dkim.c
 tree.o:          $(HDRS) tree.c
 verify.o:        $(HDRS) transports/smtp.h verify.c
 dkim.o:          $(HDRS) pdkim/pdkim.h dkim.c
+dkim_transport.o: $(HDRS) dkim_transport.c
 
 # Dependencies for WITH_CONTENT_SCAN modules
 
 
 # Dependencies for WITH_CONTENT_SCAN modules
 
index b710c2fd8ab6f133fc3888a09a894d41c5c40e7c..d361487cf0cd99e3b37b9459acf04b9006edb9bc 100755 (executable)
@@ -107,7 +107,8 @@ for f in blob.h dbfunctions.h dbstuff.h exim.h functions.h globals.h \
   setenv.c environment.c \
   sieve.c smtp_in.c smtp_out.c spool_in.c spool_out.c std-crypto.c store.c \
   string.c tls.c tlscert-gnu.c tlscert-openssl.c tls-gnu.c tls-openssl.c \
   setenv.c environment.c \
   sieve.c smtp_in.c smtp_out.c spool_in.c spool_out.c std-crypto.c store.c \
   string.c tls.c tlscert-gnu.c tlscert-openssl.c tls-gnu.c tls-openssl.c \
-  tod.c transport.c tree.c verify.c version.c dkim.c dkim.h dmarc.c dmarc.h \
+  tod.c transport.c tree.c verify.c version.c \
+  dkim.c dkim.h dkim_transport.c dmarc.c dmarc.h \
   valgrind.h memcheck.h
 do
   ln -s ../src/$f $f
   valgrind.h memcheck.h
 do
   ln -s ../src/$f $f
index ca3b8abfb12956d0dc816a15d9f0f015e570b1a6..2787d0040d5cba90d39886914e1c1f8c3f3cabf5 100644 (file)
@@ -7272,8 +7272,9 @@ if (addr_senddsn)
 
     /* Write the original email out */
 
 
     /* Write the original email out */
 
+    tctx.u.fd = fileno(f);
     tctx.options = topt_add_return_path | topt_no_body;
     tctx.options = topt_add_return_path | topt_no_body;
-    transport_write_message(fileno(f), &tctx, 0);
+    transport_write_message(&tctx, 0);
     fflush(f);
 
     fprintf(f,"\n--%s--\n", bound);
     fflush(f);
 
     fprintf(f,"\n--%s--\n", bound);
@@ -7732,11 +7733,12 @@ wording. */
        transport_ctx tctx = {0};
        transport_instance tb = {0};
 
        transport_ctx tctx = {0};
        transport_instance tb = {0};
 
+       tctx.u.fd = fileno(f);
        tctx.tblock = &tb;
        tctx.options = topt;
        tb.add_headers = dsnnotifyhdr;
 
        tctx.tblock = &tb;
        tctx.options = topt;
        tb.add_headers = dsnnotifyhdr;
 
-       transport_write_message(fileno(f), &tctx, 0);
+       transport_write_message(&tctx, 0);
        }
       fflush(f);
 
        }
       fflush(f);
 
@@ -8197,12 +8199,13 @@ else if (addr_defer != (address_item *)(+1))
 
         fflush(f);
         /* header only as required by RFC. only failure DSN needs to honor RET=FULL */
 
         fflush(f);
         /* header only as required by RFC. only failure DSN needs to honor RET=FULL */
+       tctx.u.fd = fileno(f);
         tctx.options = topt_add_return_path | topt_no_body;
         transport_filter_argv = NULL;   /* Just in case */
         return_path = sender_address;   /* In case not previously set */
 
         /* Write the original email out */
         tctx.options = topt_add_return_path | topt_no_body;
         transport_filter_argv = NULL;   /* Just in case */
         return_path = sender_address;   /* In case not previously set */
 
         /* Write the original email out */
-        transport_write_message(fileno(f), &tctx, 0);
+        transport_write_message(&tctx, 0);
         fflush(f);
 
         fprintf(f,"\n--%s--\n", bound);
         fflush(f);
 
         fprintf(f,"\n--%s--\n", bound);
index f510214439b23605335945a27a40911ade2da981..f0dfb8af3e23e1069c2084d6cff2580c8fcdb7c9 100644 (file)
@@ -448,15 +448,19 @@ switch (what)
 }
 
 
 }
 
 
+/* Generate signatures for the given file, returning a string.
+If a prefix is given, prepend it to the file for the calculations.
+*/
+
 uschar *
 uschar *
-dkim_exim_sign(int dkim_fd, struct ob_dkim * dkim, const uschar ** errstr)
+dkim_exim_sign(int fd, off_t off, uschar * prefix,
+  struct ob_dkim * dkim, const uschar ** errstr)
 {
 const uschar * dkim_domain;
 int sep = 0;
 uschar *seen_items = NULL;
 int seen_items_size = 0;
 int seen_items_offset = 0;
 {
 const uschar * dkim_domain;
 int sep = 0;
 uschar *seen_items = NULL;
 int seen_items_size = 0;
 int seen_items_offset = 0;
-uschar itembuf[256];
 uschar *dkim_canon_expanded;
 uschar *dkim_sign_headers_expanded;
 uschar *dkim_private_key_expanded;
 uschar *dkim_canon_expanded;
 uschar *dkim_sign_headers_expanded;
 uschar *dkim_private_key_expanded;
@@ -485,10 +489,9 @@ if (!(dkim_domain = expand_cstring(dkim->dkim_domain)))
 
 /* Set $dkim_domain expansion variable to each unique domain in list. */
 
 
 /* Set $dkim_domain expansion variable to each unique domain in list. */
 
-while ((dkim_signing_domain = string_nextinlist(&dkim_domain, &sep,
-                                  itembuf, sizeof(itembuf))))
+while ((dkim_signing_domain = string_nextinlist(&dkim_domain, &sep, NULL, 0)))
   {
   {
-  if (!dkim_signing_domain || dkim_signing_domain[0] == '\0')
+  if (dkim_signing_domain[0] == '\0')
     continue;
 
   /* Only sign once for each domain, no matter how often it
     continue;
 
   /* Only sign once for each domain, no matter how often it
@@ -619,9 +622,12 @@ while ((dkim_signing_domain = string_nextinlist(&dkim_domain, &sep,
                      pdkim_canon,
                      pdkim_canon, -1, 0, 0);
 
                      pdkim_canon,
                      pdkim_canon, -1, 0, 0);
 
-  lseek(dkim_fd, 0, SEEK_SET);
+  if (prefix)
+    pdkim_feed(ctx, prefix, Ustrlen(prefix));
+
+  lseek(fd, off, SEEK_SET);
 
 
-  while ((sread = read(dkim_fd, &buf, sizeof(buf))) > 0)
+  while ((sread = read(fd, &buf, sizeof(buf))) > 0)
     if ((pdkim_rc = pdkim_feed(ctx, buf, sread)) != PDKIM_OK)
       goto pk_bad;
 
     if ((pdkim_rc = pdkim_feed(ctx, buf, sread)) != PDKIM_OK)
       goto pk_bad;
 
index bfdc7d42b519275ecfd9b27a0cc4675b59c9d1ab..83c68a76ca0152ed5ecc9934f9eebef8e00ee51f 100644 (file)
@@ -6,7 +6,7 @@
 /* See the file NOTICE for conditions of use and distribution. */
 
 void    dkim_exim_init(void);
 /* See the file NOTICE for conditions of use and distribution. */
 
 void    dkim_exim_init(void);
-uschar *dkim_exim_sign(int, struct ob_dkim *, const uschar **);
+uschar *dkim_exim_sign(int, off_t, uschar *, struct ob_dkim *, const uschar **);
 void    dkim_exim_verify_init(BOOL);
 void    dkim_exim_verify_feed(uschar *, int);
 void    dkim_exim_verify_finish(void);
 void    dkim_exim_verify_init(BOOL);
 void    dkim_exim_verify_feed(uschar *, int);
 void    dkim_exim_verify_finish(void);
diff --git a/src/src/dkim_transport.c b/src/src/dkim_transport.c
new file mode 100644 (file)
index 0000000..c8ac92e
--- /dev/null
@@ -0,0 +1,346 @@
+/*************************************************
+*     Exim - an Internet mail transport agent    *
+*************************************************/
+
+/* Copyright (c) University of Cambridge 1995 - 2016 */
+/* See the file NOTICE for conditions of use and distribution. */
+
+/* Transport shim for dkim signing */
+
+#ifndef DISABLE_DKIM
+
+
+#include "exim.h"
+
+#ifdef HAVE_LINUX_SENDFILE
+#include <sys/sendfile.h>
+#endif
+
+
+static BOOL
+dkt_sign_fail(struct ob_dkim * dkim, int * errp)
+{
+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) )
+      {
+      /* Set errno to something halfway meaningful */
+      *errp = EACCES;
+      log_write(0, LOG_MAIN, "DKIM: message could not be signed,"
+       " and dkim_strict is set. Deferring message delivery.");
+      return FALSE;
+      }
+  }
+return TRUE;
+}
+
+static BOOL
+dkt_send_file(int out_fd, int in_fd, off_t off, size_t size)
+{
+DEBUG(D_transport) debug_printf("send file fd=%d size=%d\n", out_fd, size - off);
+
+/*XXX should implement timeout, like transport_write_block_fd() ? */
+
+/* Rewind file */
+lseek(in_fd, off, SEEK_SET);
+
+#ifdef HAVE_LINUX_SENDFILE
+/* We can use sendfile() to shove the file contents
+   to the socket. However only if we don't use TLS,
+   as then there's another layer of indirection
+   before the data finally hits the socket. */
+if (tls_out.active != out_fd)
+  {
+  ssize_t copied = 0;
+
+  while(copied >= 0 && off < size)
+    copied = sendfile(tctx->u.fd, dkim_fd, &off, size - off);
+  if (copied < 0)
+    return FALSE;
+  }
+else
+
+#endif
+
+  {
+  int sread, wwritten;
+
+  /* Send file down the original fd */
+  while((sread = read(in_fd, deliver_out_buffer, DELIVER_OUT_BUFFER_SIZE)) >0)
+    {
+    uschar * p = deliver_out_buffer;
+    /* write the chunk */
+
+    while (sread)
+      {
+#ifdef SUPPORT_TLS
+      wwritten = tls_out.active == out_fd
+       ? tls_write(FALSE, p, sread)
+       : write(out_fd, CS p, sread);
+#else
+      wwritten = write(out_fd, CS p, sread);
+#endif
+      if (wwritten == -1)
+       return FALSE;
+      p += wwritten;
+      sread -= wwritten;
+      }
+    }
+
+  if (sread == -1)
+    return FALSE;
+  }
+
+return TRUE;
+}
+
+
+
+
+/* This function is a wrapper around transport_write_message().
+   It is only called from the smtp transport if DKIM or Domainkeys support
+   is active and no transport filter is to be used.
+
+Arguments:
+  As for transport_write_message() in transort.c, with additional arguments
+  for DKIM.
+
+Returns:       TRUE on success; FALSE (with errno) for any failure
+*/
+
+static BOOL
+dkt_direct(transport_ctx * tctx, struct ob_dkim * dkim,
+  const uschar ** err)
+{
+int save_fd = tctx->u.fd;
+int save_options = tctx->options;
+uschar * hdrs, * dkim_signature;
+int siglen, hsize;
+const uschar * errstr;
+BOOL rc;
+
+DEBUG(D_transport) debug_printf("dkim signing direct-mode\n");
+
+/* Get headers in string for signing and transmission */
+
+tctx->u.msg = NULL;
+tctx->options = tctx->options & ~(topt_end_dot | topt_use_bdat)
+  | topt_output_string | topt_no_body;
+
+rc = transport_write_message(tctx, 0);
+hdrs = tctx->u.msg;
+hdrs[hsize = tctx->msg_ptr] = '\0';
+
+tctx->u.fd = save_fd;
+tctx->options = save_options;
+if (!rc) return FALSE;
+
+/* Get signatures for headers plus spool data file */
+
+dkim->dot_stuffed = !!(save_options & topt_end_dot);
+
+if ((dkim_signature = dkim_exim_sign(deliver_datafile, SPOOL_DATA_START_OFFSET,
+                                   hdrs, dkim, &errstr)))
+  siglen = Ustrlen(dkim_signature);
+else if (!(rc = dkt_sign_fail(dkim, &errno)))
+  {
+  *err = errstr;
+  return FALSE;
+  }
+
+/* Write the signature and headers into the deliver-out-buffer.  This should
+mean they go out in the same packet as the MAIL, RCPT and (first) BDAT commands
+(transport_write_message() sizes the BDAT for the buffered amount) - for short
+messages, the BDAT LAST command.  We want no CRLF or dotstuffing expansion */
+
+tctx->options &= ~topt_use_crlf;
+transport_write_reset(0);
+if (  !write_chunk(tctx, dkim_signature, siglen)
+   || !write_chunk(tctx, hdrs, hsize))
+  return FALSE;
+
+tctx->options = save_options | topt_no_headers | topt_continuation;
+
+if (!(transport_write_message(tctx, 0)))
+  return FALSE;
+
+tctx->options = save_options;
+return TRUE;
+}
+
+
+/* This function is a wrapper around transport_write_message().
+   It is only called from the smtp transport if DKIM or Domainkeys support
+   is active and a transport filter is to be used.  The function sets up a
+   replacement fd into a -K file, then calls the normal function. This way, the
+   exact bits that exim would have put "on the wire" will end up in the file
+   (except for TLS encapsulation, which is the very very last thing). When we
+   are done signing the file, send the signed message down the original fd (or
+   TLS fd).
+
+Arguments:
+  As for transport_write_message() in transort.c, with additional arguments
+  for DKIM.
+
+Returns:       TRUE on success; FALSE (with errno) for any failure
+*/
+
+static BOOL
+dkt_via_kfile(transport_ctx * tctx, struct ob_dkim * dkim, const uschar ** err)
+{
+int dkim_fd;
+int save_errno = 0;
+BOOL rc;
+uschar * dkim_spool_name, * dkim_signature;
+int sread = 0, wwritten = 0, siglen, options;
+off_t k_file_size;
+const uschar * errstr;
+
+dkim_spool_name = spool_fname(US"input", message_subdir, message_id,
+                   string_sprintf("-%d-K", (int)getpid()));
+
+DEBUG(D_transport) debug_printf("dkim signing via file %s\n", dkim_spool_name);
+
+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 transport utility function to write the -K file; does the CRLF expansion
+(but, in the CHUNKING case, neither dot-stuffing nor dot-termination). */
+
+  {
+  int save_fd = tctx->u.fd;
+  tctx->u.fd = dkim_fd;
+  options = tctx->options;
+  tctx->options &= ~topt_use_bdat;
+
+  rc = transport_write_message(tctx, 0);
+
+  tctx->u.fd = save_fd;
+  tctx->options = options;
+  }
+
+/* Save error state. We must clean up before returning. */
+if (!rc)
+  {
+  save_errno = errno;
+  goto CLEANUP;
+  }
+
+/* Feed the file to the goats^W DKIM lib */
+
+dkim->dot_stuffed = !!(options & topt_end_dot);
+if ((dkim_signature = dkim_exim_sign(dkim_fd, 0, NULL, dkim, &errstr)))
+  siglen = Ustrlen(dkim_signature);
+else if (!(rc = dkt_sign_fail(dkim, &save_errno)))
+  {
+  *err = errstr;
+  goto CLEANUP;
+  }
+
+#ifndef HAVE_LINUX_SENDFILE
+if (options & topt_use_bdat)
+#endif
+  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(tctx, siglen, 0) != OK
+       || !transport_write_block(tctx, dkim_signature, siglen, FALSE)
+       || tctx->chunk_cb(tctx, 0, tc_reap_prev) != OK
+       )
+      goto err;
+    siglen = 0;
+    }
+
+  /* Send the BDAT command for the entire message, as a single LAST-marked
+  chunk. */
+
+  if (tctx->chunk_cb(tctx, siglen + k_file_size, tc_chunk_last) != OK)
+    goto err;
+  }
+
+if(siglen > 0 && !transport_write_block(tctx, dkim_signature, siglen, TRUE))
+  goto err;
+
+if (!dkt_send_file(tctx->u.fd, dkim_fd, 0, k_file_size))
+  {
+  save_errno = errno;
+  rc = FALSE;
+  }
+
+CLEANUP:
+  /* unlink -K file */
+  (void)close(dkim_fd);
+  Uunlink(dkim_spool_name);
+  errno = save_errno;
+  return rc;
+
+err:
+  save_errno = errno;
+  rc = FALSE;
+  goto CLEANUP;
+}
+
+
+
+/***************************************************************************************************
+*    External interface to write the message, while signing it with DKIM and/or Domainkeys         *
+***************************************************************************************************/
+
+/* This function is a wrapper around transport_write_message().
+   It is only called from the smtp transport if DKIM or Domainkeys support
+   is compiled in.
+
+Arguments:
+  As for transport_write_message() in transort.c, with additional arguments
+  for DKIM.
+
+Returns:       TRUE on success; FALSE (with errno) for any failure
+*/
+
+BOOL
+dkim_transport_write_message(transport_ctx * tctx,
+  struct ob_dkim * dkim, const uschar ** err)
+{
+/* If we can't sign, just call the original function. */
+
+if (!(dkim->dkim_private_key && dkim->dkim_domain && dkim->dkim_selector))
+  return transport_write_message(tctx, 0);
+
+/* If there is no filter command set up, construct the message and calculate
+a dkim signature of it, send the signature and a reconstructed message. This
+avoids using a temprary file. */
+
+if (  !transport_filter_argv
+   || !*transport_filter_argv
+   || !**transport_filter_argv
+   )
+  return dkt_direct(tctx, dkim, err);
+
+/* Use the transport path to write a file, calculate a dkim signature,
+send the signature and then send the file. */
+
+return dkt_via_kfile(tctx, dkim, err);
+}
+
+#endif /* whole file */
+
+/* vi: aw ai sw=2
+*/
+/* End of dkim_transport.c */
index c3c96b69c8dc3ff48b736b8d7eae9d9a05d82576..ee17e9c2774f4ae3667ed0f4801e28c69f413f47 100644 (file)
@@ -154,7 +154,7 @@ extern void    delivery_re_exec(int);
 
 extern BOOL    directory_make(const uschar *, const uschar *, int, BOOL);
 #ifndef DISABLE_DKIM
 
 extern BOOL    directory_make(const uschar *, const uschar *, int, BOOL);
 #ifndef DISABLE_DKIM
-extern BOOL    dkim_transport_write_message(int, transport_ctx *,
+extern BOOL    dkim_transport_write_message(transport_ctx *,
                  struct ob_dkim *, const uschar ** errstr);
 #endif
 extern dns_address *dns_address_from_rr(dns_answer *, dns_record *);
                  struct ob_dkim *, const uschar ** errstr);
 #endif
 extern dns_address *dns_address_from_rr(dns_answer *, dns_record *);
@@ -231,6 +231,7 @@ extern uschar *imap_utf7_encode(uschar *, const uschar *,
                                 uschar, uschar *, uschar **);
 
 extern void    invert_address(uschar *, uschar *);
                                 uschar, uschar *, uschar **);
 
 extern void    invert_address(uschar *, uschar *);
+extern BOOL    internal_transport_write_message(transport_ctx *, int);
 extern int     ip_addr(void *, int, const uschar *, int);
 extern int     ip_bind(int, int, uschar *, int);
 extern int     ip_connect(int, int, const uschar *, int, int, BOOL);
 extern int     ip_addr(void *, int, const uschar *, int);
 extern int     ip_bind(int, int, uschar *, int);
 extern int     ip_connect(int, int, const uschar *, int, int, BOOL);
@@ -484,11 +485,12 @@ extern uschar *transport_rcpt_address(address_item *, BOOL);
 extern BOOL    transport_set_up_command(const uschar ***, uschar *,
                 BOOL, int, address_item *, uschar *, uschar **);
 extern void    transport_update_waiting(host_item *, uschar *);
 extern BOOL    transport_set_up_command(const uschar ***, uschar *,
                 BOOL, int, address_item *, uschar *, uschar **);
 extern void    transport_update_waiting(host_item *, uschar *);
-extern BOOL    transport_write_block(int, uschar *, int);
+extern BOOL    transport_write_block(transport_ctx *, uschar *, int, BOOL);
+extern void    transport_write_reset(int);
 extern BOOL    transport_write_string(int, const char *, ...);
 extern BOOL    transport_write_string(int, const char *, ...);
-extern BOOL    transport_headers_send(int, transport_ctx *,
-                 BOOL (*)(int, transport_ctx *, uschar *, int));
-extern BOOL    transport_write_message(int, transport_ctx *, int);
+extern BOOL    transport_headers_send(transport_ctx *,
+                 BOOL (*)(transport_ctx *, uschar *, int));
+extern BOOL    transport_write_message(transport_ctx *, int);
 extern void    tree_add_duplicate(uschar *, address_item *);
 extern void    tree_add_nonrecipient(uschar *);
 extern void    tree_add_unusable(host_item *);
 extern void    tree_add_duplicate(uschar *, address_item *);
 extern void    tree_add_nonrecipient(uschar *);
 extern void    tree_add_unusable(host_item *);
@@ -522,6 +524,7 @@ extern BOOL    verify_sender(int *, uschar **);
 extern BOOL    verify_sender_preliminary(int *, uschar **);
 extern void    version_init(void);
 
 extern BOOL    verify_sender_preliminary(int *, uschar **);
 extern void    version_init(void);
 
+extern BOOL    write_chunk(transport_ctx *, uschar *, int);
 extern ssize_t write_to_fd_buf(int, const uschar *, size_t);
 
 /* vi: aw
 extern ssize_t write_to_fd_buf(int, const uschar *, size_t);
 
 /* vi: aw
index 0c1425f80c251894981b33104807c0c1e7acc904..8b608f7f8bcec0572ba69bf47de11dce423766a8 100644 (file)
@@ -854,6 +854,8 @@ enum {
 #define topt_no_body            0x040  /* Omit body */
 #define topt_escape_headers     0x080  /* Apply escape check to headers */
 #define topt_use_bdat          0x100  /* prepend chunks with RFC3030 BDAT header */
 #define topt_no_body            0x040  /* Omit body */
 #define topt_escape_headers     0x080  /* Apply escape check to headers */
 #define topt_use_bdat          0x100  /* prepend chunks with RFC3030 BDAT header */
+#define topt_output_string     0x200  /* create string rather than write to fd */
+#define topt_continuation      0x400  /* do not reset buffer */
 
 /* Options for smtp_write_command */
 
 
 /* Options for smtp_write_command */
 
index 50e4aaef34667e03f2cd4707058e6c1bfd0b3283..7b8f727bc9e8ac13407626c071bbe7f4cc002ac7 100644 (file)
@@ -1142,10 +1142,14 @@ if (action != MSG_SHOW_COPY) printf("Message %s ", id);
 switch(action)
   {
   case MSG_SHOW_COPY:
 switch(action)
   {
   case MSG_SHOW_COPY:
-  deliver_in_buffer = store_malloc(DELIVER_IN_BUFFER_SIZE);
-  deliver_out_buffer = store_malloc(DELIVER_OUT_BUFFER_SIZE);
-  transport_write_message(1, NULL, 0);
-  break;
+    {
+    transport_ctx tctx = {0};
+    deliver_in_buffer = store_malloc(DELIVER_IN_BUFFER_SIZE);
+    deliver_out_buffer = store_malloc(DELIVER_OUT_BUFFER_SIZE);
+    tctx.u.fd = 1;
+    transport_write_message(&tctx, 0);
+    break;
+    }
 
 
   case MSG_FREEZE:
 
 
   case MSG_FREEZE:
index 7ade9ba6763014d2c988b3e6b5668f51a74f10f2..e10543b0837b3eb0b235e530b77a66ba5d9ac81e 100644 (file)
@@ -331,7 +331,7 @@ int rc;
 int n = outblock->ptr - outblock->buffer;
 
 HDEBUG(D_transport|D_acl) debug_printf_indent("cmd buf flush %d bytes%s\n", n,
 int n = outblock->ptr - outblock->buffer;
 
 HDEBUG(D_transport|D_acl) debug_printf_indent("cmd buf flush %d bytes%s\n", n,
-  mode == SCMD_MORE ? " (with MORE annotation)" : "");
+  mode == SCMD_MORE ? " (more expected)" : "");
 
 #ifdef SUPPORT_TLS
 if (tls_out.active == outblock->sock)
 
 #ifdef SUPPORT_TLS
 if (tls_out.active == outblock->sock)
index 4850fd958776875c7e6ef1a51482ea2576df0b86..0d5a09703452a764d4dade82d82108d39bc70659 100644 (file)
@@ -1081,7 +1081,7 @@ Arguments:
              characters, updated to the new offset
   s        points to characters to add
   count    count of characters to add; must not exceed the length of s, if s
              characters, updated to the new offset
   s        points to characters to add
   count    count of characters to add; must not exceed the length of s, if s
-             is a C string.  If -1 given, strlen(s) is used.
+             is a C string.
 
 If string is given as NULL, *size and *ptr should both be zero.
 
 
 If string is given as NULL, *size and *ptr should both be zero.
 
index 60e7ccd9d79a892548057ca3312c099bb9afe970..474b855779b8725809fca9eb6a0285f9d58b9f54 100644 (file)
@@ -235,6 +235,10 @@ typedef int (*tpt_chunk_cmd_cb)(struct transport_context *, unsigned, unsigned);
 /* Structure for information about a delivery-in-progress */
 
 typedef struct transport_context {
 /* Structure for information about a delivery-in-progress */
 
 typedef struct transport_context {
+  union {                      /* discriminated by option topt_output_string */
+    int                          fd;   /* file descriptor to write message to */
+    uschar *             msg;  /* allocated string with written message */
+  } u;
   transport_instance   * tblock;               /* transport */
   struct address_item  * addr;
   uschar               * check_string;         /* string replacement */
   transport_instance   * tblock;               /* transport */
   struct address_item  * addr;
   uschar               * check_string;         /* string replacement */
@@ -244,6 +248,10 @@ typedef struct transport_context {
   /* items below only used with option topt_use_bdat */
   tpt_chunk_cmd_cb       chunk_cb;             /* per-datachunk callback */
   void                 * smtp_context;
   /* items below only used with option topt_use_bdat */
   tpt_chunk_cmd_cb       chunk_cb;             /* per-datachunk callback */
   void                 * smtp_context;
+
+  /* items below only used with option topt_output_string */
+  int                    msg_size;
+  int                    msg_ptr;
 } transport_ctx;
 
 
 } transport_ctx;
 
 
index 594e02cdee2f4ec9aed6c883c7bbb8e3fd615bde..0f20efe1b3c7e6f160ac783bbdb0822f061a8303 100644 (file)
@@ -11,10 +11,6 @@ transports. */
 
 #include "exim.h"
 
 
 #include "exim.h"
 
-#ifdef HAVE_LINUX_SENDFILE
-#include <sys/sendfile.h>
-#endif
-
 /* Structure for keeping list of addresses that have been added to
 Envelope-To:, in order to avoid duplication. */
 
 /* Structure for keeping list of addresses that have been added to
 Envelope-To:, in order to avoid duplication. */
 
@@ -204,7 +200,7 @@ evermore, so stick a maximum repetition count on the loop to act as a
 longstop.
 
 Arguments:
 longstop.
 
 Arguments:
-  fd        file descriptor to write to
+  tctx      transport context: file descriptor or string to write to
   block     block of bytes to write
   len       number of bytes to write
 
   block     block of bytes to write
   len       number of bytes to write
 
@@ -212,11 +208,12 @@ Returns:    TRUE on success, FALSE on failure (with errno preserved);
               transport_count is incremented by the number of bytes written
 */
 
               transport_count is incremented by the number of bytes written
 */
 
-BOOL
-transport_write_block(int fd, uschar *block, int len)
+static BOOL
+transport_write_block_fd(transport_ctx * tctx, uschar *block, int len, BOOL more)
 {
 int i, rc, save_errno;
 int local_timeout = transport_write_timeout;
 {
 int i, rc, save_errno;
 int local_timeout = transport_write_timeout;
+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. */
 
 /* This loop is for handling incomplete writes and other retries. In most
 normal cases, it is only ever executed once. */
@@ -224,8 +221,8 @@ normal cases, it is only ever executed once. */
 for (i = 0; i < 100; i++)
   {
   DEBUG(D_transport)
 for (i = 0; i < 100; i++)
   {
   DEBUG(D_transport)
-    debug_printf("writing data block fd=%d size=%d timeout=%d\n",
-      fd, len, local_timeout);
+    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()
 
   /* 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()
@@ -234,10 +231,14 @@ for (i = 0; i < 100; i++)
 
   if (transport_write_timeout <= 0)   /* No timeout wanted */
     {
 
   if (transport_write_timeout <= 0)   /* No timeout wanted */
     {
-    #ifdef SUPPORT_TLS
-    if (tls_out.active == fd) rc = tls_write(FALSE, block, len); else
-    #endif
-    rc = write(fd, block, len);
+    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;
     }
 
     save_errno = errno;
     }
 
@@ -246,12 +247,16 @@ for (i = 0; i < 100; i++)
   else
     {
     alarm(local_timeout);
   else
     {
     alarm(local_timeout);
+
+    rc =
 #ifdef SUPPORT_TLS
 #ifdef SUPPORT_TLS
-    if (tls_out.active == fd)
-      rc = tls_write(FALSE, block, len);
-    else
+       (tls_out.active == fd) ? tls_write(FALSE, block, len) :
+#endif
+#ifdef MSG_MORE
+       more ? send(fd, block, len, MSG_MORE) :
 #endif
 #endif
-      rc = write(fd, block, len);
+       write(fd, block, len);
+
     save_errno = errno;
     local_timeout = alarm(0);
     if (sigalrm_seen)
     save_errno = errno;
     local_timeout = alarm(0);
     if (sigalrm_seen)
@@ -323,6 +328,25 @@ return FALSE;
 }
 
 
 }
 
 
+BOOL
+transport_write_block(transport_ctx * tctx, uschar *block, int len, BOOL more)
+{
+if (!(tctx->options & topt_output_string))
+  return transport_write_block_fd(tctx, block, len, more);
+
+/* 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_catn(tctx->u.msg, &tctx->msg_size, &tctx->msg_ptr, block, len);
+return TRUE;
+}
+
+
 
 
 /*************************************************
 
 
 /*************************************************
@@ -342,17 +366,29 @@ Returns:      the yield of transport_write_block()
 BOOL
 transport_write_string(int fd, const char *format, ...)
 {
 BOOL
 transport_write_string(int fd, const char *format, ...)
 {
+transport_ctx tctx = {0};
 va_list ap;
 va_start(ap, format);
 if (!string_vformat(big_buffer, big_buffer_size, format, ap))
   log_write(0, LOG_MAIN|LOG_PANIC_DIE, "overlong formatted string in transport");
 va_end(ap);
 va_list ap;
 va_start(ap, format);
 if (!string_vformat(big_buffer, big_buffer_size, format, ap))
   log_write(0, LOG_MAIN|LOG_PANIC_DIE, "overlong formatted string in transport");
 va_end(ap);
-return transport_write_block(fd, big_buffer, Ustrlen(big_buffer));
+tctx.u.fd = fd;
+return transport_write_block(&tctx, big_buffer, Ustrlen(big_buffer), FALSE);
 }
 
 
 
 
 }
 
 
 
 
+void
+transport_write_reset(int options)
+{
+if (!(options & topt_continuation)) chunk_ptr = deliver_out_buffer;
+nl_partial_match = -1;
+nl_check_length = nl_escape_length = 0;
+}
+
+
+
 /*************************************************
 *              Write character chunk             *
 *************************************************/
 /*************************************************
 *              Write character chunk             *
 *************************************************/
@@ -366,18 +402,18 @@ Static data is used to handle the case when the last character of the previous
 chunk was NL, or matched part of the data that has to be escaped.
 
 Arguments:
 chunk was NL, or matched part of the data that has to be escaped.
 
 Arguments:
-  fd         file descript to write to
+  tctx       transport context - processing to be done during output,
+               and file descriptor to write to
   chunk      pointer to data to write
   len        length of data to write
   chunk      pointer to data to write
   len        length of data to write
-  tctx       transport context - processing to be done during output
 
 In addition, the static nl_xxx variables must be set as required.
 
 Returns:     TRUE on success, FALSE on failure (with errno preserved)
 */
 
 
 In addition, the static nl_xxx variables must be set as required.
 
 Returns:     TRUE on success, FALSE on failure (with errno preserved)
 */
 
-static BOOL
-write_chunk(int fd, transport_ctx * tctx, uschar *chunk, int len)
+BOOL
+write_chunk(transport_ctx * tctx, uschar *chunk, int len)
 {
 uschar *start = chunk;
 uschar *end = chunk + len;
 {
 uschar *start = chunk;
 uschar *end = chunk + len;
@@ -436,13 +472,13 @@ for (ptr = start; ptr < end; ptr++)
     if (tctx &&  tctx->options & topt_use_bdat  &&  tctx->chunk_cb)
       {
       if (  tctx->chunk_cb(tctx, (unsigned)len, 0) != OK
     if (tctx &&  tctx->options & topt_use_bdat  &&  tctx->chunk_cb)
       {
       if (  tctx->chunk_cb(tctx, (unsigned)len, 0) != OK
-        || !transport_write_block(fd, deliver_out_buffer, len)
+        || !transport_write_block(tctx, deliver_out_buffer, len, FALSE)
         || tctx->chunk_cb(tctx, 0, tc_reap_prev) != OK
         )
        return FALSE;
       }
     else
         || tctx->chunk_cb(tctx, 0, tc_reap_prev) != OK
         )
        return FALSE;
       }
     else
-      if (!transport_write_block(fd, deliver_out_buffer, len))
+      if (!transport_write_block(tctx, deliver_out_buffer, len, FALSE))
        return FALSE;
     chunk_ptr = deliver_out_buffer;
     }
        return FALSE;
     chunk_ptr = deliver_out_buffer;
     }
@@ -569,15 +605,15 @@ Arguments:
   pplist    address of anchor of the list of addresses not to output
   pdlist    address of anchor of the list of processed addresses
   first     TRUE if this is the first address; set it FALSE afterwards
   pplist    address of anchor of the list of addresses not to output
   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
   tctx      transport context - processing to be done during output
   tctx      transport context - processing to be done during output
+             and the file descriptor to write to
 
 Returns:    FALSE if writing failed
 */
 
 static BOOL
 write_env_to(address_item *p, struct aci **pplist, struct aci **pdlist,
 
 Returns:    FALSE if writing failed
 */
 
 static BOOL
 write_env_to(address_item *p, struct aci **pplist, struct aci **pdlist,
-  BOOL *first, int fd, transport_ctx * tctx)
+  BOOL *first, transport_ctx * tctx)
 {
 address_item *pp;
 struct aci *ppp;
 {
 address_item *pp;
 struct aci *ppp;
@@ -599,7 +635,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 */
   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, tctx))
+      if (!write_env_to(dup, pplist, pdlist, first, tctx))
        return FALSE;
   if (!pp->parent) break;
   }
        return FALSE;
   if (!pp->parent) break;
   }
@@ -616,9 +652,9 @@ ppp->next = *pplist;
 *pplist = ppp;
 ppp->ptr = pp;
 
 *pplist = ppp;
 ppp->ptr = pp;
 
-if (!*first && !write_chunk(fd, tctx, US",\n ", 3)) return FALSE;
+if (!*first && !write_chunk(tctx, US",\n ", 3)) return FALSE;
 *first = FALSE;
 *first = FALSE;
-return write_chunk(fd, tctx, pp->address, Ustrlen(pp->address));
+return write_chunk(tctx, pp->address, Ustrlen(pp->address));
 }
 
 
 }
 
 
@@ -632,15 +668,14 @@ Globals:
 Arguments:
   addr                  (chain of) addresses (for extra headers), or NULL;
                           only the first address is used
 Arguments:
   addr                  (chain of) addresses (for extra headers), or NULL;
                           only the first address is used
-  fd                    file descriptor to write the message to
   tctx                  transport context
   sendfn               function for output (transport or verify)
 
 Returns:                TRUE on success; FALSE on failure.
 */
 BOOL
   tctx                  transport context
   sendfn               function for output (transport or verify)
 
 Returns:                TRUE on success; FALSE on failure.
 */
 BOOL
-transport_headers_send(int fd, transport_ctx * tctx,
-  BOOL (*sendfn)(int fd, transport_ctx * tctx, uschar * s, int len))
+transport_headers_send(transport_ctx * tctx,
+  BOOL (*sendfn)(transport_ctx * tctx, uschar * s, int len))
 {
 header_line *h;
 const uschar *list;
 {
 header_line *h;
 const uschar *list;
@@ -700,7 +735,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 ((hh = rewrite_header(h, NULL, NULL, tblock->rewrite_rules,
                  tblock->rewrite_existflags, FALSE)))
        {
-       if (!sendfn(fd, tctx, hh->text, hh->slen)) return FALSE;
+       if (!sendfn(tctx, hh->text, hh->slen)) return FALSE;
        store_reset(reset_point);
        continue;     /* With the next header line */
        }
        store_reset(reset_point);
        continue;     /* With the next header line */
        }
@@ -708,7 +743,7 @@ for (h = header_list; h; h = h->next) if (h->type != htype_old)
 
     /* Either no rewriting rules, or it didn't get rewritten */
 
 
     /* Either no rewriting rules, or it didn't get rewritten */
 
-    if (!sendfn(fd, tctx, h->text, h->slen)) return FALSE;
+    if (!sendfn(tctx, h->text, h->slen)) return FALSE;
     }
 
   /* Header removed */
     }
 
   /* Header removed */
@@ -743,7 +778,7 @@ if (addr)
       hprev = h;
       if (i == 1)
        {
       hprev = h;
       if (i == 1)
        {
-       if (!sendfn(fd, tctx, h->text, h->slen)) return FALSE;
+       if (!sendfn(tctx, h->text, h->slen)) return FALSE;
        DEBUG(D_transport)
          debug_printf("added header line(s):\n%s---\n", h->text);
        }
        DEBUG(D_transport)
          debug_printf("added header line(s):\n%s---\n", h->text);
        }
@@ -768,8 +803,8 @@ if (tblock && (list = CUS tblock->add_headers))
       int len = Ustrlen(s);
       if (len > 0)
        {
       int len = Ustrlen(s);
       if (len > 0)
        {
-       if (!sendfn(fd, tctx, s, len)) return FALSE;
-       if (s[len-1] != '\n' && !sendfn(fd, tctx, US"\n", 1))
+       if (!sendfn(tctx, s, len)) return FALSE;
+       if (s[len-1] != '\n' && !sendfn(tctx, US"\n", 1))
          return FALSE;
        DEBUG(D_transport)
          {
          return FALSE;
        DEBUG(D_transport)
          {
@@ -785,7 +820,7 @@ if (tblock && (list = CUS tblock->add_headers))
 
 /* Separate headers from body with a blank line */
 
 
 /* Separate headers from body with a blank line */
 
-return sendfn(fd, tctx, US"\n", 1);
+return sendfn(tctx, US"\n", 1);
 }
 
 
 }
 
 
@@ -818,8 +853,10 @@ can include timeouts for certain transports, which are requested by setting
 transport_write_timeout non-zero.
 
 Arguments:
 transport_write_timeout non-zero.
 
 Arguments:
-  fd                    file descriptor to write the message to
   tctx
   tctx
+    (fd, msg)          Either and fd, to write the message to,
+                       or a string: if null write message to allocated space
+                       otherwire take content as headers.
     addr                (chain of) addresses (for extra headers), or NULL;
                           only the first address is used
     tblock             optional transport instance block (NULL signifies NULL/0):
     addr                (chain of) addresses (for extra headers), or NULL;
                           only the first address is used
     tblock             optional transport instance block (NULL signifies NULL/0):
@@ -850,18 +887,17 @@ Returns:                TRUE on success; FALSE (with errno) on failure.
                         is incremented by the number of bytes written.
 */
 
                         is incremented by the number of bytes written.
 */
 
-static BOOL
-internal_transport_write_message(int fd, transport_ctx * tctx, int size_limit)
+BOOL
+internal_transport_write_message(transport_ctx * tctx, int size_limit)
 {
 int len;
 
 /* Initialize pointer in output buffer. */
 
 {
 int len;
 
 /* Initialize pointer in output buffer. */
 
-chunk_ptr = deliver_out_buffer;
+transport_write_reset(tctx->options);
 
 /* Set up the data for start-of-line data checking and escaping */
 
 
 /* Set up the data for start-of-line data checking and escaping */
 
-nl_partial_match = -1;
 if (tctx->check_string && tctx->escape_string)
   {
   nl_check = tctx->check_string;
 if (tctx->check_string && tctx->escape_string)
   {
   nl_check = tctx->check_string;
@@ -869,21 +905,19 @@ if (tctx->check_string && tctx->escape_string)
   nl_escape = tctx->escape_string;
   nl_escape_length = Ustrlen(nl_escape);
   }
   nl_escape = tctx->escape_string;
   nl_escape_length = Ustrlen(nl_escape);
   }
-else
-  nl_check_length = nl_escape_length = 0;
-
-/* Whether the escaping mechanism is applied to headers or not is controlled by
-an option (set for SMTP, not otherwise). Negate the length if not wanted till
-after the headers. */
-
-if (!(tctx->options & topt_escape_headers))
-  nl_check_length = -nl_check_length;
 
 /* Write the headers if required, including any that have to be added. If there
 are header rewriting rules, apply them. */
 
 if (!(tctx->options & topt_no_headers))
   {
 
 /* Write the headers if required, including any that have to be added. If there
 are header rewriting rules, apply them. */
 
 if (!(tctx->options & topt_no_headers))
   {
+  /* Whether the escaping mechanism is applied to headers or not is controlled by
+  an option (set for SMTP, not otherwise). Negate the length if not wanted till
+  after the headers. */
+
+  if (!(tctx->options & topt_escape_headers))
+    nl_check_length = -nl_check_length;
+
   /* Add return-path: if requested. */
 
   if (tctx->options & topt_add_return_path)
   /* Add return-path: if requested. */
 
   if (tctx->options & topt_add_return_path)
@@ -891,7 +925,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);
     uschar buffer[ADDRESS_MAXLENGTH + 20];
     int n = sprintf(CS buffer, "Return-path: <%.*s>\n", ADDRESS_MAXLENGTH,
       return_path);
-    if (!write_chunk(fd, tctx, buffer, n)) return FALSE;
+    if (!write_chunk(tctx, buffer, n)) return FALSE;
     }
 
   /* Add envelope-to: if requested */
     }
 
   /* Add envelope-to: if requested */
@@ -904,19 +938,19 @@ if (!(tctx->options & topt_no_headers))
     struct aci *dlist = NULL;
     void *reset_point = store_get(0);
 
     struct aci *dlist = NULL;
     void *reset_point = store_get(0);
 
-    if (!write_chunk(fd, tctx, US"Envelope-to: ", 13)) return FALSE;
+    if (!write_chunk(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 because write_env_to() calls itself recursively. */
 
     for (p = tctx->addr; p; p = p->next)
 
     /* 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 because write_env_to() calls itself recursively. */
 
     for (p = tctx->addr; p; p = p->next)
-      if (!write_env_to(p, &plist, &dlist, &first, fd, tctx))
+      if (!write_env_to(p, &plist, &dlist, &first, tctx))
        return FALSE;
 
     /* Add a final newline and reset the store used for tracking duplicates */
 
        return FALSE;
 
     /* Add a final newline and reset the store used for tracking duplicates */
 
-    if (!write_chunk(fd, tctx, US"\n", 1)) return FALSE;
+    if (!write_chunk(tctx, US"\n", 1)) return FALSE;
     store_reset(reset_point);
     }
 
     store_reset(reset_point);
     }
 
@@ -926,7 +960,7 @@ if (!(tctx->options & topt_no_headers))
     {
     uschar buffer[100];
     int n = sprintf(CS buffer, "Delivery-date: %s\n", tod_stamp(tod_full));
     {
     uschar buffer[100];
     int n = sprintf(CS buffer, "Delivery-date: %s\n", tod_stamp(tod_full));
-    if (!write_chunk(fd, tctx, buffer, n)) return FALSE;
+    if (!write_chunk(tctx, buffer, n)) return FALSE;
     }
 
   /* Then the message's headers. Don't write any that are flagged as "old";
     }
 
   /* Then the message's headers. Don't write any that are flagged as "old";
@@ -935,7 +969,7 @@ if (!(tctx->options & topt_no_headers))
   match any entries therein. Then check addr->prop.remove_headers too, provided that
   addr is not NULL. */
 
   match any entries therein. Then check addr->prop.remove_headers too, provided that
   addr is not NULL. */
 
-  if (!transport_headers_send(fd, tctx, &write_chunk))
+  if (!transport_headers_send(tctx, &write_chunk))
     return FALSE;
   }
 
     return FALSE;
   }
 
@@ -967,6 +1001,9 @@ if (tctx->options & topt_use_bdat)
     size = hsize + fsize;
     if (tctx->options & topt_use_crlf)
       size += body_linecount;  /* account for CRLF-expansion */
     size = hsize + fsize;
     if (tctx->options & topt_use_crlf)
       size += body_linecount;  /* account for CRLF-expansion */
+
+    /* With topt_use_bdat we never do dot-stuffing; no need to
+    account for any expansion due to that. */
     }
 
   /* If the message is large, emit first a non-LAST chunk with just the
     }
 
   /* If the message is large, emit first a non-LAST chunk with just the
@@ -980,7 +1017,7 @@ if (tctx->options & topt_use_bdat)
     DEBUG(D_transport)
       debug_printf("sending small initial BDAT; hsize=%d\n", hsize);
     if (  tctx->chunk_cb(tctx, hsize, 0) != OK
     DEBUG(D_transport)
       debug_printf("sending small initial BDAT; hsize=%d\n", hsize);
     if (  tctx->chunk_cb(tctx, hsize, 0) != OK
-       || !transport_write_block(fd, deliver_out_buffer, hsize)
+       || !transport_write_block(tctx, deliver_out_buffer, hsize, FALSE)
        || tctx->chunk_cb(tctx, 0, tc_reap_prev) != OK
        )
       return FALSE;
        || tctx->chunk_cb(tctx, 0, tc_reap_prev) != OK
        )
       return FALSE;
@@ -1013,7 +1050,7 @@ if (!(tctx->options & topt_no_body))
   while (  (len = MAX(DELIVER_IN_BUFFER_SIZE, size)) > 0
        && (len = read(deliver_datafile, deliver_in_buffer, len)) > 0)
     {
   while (  (len = MAX(DELIVER_IN_BUFFER_SIZE, size)) > 0
        && (len = read(deliver_datafile, deliver_in_buffer, len)) > 0)
     {
-    if (!write_chunk(fd, tctx, deliver_in_buffer, len))
+    if (!write_chunk(tctx, deliver_in_buffer, len))
       return FALSE;
     size -= len;
     }
       return FALSE;
     size -= len;
     }
@@ -1029,206 +1066,15 @@ nl_check_length = nl_escape_length = 0;
 
 /* If requested, add a terminating "." line (SMTP output). */
 
 
 /* If requested, add a terminating "." line (SMTP output). */
 
-if (tctx->options & topt_end_dot && !write_chunk(fd, tctx, US".\n", 2))
+if (tctx->options & topt_end_dot && !write_chunk(tctx, US".\n", 2))
   return FALSE;
 
 /* Write out any remaining data in the buffer before returning. */
 
 return (len = chunk_ptr - deliver_out_buffer) <= 0 ||
   return FALSE;
 
 /* Write out any remaining data in the buffer before returning. */
 
 return (len = chunk_ptr - deliver_out_buffer) <= 0 ||
-  transport_write_block(fd, deliver_out_buffer, len);
-}
-
-
-#ifndef DISABLE_DKIM
-
-/***************************************************************************************************
-*    External interface to write the message, while signing it with DKIM and/or Domainkeys         *
-***************************************************************************************************/
-
-/* This function is a wrapper around transport_write_message().
-   It is only called from the smtp transport if DKIM or Domainkeys support
-   is compiled in.  The function sets up a replacement fd into a -K file,
-   then calls the normal function. This way, the exact bits that exim would
-   have put "on the wire" will end up in the file (except for TLS
-   encapsulation, which is the very very last thing). When we are done
-   signing the file, send the signed message down the original fd (or TLS fd).
-
-Arguments:
-  as for internal_transport_write_message() above, with additional arguments
-  for DKIM.
-
-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, const uschar ** err)
-{
-int dkim_fd;
-int save_errno = 0;
-BOOL rc;
-uschar * dkim_spool_name;
-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. */
-
-if (!(dkim->dkim_private_key && dkim->dkim_domain && dkim->dkim_selector))
-  return transport_write_message(out_fd, tctx, 0);
-
-dkim_spool_name = spool_fname(US"input", message_subdir, message_id,
-                   string_sprintf("-%d-K", (int)getpid()));
-
-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
-(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)
-  {
-  save_errno = errno;
-  goto CLEANUP;
-  }
-
-/* 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)
-  {
-  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.");
-      *err = errstr;
-      rc = FALSE;
-      goto CLEANUP;
-      }
-  }
-
-#ifndef HAVE_LINUX_SENDFILE
-if (options & topt_use_bdat)
-#endif
-  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(tctx, siglen, 0) != OK
-       || !transport_write_block(out_fd, dkim_signature, siglen)
-       || tctx->chunk_cb(tctx, 0, tc_reap_prev) != OK
-       )
-      goto err;
-    siglen = 0;
-    }
-
-  /* Send the BDAT command for the entire message, as a single LAST-marked
-  chunk. */
-
-  if (tctx->chunk_cb(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,
-   as then there's another layer of indirection
-   before the data finally hits the socket. */
-if (tls_out.active != out_fd)
-  {
-  ssize_t copied = 0;
-  off_t offset = 0;
-
-  /* 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)
-    goto err;
-  }
-else
-
-#endif
-
-  {
-  /* Rewind file */
-  lseek(dkim_fd, 0, SEEK_SET);
-
-  /* Send file down the original fd */
-  while((sread = read(dkim_fd, deliver_out_buffer, DELIVER_OUT_BUFFER_SIZE)) >0)
-    {
-    uschar * p = deliver_out_buffer;
-    /* write the chunk */
-
-    while (sread)
-      {
-#ifdef SUPPORT_TLS
-      wwritten = tls_out.active == out_fd
-       ? tls_write(FALSE, p, sread)
-       : write(out_fd, CS p, sread);
-#else
-      wwritten = write(out_fd, CS p, sread);
-#endif
-      if (wwritten == -1)
-       goto err;
-      p += wwritten;
-      sread -= wwritten;
-      }
-    }
-
-  if (sread == -1)
-    {
-    save_errno = errno;
-    rc = FALSE;
-    }
-  }
-
-CLEANUP:
-  /* unlink -K file */
-  (void)close(dkim_fd);
-  Uunlink(dkim_spool_name);
-  errno = save_errno;
-  return rc;
-
-err:
-  save_errno = errno;
-  rc = FALSE;
-  goto CLEANUP;
+  transport_write_block(tctx, deliver_out_buffer, len, FALSE);
 }
 
 }
 
-#endif
-
 
 
 /*************************************************
 
 
 /*************************************************
@@ -1239,7 +1085,8 @@ err:
 the real work, passing over all the arguments from this function. Otherwise,
 set up a filtering process, fork another process to call the internal function
 to write to the filter, and in this process just suck from the filter and write
 the real work, passing over all the arguments from this function. Otherwise,
 set up a filtering process, fork another process to call the internal function
 to write to the filter, and in this process just suck from the filter and write
-down the given fd. At the end, tidy up the pipes and the processes.
+down the fd in the transport context. At the end, tidy up the pipes and the
+processes.
 
 Arguments:     as for internal_transport_write_message() above
 
 
 Arguments:     as for internal_transport_write_message() above
 
@@ -1248,7 +1095,7 @@ Returns:       TRUE on success; FALSE (with errno) for any failure
 */
 
 BOOL
 */
 
 BOOL
-transport_write_message(int fd, transport_ctx * tctx, int size_limit)
+transport_write_message(transport_ctx * tctx, int size_limit)
 {
 BOOL last_filter_was_NL = TRUE;
 int rc, len, yield, fd_read, fd_write, save_errno;
 {
 BOOL last_filter_was_NL = TRUE;
 int rc, len, yield, fd_read, fd_write, save_errno;
@@ -1256,8 +1103,6 @@ int pfd[2] = {-1, -1};
 pid_t filter_pid, write_pid;
 static transport_ctx dummy_tctx = {0};
 
 pid_t filter_pid, write_pid;
 static transport_ctx dummy_tctx = {0};
 
-if (!tctx) tctx = &dummy_tctx;
-
 transport_filter_timed_out = FALSE;
 
 /* If there is no filter command set up, call the internal function that does
 transport_filter_timed_out = FALSE;
 
 /* If there is no filter command set up, call the internal function that does
@@ -1267,7 +1112,7 @@ if (  !transport_filter_argv
    || !*transport_filter_argv
    || !**transport_filter_argv
    )
    || !*transport_filter_argv
    || !**transport_filter_argv
    )
-  return internal_transport_write_message(fd, tctx, size_limit);
+  return internal_transport_write_message(tctx, size_limit);
 
 /* Otherwise the message must be written to a filter process and read back
 before being written to the incoming fd. First set up the special processing to
 
 /* Otherwise the message must be written to a filter process and read back
 before being written to the incoming fd. First set up the special processing to
@@ -1297,11 +1142,11 @@ yield = FALSE;
 write_pid = (pid_t)(-1);
 
   {
 write_pid = (pid_t)(-1);
 
   {
-  int bits = fcntl(fd, F_GETFD);
-  (void)fcntl(fd, F_SETFD, bits | FD_CLOEXEC);
+  int bits = fcntl(tctx->u.fd, F_GETFD);
+  (void)fcntl(tctx->u.fd, F_SETFD, bits | FD_CLOEXEC);
   filter_pid = child_open(USS transport_filter_argv, NULL, 077,
    &fd_write, &fd_read, FALSE);
   filter_pid = child_open(USS transport_filter_argv, NULL, 077,
    &fd_write, &fd_read, FALSE);
-  (void)fcntl(fd, F_SETFD, bits & ~FD_CLOEXEC);
+  (void)fcntl(tctx->u.fd, F_SETFD, bits & ~FD_CLOEXEC);
   }
 if (filter_pid < 0) goto TIDY_UP;      /* errno set */
 
   }
 if (filter_pid < 0) goto TIDY_UP;      /* errno set */
 
@@ -1321,10 +1166,11 @@ if ((write_pid = fork()) == 0)
   (void)close(pfd[pipe_read]);
   nl_check_length = nl_escape_length = 0;
 
   (void)close(pfd[pipe_read]);
   nl_check_length = nl_escape_length = 0;
 
+  tctx->u.fd = fd_write;
   tctx->check_string = tctx->escape_string = NULL;
   tctx->options &= ~(topt_use_crlf | topt_end_dot | topt_use_bdat);
 
   tctx->check_string = tctx->escape_string = NULL;
   tctx->options &= ~(topt_use_crlf | topt_end_dot | topt_use_bdat);
 
-  rc = internal_transport_write_message(fd_write, tctx, size_limit);
+  rc = internal_transport_write_message(tctx, size_limit);
 
   save_errno = errno;
   if (  write(pfd[pipe_write], (void *)&rc, sizeof(BOOL))
 
   save_errno = errno;
   if (  write(pfd[pipe_write], (void *)&rc, sizeof(BOOL))
@@ -1391,7 +1237,7 @@ for (;;)
 
   if (len > 0)
     {
 
   if (len > 0)
     {
-    if (!write_chunk(fd, tctx, deliver_in_buffer, len)) goto TIDY_UP;
+    if (!write_chunk(tctx, deliver_in_buffer, len)) goto TIDY_UP;
     last_filter_was_NL = (deliver_in_buffer[len-1] == '\n');
     }
 
     last_filter_was_NL = (deliver_in_buffer[len-1] == '\n');
     }
 
@@ -1477,8 +1323,8 @@ if (yield)
   nl_check_length = nl_escape_length = 0;
   if (  tctx->options & topt_end_dot
      && ( last_filter_was_NL
   nl_check_length = nl_escape_length = 0;
   if (  tctx->options & topt_end_dot
      && ( last_filter_was_NL
-        ? !write_chunk(fd, tctx, US".\n", 2)
-       : !write_chunk(fd, tctx, US"\n.\n", 3)
+        ? !write_chunk(tctx, US".\n", 2)
+       : !write_chunk(tctx, US"\n.\n", 3)
      )  )
     yield = FALSE;
 
      )  )
     yield = FALSE;
 
@@ -1486,7 +1332,7 @@ if (yield)
 
   else
     yield = (len = chunk_ptr - deliver_out_buffer) <= 0
 
   else
     yield = (len = chunk_ptr - deliver_out_buffer) <= 0
-         || transport_write_block(fd, deliver_out_buffer, len);
+         || transport_write_block(tctx, deliver_out_buffer, len, FALSE);
   }
 else
   errno = save_errno;      /* From some earlier error */
   }
 else
   errno = save_errno;      /* From some earlier error */
index 9b3379be2dbe0412dafa2ce16f167c97f6570eeb..760e9603921204b89bdb45b2f4ecd173e8363caf 100644 (file)
@@ -916,6 +916,9 @@ copy_mbx_message(int to_fd, int from_fd, off_t saved_size)
 int used;
 off_t size;
 struct stat statbuf;
 int used;
 off_t size;
 struct stat statbuf;
+transport_ctx tctx = {0};
+
+tctx.u.fd = to_fd;
 
 /* If the current mailbox size is zero, write a header block */
 
 
 /* If the current mailbox size is zero, write a header block */
 
@@ -928,7 +931,7 @@ if (saved_size == 0)
     (long int)time(NULL));
   for (i = 0; i < MBX_NUSERFLAGS; i++)
     sprintf (CS(s += Ustrlen(s)), "\015\012");
     (long int)time(NULL));
   for (i = 0; i < MBX_NUSERFLAGS; i++)
     sprintf (CS(s += Ustrlen(s)), "\015\012");
-  if (!transport_write_block (to_fd, deliver_out_buffer, MBX_HDRSIZE))
+  if (!transport_write_block (&tctx, deliver_out_buffer, MBX_HDRSIZE, FALSE))
     return DEFER;
   }
 
     return DEFER;
   }
 
@@ -957,7 +960,7 @@ while (size > 0)
     if (len == 0) errno = ERRNO_MBXLENGTH;
     return DEFER;
     }
     if (len == 0) errno = ERRNO_MBXLENGTH;
     return DEFER;
     }
-  if (!transport_write_block(to_fd, deliver_out_buffer, used + len))
+  if (!transport_write_block(&tctx, deliver_out_buffer, used + len, FALSE))
     return DEFER;
   size -= len;
   used = 0;
     return DEFER;
   size -= len;
   used = 0;
@@ -2874,13 +2877,14 @@ at initialization time. */
 if (yield == OK)
   {
   transport_ctx tctx = {
 if (yield == OK)
   {
   transport_ctx tctx = {
+    fd,
     tblock,
     addr,
     ob->check_string,
     ob->escape_string,
     ob->options
   };
     tblock,
     addr,
     ob->check_string,
     ob->escape_string,
     ob->options
   };
-  if (!transport_write_message(fd, &tctx, 0))
+  if (!transport_write_message(&tctx, 0))
     yield = DEFER;
   }
 
     yield = DEFER;
   }
 
index f07cd83cf7e63ae63c9f34ae8090d348faa601f8..cdc4bdd05694876a47ff4958a29e1e89b99c40cf 100644 (file)
@@ -692,6 +692,7 @@ if (return_message)
     :
     US"------ This is a copy of the message, including all the headers.\n";
   transport_ctx tctx = {
     :
     US"------ This is a copy of the message, including all the headers.\n";
   transport_ctx tctx = {
+    fileno(f),
     tblock,
     addr,
     NULL, NULL,
     tblock,
     addr,
     NULL, NULL,
@@ -720,7 +721,7 @@ if (return_message)
 
   fflush(f);
   transport_count = 0;
 
   fflush(f);
   transport_count = 0;
-  transport_write_message(fileno(f), &tctx, bounce_return_size_limit);
+  transport_write_message(&tctx, bounce_return_size_limit);
   }
 
 /* End the message and wait for the child process to end; no timeout. */
   }
 
 /* End the message and wait for the child process to end; no timeout. */
index c4606ef8b8c647bd096829e2ea2660f007ed589b..610320c25aa86f7174d8cda09e4d50b92657a1cf 100644 (file)
@@ -610,6 +610,7 @@ if (send_data)
   {
   BOOL ok;
   transport_ctx tctx = {
   {
   BOOL ok;
   transport_ctx tctx = {
+    fd_in,
     tblock,
     addrlist,
     US".", US"..",
     tblock,
     addrlist,
     US".", US"..",
@@ -634,7 +635,7 @@ if (send_data)
     debug_printf("  LMTP>> writing message and terminating \".\"\n");
 
   transport_count = 0;
     debug_printf("  LMTP>> writing message and terminating \".\"\n");
 
   transport_count = 0;
-  ok = transport_write_message(fd_in, &tctx, 0);
+  ok = transport_write_message(&tctx, 0);
 
   /* Failure can either be some kind of I/O disaster (including timeout),
   or the failure of a transport filter or the expansion of added headers. */
 
   /* Failure can either be some kind of I/O disaster (including timeout),
   or the failure of a transport filter or the expansion of added headers. */
index 8b87e4a95909c57ed6fd74877b9f9c111c243bcc..f7f0e590abbec5e5cf0e773275218d05232d64c0 100644 (file)
@@ -552,6 +552,7 @@ const uschar *envlist = ob->environment;
 uschar *cmd, *ss;
 uschar *eol = ob->use_crlf ? US"\r\n" : US"\n";
 transport_ctx tctx = {
 uschar *cmd, *ss;
 uschar *eol = ob->use_crlf ? US"\r\n" : US"\n";
 transport_ctx tctx = {
+  0,
   tblock,
   addr,
   ob->check_string,
   tblock,
   addr,
   ob->check_string,
@@ -739,6 +740,7 @@ if ((pid = child_open(USS argv, envp, ob->umask, &fd_in, &fd_out, TRUE)) < 0)
       strerror(errno));
   return FALSE;
   }
       strerror(errno));
   return FALSE;
   }
+tctx.u.fd = fd_in;
 
 /* Now fork a process to handle the output that comes down the pipe. */
 
 
 /* Now fork a process to handle the output that comes down the pipe. */
 
@@ -829,7 +831,7 @@ if (ob->message_prefix != NULL)
       expand_string_message);
     return FALSE;
     }
       expand_string_message);
     return FALSE;
     }
-  if (!transport_write_block(fd_in, prefix, Ustrlen(prefix)))
+  if (!transport_write_block(&tctx, prefix, Ustrlen(prefix), FALSE))
     goto END_WRITE;
   }
 
     goto END_WRITE;
   }
 
@@ -857,7 +859,7 @@ if (ob->use_bsmtp)
 
 /* Now the actual message */
 
 
 /* Now the actual message */
 
-if (!transport_write_message(fd_in, &tctx, 0))
+if (!transport_write_message(&tctx, 0))
     goto END_WRITE;
 
 /* Now any configured suffix */
     goto END_WRITE;
 
 /* Now any configured suffix */
@@ -873,7 +875,7 @@ if (ob->message_suffix)
       expand_string_message);
     return FALSE;
     }
       expand_string_message);
     return FALSE;
     }
-  if (!transport_write_block(fd_in, suffix, Ustrlen(suffix)))
+  if (!transport_write_block(&tctx, suffix, Ustrlen(suffix), FALSE))
     goto END_WRITE;
   }
 
     goto END_WRITE;
   }
 
index f65463ea03d0efe722cdd54f00a935edec1328ac..e28a5bfe67f7c6462b36e19bea3959974b9e9579 100644 (file)
@@ -2848,6 +2848,7 @@ if (!(sx.peer_offered & PEER_OFFERED_CHUNKING) && !sx.ok)
 else
   {
   transport_ctx tctx = {
 else
   {
   transport_ctx tctx = {
+    sx.inblock.sock,
     tblock,
     addrlist,
     US".", US"..",    /* Escaping strings */
     tblock,
     addrlist,
     US".", US"..",    /* Escaping strings */
@@ -2896,10 +2897,9 @@ else
   transport_count = 0;
 
 #ifndef DISABLE_DKIM
   transport_count = 0;
 
 #ifndef DISABLE_DKIM
-  sx.ok = dkim_transport_write_message(sx.inblock.sock, &tctx, &sx.ob->dkim,
-           CUSS &message);
+  sx.ok = dkim_transport_write_message(&tctx, &sx.ob->dkim, CUSS &message);
 #else
 #else
-  sx.ok = transport_write_message(sx.inblock.sock, &tctx, 0);
+  sx.ok = transport_write_message(&tctx, 0);
 #endif
 
   /* transport_write_message() uses write() because it is called from other
 #endif
 
   /* transport_write_message() uses write() because it is called from other
index de4ffbe48d9ab4f77c39b9b218fc0195b24f5830..e46d2020bd76d006de3572225c2389df8103cfa1 100644 (file)
@@ -1308,9 +1308,9 @@ return cutthrough_response(cutthrough.fd, '3', NULL, CUTTHROUGH_DATA_TIMEOUT) ==
 }
 
 
 }
 
 
-/* fd and tctx args only to match write_chunk() */
+/* tctx arg only to match write_chunk() */
 static BOOL
 static BOOL
-cutthrough_write_chunk(int fd, transport_ctx * tctx, uschar * s, int len)
+cutthrough_write_chunk(transport_ctx * tctx, uschar * s, int len)
 {
 uschar * s2;
 while(s && (s2 = Ustrchr(s, '\n')))
 {
 uschar * s2;
 while(s && (s2 = Ustrchr(s, '\n')))
@@ -1339,13 +1339,14 @@ if(cutthrough.fd < 0 || cutthrough.callout_hold_only)
 */
 HDEBUG(D_acl) debug_printf_indent("----------- start cutthrough headers send -----------\n");
 
 */
 HDEBUG(D_acl) debug_printf_indent("----------- start cutthrough headers send -----------\n");
 
+tctx.u.fd = cutthrough.fd;
 tctx.tblock = cutthrough.addr.transport;
 tctx.addr = &cutthrough.addr;
 tctx.check_string = US".";
 tctx.escape_string = US"..";
 tctx.options = topt_use_crlf;
 
 tctx.tblock = cutthrough.addr.transport;
 tctx.addr = &cutthrough.addr;
 tctx.check_string = US".";
 tctx.escape_string = US"..";
 tctx.options = topt_use_crlf;
 
-if (!transport_headers_send(cutthrough.fd, &tctx, &cutthrough_write_chunk))
+if (!transport_headers_send(&tctx, &cutthrough_write_chunk))
   return FALSE;
 
 HDEBUG(D_acl) debug_printf_indent("----------- done cutthrough headers send ------------\n");
   return FALSE;
 
 HDEBUG(D_acl) debug_printf_indent("----------- done cutthrough headers send ------------\n");
index 052569fa97042fff6ccf1b2ed39ddb4f41654f37..c5f1e3b5fb5dd813de79f6610b347024b8717882 100644 (file)
@@ -2,7 +2,7 @@
 1999-03-02 09:44:33 10HmaX-0005vi-00 => a@test.ex R=to_server T=remote_smtp_dkim H=127.0.0.1 [127.0.0.1] K C="250- 661 byte chunk, total 661\\n250 OK id=10HmaY-0005vi-00"
 1999-03-02 09:44:33 10HmaX-0005vi-00 Completed
 1999-03-02 09:44:33 10HmaZ-0005vi-00 <= sender@testhost.test.ex U=sender P=local S=sss for b@test.ex
 1999-03-02 09:44:33 10HmaX-0005vi-00 => a@test.ex R=to_server T=remote_smtp_dkim H=127.0.0.1 [127.0.0.1] K C="250- 661 byte chunk, total 661\\n250 OK id=10HmaY-0005vi-00"
 1999-03-02 09:44:33 10HmaX-0005vi-00 Completed
 1999-03-02 09:44:33 10HmaZ-0005vi-00 <= sender@testhost.test.ex U=sender P=local S=sss for b@test.ex
-1999-03-02 09:44:33 10HmaZ-0005vi-00 => b@test.ex R=to_server T=remote_smtp_dkim H=127.0.0.1 [127.0.0.1] K C="250- 8520 byte chunk, total 8848\\n250 OK id=10HmbA-0005vi-00"
+1999-03-02 09:44:33 10HmaZ-0005vi-00 => b@test.ex R=to_server T=remote_smtp_dkim H=127.0.0.1 [127.0.0.1] K C="250- 8196 byte chunk, total 8848\\n250 OK id=10HmbA-0005vi-00"
 1999-03-02 09:44:33 10HmaZ-0005vi-00 Completed
 
 ******** SERVER ********
 1999-03-02 09:44:33 10HmaZ-0005vi-00 Completed
 
 ******** SERVER ********