Logging: make cipher info available for continued-TLS connection deliveries
[exim.git] / src / src / transport.c
index 3987fad3e0b0319cf26f6f219569b504c33ff6fc..e6e327822c1a6d7224e21ab958eab61ad132f5c0 100644 (file)
@@ -108,10 +108,23 @@ optionlist optionlist_transports[] = {
                  (void *)offsetof(transport_instance, uid) }
 };
 
                  (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           *
 *************************************************/
 /*************************************************
 *             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. */
 
 /* 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,
 
   if (t->body_only && t->headers_only)
     log_write(0, LOG_PANIC_DIE|LOG_CONFIG,
@@ -418,15 +428,22 @@ for (ptr = start; ptr < end; ptr++)
 
   if ((len = chunk_ptr - deliver_out_buffer) > mlen)
     {
 
   if ((len = chunk_ptr - deliver_out_buffer) > mlen)
     {
+    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 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, tc_reap_prev|tc_reap_one) != OK)
+      {
+      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;
        return FALSE;
-
-    if (!transport_write_block(fd, deliver_out_buffer, len))
-      return FALSE;
     chunk_ptr = deliver_out_buffer;
     }
 
     chunk_ptr = deliver_out_buffer;
     }
 
@@ -607,7 +624,7 @@ 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
 
 Globals:
   header_list
@@ -616,13 +633,8 @@ Arguments:
   addr                  (chain of) addresses (for extra headers), or NULL;
                           only the first address is used
   fd                    file descriptor to write the message to
   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)
   sendfn               function for output (transport or verify)
-  wck_flags
-    use_crlf           turn NL into CR LF
-    use_bdat           callback before chunk flush
-  rewrite_rules         chain of header rewriting rules
-  rewrite_existflags    flags for the rewriting rules
-  chunk_cb             transport callback function for data-chunk commands
 
 Returns:                TRUE on success; FALSE on failure.
 */
 
 Returns:                TRUE on success; FALSE on failure.
 */
@@ -826,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
       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
                             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
 
 Returns:                TRUE on success; FALSE (with errno) on failure.
                         In addition, the global variable transport_count
@@ -896,7 +908,7 @@ if (!(tctx->options & topt_no_headers))
 
     /* 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
 
     /* 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, tctx))
 
     for (p = tctx->addr; p; p = p->next)
       if (!write_env_to(p, &plist, &dlist, &first, fd, tctx))
@@ -927,11 +939,11 @@ if (!(tctx->options & topt_no_headers))
     return FALSE;
   }
 
     return FALSE;
   }
 
-/* When doing RFC3030 CHUNKING output, work out how much data will be in the
-last BDAT, consisting of the current write_chunk() output buffer fill
+/* 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
 (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 the BDAT, and ensure
+(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.
 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.
@@ -942,7 +954,7 @@ suboptimal. */
 if (tctx->options & topt_use_bdat)
   {
   off_t fsize;
 if (tctx->options & topt_use_bdat)
   {
   off_t fsize;
-  int hsize, size;
+  int hsize, size = 0;
 
   if ((hsize = chunk_ptr - deliver_out_buffer) < 0)
     hsize = 0;
 
   if ((hsize = chunk_ptr - deliver_out_buffer) < 0)
     hsize = 0;
@@ -965,6 +977,8 @@ if (tctx->options & topt_use_bdat)
 
   if (size > DELIVER_OUT_BUFFER_SIZE && hsize > 0)
     {
 
   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
     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
@@ -1048,18 +1062,16 @@ Returns:       TRUE on success; FALSE (with errno) for any failure
 
 BOOL
 dkim_transport_write_message(int out_fd, transport_ctx * tctx,
 
 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 dkim_fd;
 int save_errno = 0;
 BOOL rc;
 uschar * dkim_spool_name;
-int sread = 0;
-int wwritten = 0;
-uschar *dkim_signature = NULL;
-int siglen = 0;
+uschar * dkim_signature = NULL;
+int sread = 0, wwritten = 0, siglen = 0, options;
 off_t k_file_size;
 off_t k_file_size;
-int options;
+const uschar * errstr;
 
 /* If we can't sign, just call the original function. */
 
 
 /* If we can't sign, just call the original function. */
 
@@ -1074,10 +1086,12 @@ 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;
   /* Can't create spool file. Ugh. */
   rc = FALSE;
   save_errno = errno;
+  *err = string_sprintf("dkim spoolfile create: %s", strerror(errno));
   goto CLEANUP;
   }
 
   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;
 
 options = tctx->options;
 tctx->options &= ~topt_use_bdat;
@@ -1092,14 +1106,9 @@ if (!rc)
   }
 
 /* Rewind file and feed it to the goats^W DKIM lib */
   }
 
 /* Rewind file and feed it to the goats^W DKIM lib */
+dkim->dot_stuffed = !!(options & topt_end_dot);
 lseek(dkim_fd, 0, SEEK_SET);
 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_signature = dkim_exim_sign(dkim_fd, dkim, &errstr)))
   siglen = Ustrlen(dkim_signature);
 else if (dkim->dkim_strict)
   {
   siglen = Ustrlen(dkim_signature);
 else if (dkim->dkim_strict)
   {
@@ -1112,6 +1121,7 @@ else if (dkim->dkim_strict)
       save_errno = EACCES;
       log_write(0, LOG_MAIN, "DKIM: message could not be signed,"
        " and dkim_strict is set. Deferring message delivery.");
       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;
       }
       rc = FALSE;
       goto CLEANUP;
       }
