Callout/hold: fix uninitialized variable
[exim.git] / src / src / transport.c
index efc30be593645ebd2585c914bc1e3b407341363f..974b786ad4a93eddc2dd17f273ede06aa7ba0791 100644 (file)
@@ -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           *
 *************************************************/
@@ -422,9 +435,9 @@ for (ptr = start; ptr < end; ptr++)
 
     if (tctx &&  tctx->options & topt_use_bdat  &&  tctx->chunk_cb)
       {
-      if (  tctx->chunk_cb(fd, tctx, (unsigned)len, 0) != OK
+      if (  tctx->chunk_cb(tctx, (unsigned)len, 0) != OK
         || !transport_write_block(fd, deliver_out_buffer, len)
-        || tctx->chunk_cb(fd, tctx, 0, tc_reap_prev) != OK
+        || tctx->chunk_cb(tctx, 0, tc_reap_prev) != OK
         )
        return FALSE;
       }
@@ -611,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
@@ -895,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
-    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))
@@ -941,7 +954,7 @@ suboptimal. */
 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;
@@ -965,21 +978,21 @@ if (tctx->options & topt_use_bdat)
   if (size > DELIVER_OUT_BUFFER_SIZE && hsize > 0)
     {
     DEBUG(D_transport)
-      debug_printf("sending small initial BDAT; hssize=%d\n", hsize);
-    if (  tctx->chunk_cb(fd, tctx, hsize, 0) != OK
+      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)
-       || tctx->chunk_cb(fd, tctx, 0, tc_reap_prev) != OK
+       || tctx->chunk_cb(tctx, 0, tc_reap_prev) != OK
        )
       return FALSE;
     chunk_ptr = deliver_out_buffer;
     size -= hsize;
     }
 
-  /* Emit a LAST datachunk command. */
+  /* Emit a LAST datachunk command, and unmark the context for further
+  BDAT commands. */
 
-  if (tctx->chunk_cb(fd, tctx, size, tc_chunk_last) != OK)
+  if (tctx->chunk_cb(tctx, size, tc_chunk_last) != OK)
     return FALSE;
-
   tctx->options &= ~topt_use_bdat;
   }
 
@@ -1049,18 +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;
-int siglen = 0;
+uschar * dkim_signature = NULL;
+int sread = 0, wwritten = 0, siglen = 0, options;
 off_t k_file_size;
-int options;
+const uschar * errstr;
 
 /* If we can't sign, just call the original function. */
 
@@ -1075,6 +1086,7 @@ 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;
   }
 
@@ -1096,7 +1108,7 @@ if (!rc)
 /* 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)))
+if ((dkim_signature = dkim_exim_sign(dkim_fd, dkim, &errstr)))
   siglen = Ustrlen(dkim_signature);
 else if (dkim->dkim_strict)
   {
@@ -1109,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.");
+      *err = errstr;
       rc = FALSE;
       goto CLEANUP;
       }
@@ -1128,15 +1141,18 @@ if (options & topt_use_bdat)
 
   if (siglen + k_file_size > DELIVER_OUT_BUFFER_SIZE && siglen > 0)
     {
-    if (  tctx->chunk_cb(out_fd, tctx, siglen, 0) != OK
+    if (  tctx->chunk_cb(tctx, siglen, 0) != OK
        || !transport_write_block(out_fd, dkim_signature, siglen)
-       || tctx->chunk_cb(out_fd, tctx, 0, tc_reap_prev) != OK
+       || tctx->chunk_cb(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)
+  /* 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;
   }
 
@@ -1172,17 +1188,17 @@ 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)
        goto err;
@@ -1225,7 +1241,6 @@ 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.
 
-XXX
 Arguments:     as for internal_transport_write_message() above
 
 Returns:       TRUE on success; FALSE (with errno) for any failure
@@ -1235,7 +1250,6 @@ Returns:       TRUE on success; FALSE (with errno) for any failure
 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};
@@ -1259,7 +1273,6 @@ if (  !transport_filter_argv
 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)
@@ -1283,10 +1296,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)
@@ -1425,14 +1441,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;
         }
@@ -1444,7 +1465,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]);
 
@@ -1916,6 +1936,72 @@ return TRUE;
 *    Deliver waiting message down same socket    *
 *************************************************/
 