@@ -1175,17 +1185,17 @@ else
   /* Send file down the original fd */
   while((sread = read(dkim_fd, deliver_out_buffer, DELIVER_OUT_BUFFER_SIZE)) >0)
     {
   /* 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
     /* 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
 #else
-      wwritten = write(out_fd, p, sread);
+      wwritten = write(out_fd, CS p, sread);
 #endif
       if (wwritten == -1)
        goto err;
 #endif
       if (wwritten == -1)
        goto err;
@@ -1238,7 +1248,6 @@ Returns:       TRUE on success; FALSE (with errno) for any failure
 BOOL
 transport_write_message(int fd, transport_ctx * tctx, int size_limit)
 {
 BOOL
 transport_write_message(int fd, transport_ctx * tctx, int size_limit)
 {
-unsigned wck_flags;
 BOOL last_filter_was_NL = TRUE;
 int rc, len, yield, fd_read, fd_write, save_errno;
 int pfd[2] = {-1, -1};
 BOOL last_filter_was_NL = TRUE;
 int rc, len, yield, fd_read, fd_write, save_errno;
 int pfd[2] = {-1, -1};
@@ -1262,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. */
 
 before being written to the incoming fd. First set up the special processing to
 be done during the copying. */
 
-wck_flags = tctx->options & topt_use_crlf;
 nl_partial_match = -1;
 
 if (tctx->check_string && tctx->escape_string)
 nl_partial_match = -1;
 
 if (tctx->check_string && tctx->escape_string)
@@ -1286,10 +1294,13 @@ save_errno = 0;
 yield = FALSE;
 write_pid = (pid_t)(-1);
 
 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)
 if (filter_pid < 0) goto TIDY_UP;      /* errno set */
 
 DEBUG(D_transport)
@@ -1428,14 +1439,19 @@ if (write_pid > 0)
   {
   rc = child_close(write_pid, 30);
   if (yield)
   {
   rc = child_close(write_pid, 30);
   if (yield)
-    {
     if (rc == 0)
       {
       BOOL ok;
     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;
         }
         dummy = read(pfd[pipe_read], (void *)&(tctx->addr->more_errno), sizeof(int));
         yield = FALSE;
         }
@@ -1447,7 +1463,6 @@ if (write_pid > 0)
       tctx->addr->more_errno = rc;
       DEBUG(D_transport) debug_printf("writing process returned %d\n", rc);
       }
       tctx->addr->more_errno = rc;
       DEBUG(D_transport) debug_printf("writing process returned %d\n", rc);
       }
-    }
   }
 (void)close(pfd[pipe_read]);
 
   }
 (void)close(pfd[pipe_read]);
 
@@ -1761,7 +1776,7 @@ while (1)
 
   /* create an array to read entire message queue into memory for processing  */
 
 
   /* 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;
 
   msgq_count = host_record->count;
   msgq_actual = msgq_count;
 
@@ -1869,7 +1884,7 @@ test but the code should work */
 
   if (bFound)          /* Usual exit from main loop */
     {
 
   if (bFound)          /* Usual exit from main loop */
     {
-    free (msgq);
+    store_free (msgq);
     break;
     }
 
     break;
     }
 
@@ -1895,7 +1910,7 @@ test but the code should work */
     return FALSE;
     }
 
     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
   }            /* we need to process a continuation record */
 
 /* Control gets here when an existing message has been encountered; its
@@ -1944,7 +1959,7 @@ DEBUG(D_transport) debug_printf("transport_pass_socket entered\n");
 
 if ((pid = fork()) == 0)
   {
 
 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
   const uschar **argv;
 
   /* Disconnect entirely from the parent process. If we are running in the
@@ -1960,16 +1975,24 @@ if ((pid = fork()) == 0)
 
   argv = CUSS child_exec_exim(CEE_RETURN_ARGV, TRUE, &i, FALSE, 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";
 
   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)
     {
 
   if (queue_run_pid != (pid_t)0)
     {
@@ -2258,7 +2281,7 @@ if (expand_arguments)
        */
       if (address_pipe_argcount > 1)
         memmove(
        */
       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,
           argv + i + address_pipe_argcount,
           /* current position + 1 (for the (uschar *)0 at the end) */
           argv + i + 1,