+/* Just the regain-root-privilege exec portion */
+void
+transport_do_pass_socket(const uschar *transport_name, const uschar *hostname,
+  const uschar *hostaddress, uschar *id, int socket_fd)
+{
+pid_t pid;
+int status;
+int i = 20;
+const uschar **argv;
+
+/* Set up the calling arguments; use the standard function for the basics,
+but we have a number of extras that may be added. */
+
+argv = CUSS child_exec_exim(CEE_RETURN_ARGV, TRUE, &i, FALSE, 0);
+
+if (smtp_authenticated)                                argv[i++] = US"-MCA";
+if (smtp_peer_options & PEER_OFFERED_CHUNKING) argv[i++] = US"-MCK";
+if (smtp_peer_options & PEER_OFFERED_DSN)      argv[i++] = US"-MCD";
+if (smtp_peer_options & PEER_OFFERED_PIPE)     argv[i++] = US"-MCP";
+if (smtp_peer_options & PEER_OFFERED_SIZE)     argv[i++] = US"-MCS";
+#ifdef SUPPORT_TLS
+if (smtp_peer_options & PEER_OFFERED_TLS)
+  if (tls_out.active >= 0 || continue_proxy_cipher)
+    {
+    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)
+  {
+  argv[i++] = US"-MCQ";
+  argv[i++] = string_sprintf("%d", queue_run_pid);
+  argv[i++] = string_sprintf("%d", queue_run_pipe);
+  }
+
+argv[i++] = US"-MC";
+argv[i++] = US transport_name;
+argv[i++] = US hostname;
+argv[i++] = US hostaddress;
+argv[i++] = string_sprintf("%d", continue_sequence + 1);
+argv[i++] = id;
+argv[i++] = NULL;
+
+/* Arrange for the channel to be on stdin. */
+
+if (socket_fd != 0)
+  {
+  (void)dup2(socket_fd, 0);
+  (void)close(socket_fd);
+  }
+
+DEBUG(D_exec) debug_print_argv(argv);
+exim_nullstd();                          /* Ensure std{out,err} exist */
+execv(CS argv[0], (char *const *)argv);
+
+DEBUG(D_any) debug_printf("execv failed: %s\n", strerror(errno));
+_exit(errno);         /* Note: must be _exit(), NOT exit() */
+}
+
+
+
 /* Fork a new exim process to deliver the message, and do a re-exec, both to
 get a clean delivery process, and to regain root privilege in cases where it
 has been given away.
@@ -1941,9 +2027,6 @@ DEBUG(D_transport) debug_printf("transport_pass_socket entered\n");
 
 if ((pid = fork()) == 0)
   {
-  int i = 17;
-  const uschar **argv;
-
   /* Disconnect entirely from the parent process. If we are running in the
   test harness, wait for a bit to allow the previous process time to finish,
   write the log, etc., so that the output is always in the same order for
@@ -1952,50 +2035,8 @@ if ((pid = fork()) == 0)
   if ((pid = fork()) != 0) _exit(EXIT_SUCCESS);
   if (running_in_test_harness) sleep(1);
 
-  /* Set up the calling arguments; use the standard function for the basics,
-  but we have a number of extras that may be added. */
-
-  argv = CUSS child_exec_exim(CEE_RETURN_ARGV, TRUE, &i, FALSE, 0);
-
-  if (smtp_authenticated) argv[i++] = US"-MCA";
-
-  if (smtp_peer_options & PEER_OFFERED_CHUNKING) argv[i++] = US"-MCK";
-  if (smtp_peer_options & PEER_OFFERED_DSN) argv[i++] = US"-MCD";
-  if (smtp_peer_options & PEER_OFFERED_PIPE) argv[i++] = US"-MCP";
-  if (smtp_peer_options & PEER_OFFERED_SIZE) argv[i++] = US"-MCS";
-#ifdef SUPPORT_TLS
-  if (smtp_peer_options & PEER_OFFERED_TLS) argv[i++] = US"-MCT";
-#endif
-
-  if (queue_run_pid != (pid_t)0)
-    {
-    argv[i++] = US"-MCQ";
-    argv[i++] = string_sprintf("%d", queue_run_pid);
-    argv[i++] = string_sprintf("%d", queue_run_pipe);
-    }
-
-  argv[i++] = US"-MC";
-  argv[i++] = US transport_name;
-  argv[i++] = US hostname;
-  argv[i++] = US hostaddress;
-  argv[i++] = string_sprintf("%d", continue_sequence + 1);
-  argv[i++] = id;
-  argv[i++] = NULL;
-
-  /* Arrange for the channel to be on stdin. */
-
-  if (socket_fd != 0)
-    {
-    (void)dup2(socket_fd, 0);
-    (void)close(socket_fd);
-    }
-
-  DEBUG(D_exec) debug_print_argv(argv);
-  exim_nullstd();                          /* Ensure std{out,err} exist */
-  execv(CS argv[0], (char *const *)argv);
-
-  DEBUG(D_any) debug_printf("execv failed: %s\n", strerror(errno));
-  _exit(errno);         /* Note: must be _exit(), NOT exit() */
+  transport_do_pass_socket(transport_name, hostname, hostaddress,
+    id, socket_fd);
   }
 
 /* If the process creation succeeded, wait for the first-level child, which
@@ -2254,7 +2295,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,